Merge branch 'cl-type-of' (bug#69739)

This commit is contained in:
Stefan Monnier 2024-03-18 09:35:11 -04:00
commit be08372bf9
13 changed files with 134 additions and 39 deletions

View file

@ -1485,8 +1485,8 @@ types that are not built into Emacs.
@subsection Type Descriptors
A @dfn{type descriptor} is a @code{record} which holds information
about a type. Slot 1 in the record must be a symbol naming the type, and
@code{type-of} relies on this to return the type of @code{record}
about a type. The first slot in the record must be a symbol naming the type,
and @code{type-of} relies on this to return the type of @code{record}
objects. No other type descriptor slot is used by Emacs; they are
free for use by Lisp extensions.
@ -2175,7 +2175,7 @@ with references to further information.
function @code{type-of}. Recall that each object belongs to one and
only one primitive type; @code{type-of} tells you which one (@pxref{Lisp
Data Types}). But @code{type-of} knows nothing about non-primitive
types. In most cases, it is more convenient to use type predicates than
types. In most cases, it is preferable to use type predicates than
@code{type-of}.
@defun type-of object
@ -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

View file

@ -1647,6 +1647,15 @@ 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'.
** New function `primitive-function-p`.
This is like `subr-primitive-p` except that it returns t only if the
argument is a function rather than a special-form.
** 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

View file

@ -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)

View file

@ -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',
@ -367,6 +365,7 @@
(cl--define-built-in-type buffer atom)
(cl--define-built-in-type window atom)
(cl--define-built-in-type process atom)
(cl--define-built-in-type finalizer atom)
(cl--define-built-in-type window-configuration atom)
(cl--define-built-in-type overlay atom)
(cl--define-built-in-type number-or-marker atom
@ -428,15 +427,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 primitive-function (subr compiled-function)
"Type of functions hand written in C.")
(unless (cl--class-parents (cl--find-class 'cl-structure-object))

View file

@ -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

View file

@ -362,8 +362,7 @@ the result.
The result is a sequence of the same type as SEQUENCE."
(seq-concatenate
(let ((type (type-of sequence)))
(if (eq type 'cons) 'list type))
(if (listp sequence) 'list (type-of sequence))
(seq-subseq sequence 0 n)
(seq-subseq sequence (1+ n))))

View file

@ -312,11 +312,20 @@ value of last one, or nil if there are none."
cond '(empty-body unless) t)))
(defsubst subr-primitive-p (object)
"Return t if OBJECT is a built-in primitive function."
"Return t if OBJECT is a built-in primitive written in C.
Such objects can be functions or special forms."
(declare (side-effect-free error-free))
(and (subrp object)
(not (subr-native-elisp-p object))))
(defsubst primitive-function-p (object)
"Return t if OBJECT is a built-in primitive function.
This excludes special forms, since they are not functions."
(declare (side-effect-free error-free))
(and (subrp object)
(not (or (subr-native-elisp-p object)
(eq (cdr (subr-arity object)) 'unevalled)))))
(defsubst xor (cond1 cond2)
"Return the boolean exclusive-or of COND1 and COND2.
If only one of the arguments is non-nil, return it; otherwise

View file

@ -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));

View file

@ -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
: Qprimitive_function;
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 (Qprimitive_function, "primitive-function");
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);

View file

@ -569,10 +569,8 @@ enum Lisp_Fwd_Type
your object -- this way, the same object could be used to represent
several disparate C structures.
In addition, you need to add switch branches in data.c for Ftype_of.
You also need to add the new type to the constant
`cl--typeof-types' in lisp/emacs-lisp/cl-preloaded.el. */
In addition, you need to add switch branches in data.c for Fcl_type_of
and `cl--define-builtin-type` in lisp/emacs-lisp/cl-preloaded.el. */
/* A Lisp_Object is a tagged pointer or integer. Ordinarily it is a

View file

@ -47,7 +47,7 @@ INLINE_HEADER_BEGIN
#endif
#ifndef BASE_PURESIZE
#define BASE_PURESIZE (2750000 + SYSTEM_PURESIZE_EXTRA + SITELOAD_PURESIZE_EXTRA)
#define BASE_PURESIZE (3000000 + SYSTEM_PURESIZE_EXTRA + SITELOAD_PURESIZE_EXTRA)
#endif
/* Increase BASE_PURESIZE by a ratio depending on the machine's word size. */

View file

@ -349,9 +349,7 @@ bind_values (sqlite3 *db, sqlite3_stmt *stmt, Lisp_Object values)
value = XCAR (values);
values = XCDR (values);
}
Lisp_Object type = Ftype_of (value);
if (EQ (type, Qstring))
if (STRINGP (value))
{
Lisp_Object encoded;
bool blob = false;
@ -385,14 +383,11 @@ bind_values (sqlite3 *db, sqlite3_stmt *stmt, Lisp_Object values)
SSDATA (encoded), SBYTES (encoded),
NULL);
}
else if (EQ (type, Qinteger))
{
if (BIGNUMP (value))
ret = sqlite3_bind_int64 (stmt, i + 1, bignum_to_intmax (value));
else
ret = sqlite3_bind_int64 (stmt, i + 1, XFIXNUM (value));
}
else if (EQ (type, Qfloat))
else if (FIXNUMP (value))
ret = sqlite3_bind_int64 (stmt, i + 1, XFIXNUM (value));
else if (BIGNUMP (value))
ret = sqlite3_bind_int64 (stmt, i + 1, bignum_to_intmax (value));
else if (FLOATP (value))
ret = sqlite3_bind_double (stmt, i + 1, XFLOAT_DATA (value));
else if (NILP (value))
ret = sqlite3_bind_null (stmt, i + 1);

View file

@ -838,4 +838,39 @@ 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'.
function))
(should-not (cl-typep val subtype)))))))))
;;; data-tests.el ends here