(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.
This commit is contained in:
parent
1a8b34a503
commit
706403f2aa
8 changed files with 108 additions and 17 deletions
|
@ -2207,6 +2207,27 @@ slot is returned; @ref{Records}.
|
|||
@end example
|
||||
@end defun
|
||||
|
||||
@defun cl-type-of object
|
||||
This function returns a symbol naming @emph{the} type of
|
||||
@var{object}. It usually behaves like @code{type-of}, except
|
||||
that it guarantees to return the most precise type possible, which also
|
||||
implies that the specific type it returns may change depending on the
|
||||
Emacs version. For this reason, as a rule you should never compare its
|
||||
return value against some fixed set of types.
|
||||
|
||||
@example
|
||||
(cl-type-of 1)
|
||||
@result{} fixnum
|
||||
@group
|
||||
(cl-type-of 'nil)
|
||||
@result{} null
|
||||
(cl-type-of (record 'foo))
|
||||
@result{} foo
|
||||
@end group
|
||||
@end example
|
||||
@end defun
|
||||
|
||||
|
||||
@node Equality Predicates
|
||||
@section Equality Predicates
|
||||
@cindex equality
|
||||
|
|
5
etc/NEWS
5
etc/NEWS
|
@ -1647,6 +1647,11 @@ values.
|
|||
|
||||
* Lisp Changes in Emacs 30.1
|
||||
|
||||
** New function 'cl-type-of'.
|
||||
This function is like 'type-of' except that it sometimes returns
|
||||
a more precise type. For example, for nil and t it returns 'null'
|
||||
and 'boolean' respectively, instead of just 'symbol'.
|
||||
|
||||
** Built-in types have now corresponding classes.
|
||||
At the Lisp level, this means that things like (cl-find-class 'integer)
|
||||
will now return a class object, and at the UI level it means that
|
||||
|
|
|
@ -1334,8 +1334,7 @@ These match if the argument is `eql' to VAL."
|
|||
|
||||
(defconst cl--generic--unreachable-types
|
||||
;; FIXME: Try to make that list empty?
|
||||
'(fixnum bignum boolean keyword
|
||||
special-form subr-primitive subr-native-elisp)
|
||||
'(keyword)
|
||||
"Built-in classes on which we cannot dispatch for technical reasons.")
|
||||
|
||||
(defun cl--generic-type-specializers (tag &rest _)
|
||||
|
@ -1345,8 +1344,7 @@ These match if the argument is `eql' to VAL."
|
|||
(cl--class-allparents class)))))
|
||||
|
||||
(cl-generic-define-generalizer cl--generic-typeof-generalizer
|
||||
;; FIXME: We could also change `type-of' to return `null' for nil.
|
||||
10 (lambda (name &rest _) `(if ,name (type-of ,name) 'null))
|
||||
10 (lambda (name &rest _) `(cl-type-of ,name))
|
||||
#'cl--generic-type-specializers)
|
||||
|
||||
(cl-defmethod cl-generic-generalizers :extra "typeof" (type)
|
||||
|
|
|
@ -339,8 +339,6 @@
|
|||
',parents))))))
|
||||
|
||||
;; FIXME: Our type DAG has various quirks:
|
||||
;; - `subr' says it's a `compiled-function' but that's not true
|
||||
;; for those subrs that are special forms!
|
||||
;; - Some `keyword's are also `symbol-with-pos' but that's not reflected
|
||||
;; in the DAG.
|
||||
;; - An OClosure can be an interpreted function or a `byte-code-function',
|
||||
|
@ -428,15 +426,17 @@ For this build of Emacs it's %dbit."
|
|||
"Abstract type of functions that have been compiled.")
|
||||
(cl--define-built-in-type byte-code-function (compiled-function)
|
||||
"Type of functions that have been byte-compiled.")
|
||||
(cl--define-built-in-type subr (compiled-function)
|
||||
(cl--define-built-in-type subr (atom)
|
||||
"Abstract type of functions compiled to machine code.")
|
||||
(cl--define-built-in-type module-function (function)
|
||||
"Type of functions provided via the module API.")
|
||||
(cl--define-built-in-type interpreted-function (function)
|
||||
"Type of functions that have not been compiled.")
|
||||
(cl--define-built-in-type subr-native-elisp (subr)
|
||||
"Type of function that have been compiled by the native compiler.")
|
||||
(cl--define-built-in-type subr-primitive (subr)
|
||||
(cl--define-built-in-type special-form (subr)
|
||||
"Type of the core syntactic elements of the Emacs Lisp language.")
|
||||
(cl--define-built-in-type subr-native-elisp (subr compiled-function)
|
||||
"Type of functions that have been compiled by the native compiler.")
|
||||
(cl--define-built-in-type subr-primitive (subr compiled-function)
|
||||
"Type of functions hand written in C.")
|
||||
|
||||
(unless (cl--class-parents (cl--find-class 'cl-structure-object))
|
||||
|
|
|
@ -1046,7 +1046,7 @@ method invocation orders of the involved classes."
|
|||
|
||||
(defun cl--generic-struct-tag (name &rest _)
|
||||
;; Use exactly the same code as for `typeof'.
|
||||
`(if ,name (type-of ,name) 'null))
|
||||
`(cl-type-of ,name))
|
||||
|
||||
(cl-generic-define-generalizer eieio--generic-generalizer
|
||||
;; Use the exact same tagcode as for cl-struct, so that methods
|
||||
|
|
|
@ -2442,7 +2442,7 @@ emit_limple_insn (Lisp_Object insn)
|
|||
{
|
||||
Lisp_Object arg1 = arg[1];
|
||||
|
||||
if (EQ (Ftype_of (arg1), Qcomp_mvar))
|
||||
if (EQ (Fcl_type_of (arg1), Qcomp_mvar))
|
||||
res = emit_mvar_rval (arg1);
|
||||
else if (EQ (FIRST (arg1), Qcall))
|
||||
res = emit_limple_call (XCDR (arg1));
|
||||
|
|
40
src/data.c
40
src/data.c
|
@ -193,16 +193,37 @@ DEFUN ("null", Fnull, Snull, 1, 1, 0,
|
|||
DEFUN ("type-of", Ftype_of, Stype_of, 1, 1, 0,
|
||||
doc: /* Return a symbol representing the type of OBJECT.
|
||||
The symbol returned names the object's basic type;
|
||||
for example, (type-of 1) returns `integer'. */)
|
||||
for example, (type-of 1) returns `integer'.
|
||||
Contrary to `cl-type-of', the returned type is not always the most
|
||||
precise type possible, because instead this function tries to preserve
|
||||
compatibility with the return value of previous Emacs versions. */)
|
||||
(Lisp_Object object)
|
||||
{
|
||||
return SYMBOLP (object) ? Qsymbol
|
||||
: INTEGERP (object) ? Qinteger
|
||||
: SUBRP (object) ? Qsubr
|
||||
: Fcl_type_of (object);
|
||||
}
|
||||
|
||||
DEFUN ("cl-type-of", Fcl_type_of, Scl_type_of, 1, 1, 0,
|
||||
doc: /* Return a symbol representing the type of OBJECT.
|
||||
The returned symbol names the most specific possible type of the object.
|
||||
for example, (cl-type-of nil) returns `null'.
|
||||
The specific type returned may change depending on Emacs versions,
|
||||
so we recommend you use `cl-typep', `cl-typecase', or other predicates
|
||||
rather than compare the return value of this function against
|
||||
a fixed set of types. */)
|
||||
(Lisp_Object object)
|
||||
{
|
||||
switch (XTYPE (object))
|
||||
{
|
||||
case_Lisp_Int:
|
||||
return Qinteger;
|
||||
return Qfixnum;
|
||||
|
||||
case Lisp_Symbol:
|
||||
return Qsymbol;
|
||||
return NILP (object) ? Qnull
|
||||
: EQ (object, Qt) ? Qboolean
|
||||
: Qsymbol;
|
||||
|
||||
case Lisp_String:
|
||||
return Qstring;
|
||||
|
@ -215,7 +236,7 @@ for example, (type-of 1) returns `integer'. */)
|
|||
switch (PSEUDOVECTOR_TYPE (XVECTOR (object)))
|
||||
{
|
||||
case PVEC_NORMAL_VECTOR: return Qvector;
|
||||
case PVEC_BIGNUM: return Qinteger;
|
||||
case PVEC_BIGNUM: return Qbignum;
|
||||
case PVEC_MARKER: return Qmarker;
|
||||
case PVEC_SYMBOL_WITH_POS: return Qsymbol_with_pos;
|
||||
case PVEC_OVERLAY: return Qoverlay;
|
||||
|
@ -224,7 +245,10 @@ for example, (type-of 1) returns `integer'. */)
|
|||
case PVEC_WINDOW_CONFIGURATION: return Qwindow_configuration;
|
||||
case PVEC_PROCESS: return Qprocess;
|
||||
case PVEC_WINDOW: return Qwindow;
|
||||
case PVEC_SUBR: return Qsubr;
|
||||
case PVEC_SUBR:
|
||||
return XSUBR (object)->max_args == UNEVALLED ? Qspecial_form
|
||||
: SUBR_NATIVE_COMPILEDP (object) ? Qsubr_native_elisp
|
||||
: Qsubr_primitive;
|
||||
case PVEC_COMPILED: return Qcompiled_function;
|
||||
case PVEC_BUFFER: return Qbuffer;
|
||||
case PVEC_CHAR_TABLE: return Qchar_table;
|
||||
|
@ -4202,7 +4226,9 @@ syms_of_data (void)
|
|||
"Variable binding depth exceeds max-specpdl-size");
|
||||
|
||||
/* Types that type-of returns. */
|
||||
DEFSYM (Qboolean, "boolean");
|
||||
DEFSYM (Qinteger, "integer");
|
||||
DEFSYM (Qbignum, "bignum");
|
||||
DEFSYM (Qsymbol, "symbol");
|
||||
DEFSYM (Qstring, "string");
|
||||
DEFSYM (Qcons, "cons");
|
||||
|
@ -4218,6 +4244,9 @@ syms_of_data (void)
|
|||
DEFSYM (Qprocess, "process");
|
||||
DEFSYM (Qwindow, "window");
|
||||
DEFSYM (Qsubr, "subr");
|
||||
DEFSYM (Qspecial_form, "special-form");
|
||||
DEFSYM (Qsubr_primitive, "subr-primitive");
|
||||
DEFSYM (Qsubr_native_elisp, "subr-native-elisp");
|
||||
DEFSYM (Qcompiled_function, "compiled-function");
|
||||
DEFSYM (Qbuffer, "buffer");
|
||||
DEFSYM (Qframe, "frame");
|
||||
|
@ -4255,6 +4284,7 @@ syms_of_data (void)
|
|||
defsubr (&Seq);
|
||||
defsubr (&Snull);
|
||||
defsubr (&Stype_of);
|
||||
defsubr (&Scl_type_of);
|
||||
defsubr (&Slistp);
|
||||
defsubr (&Snlistp);
|
||||
defsubr (&Sconsp);
|
||||
|
|
|
@ -838,4 +838,41 @@ comparing the subr with a much slower Lisp implementation."
|
|||
(dolist (sym (list nil t 'xyzzy (make-symbol "")))
|
||||
(should (eq sym (bare-symbol (position-symbol sym 0)))))))
|
||||
|
||||
(require 'cl-extra) ;For `cl--class-children'.
|
||||
|
||||
(ert-deftest data-tests--cl-type-of ()
|
||||
;; Make sure that `cl-type-of' returns the most precise type.
|
||||
;; Note: This doesn't work for list/vector structs since those types
|
||||
;; are too difficult/unreliable to detect (so `cl-type-of' only says
|
||||
;; it's a `cons' or a `vector').
|
||||
(dolist (val (list -2 10 (expt 2 128) nil t 'car
|
||||
(symbol-function 'car)
|
||||
(symbol-function 'progn)
|
||||
(position-symbol 'car 7)))
|
||||
(let* ((type (cl-type-of val))
|
||||
(class (cl-find-class type))
|
||||
(alltypes (cl--class-allparents class))
|
||||
;; FIXME: Our type DAG is affected by `symbols-with-pos-enabled'.
|
||||
;; (e.g. `symbolp' returns nil on a sympos if that var is nil).
|
||||
(symbols-with-pos-enabled t))
|
||||
(dolist (parent alltypes)
|
||||
(should (cl-typep val parent))
|
||||
(dolist (subtype (cl--class-children (cl-find-class parent)))
|
||||
(unless (memq subtype alltypes)
|
||||
(unless (memq subtype
|
||||
;; FIXME: Some types don't have any associated
|
||||
;; predicate,
|
||||
'( font-spec font-entity font-object
|
||||
finalizer condvar terminal
|
||||
native-comp-unit interpreted-function
|
||||
tree-sitter-compiled-query
|
||||
tree-sitter-node tree-sitter-parser
|
||||
;; `functionp' also matches things of type
|
||||
;; `symbol' and `cons'.
|
||||
;; FIXME: `subr-primitive-p' also matches
|
||||
;; special-forms.
|
||||
function subr-primitive))
|
||||
(should-not (cl-typep val subtype)))))))))
|
||||
|
||||
|
||||
;;; data-tests.el ends here
|
||||
|
|
Loading…
Add table
Reference in a new issue