* bug#69739: 30.0.50; `type-of` is not precise enough @ 2024-03-11 23:19 Stefan Monnier via Bug reports for GNU Emacs, the Swiss army knife of text editors 2024-03-12 13:37 ` Stefan Monnier via Bug reports for GNU Emacs, the Swiss army knife of text editors 2024-03-12 13:58 ` Eli Zaretskii 0 siblings, 2 replies; 20+ messages in thread From: Stefan Monnier via Bug reports for GNU Emacs, the Swiss army knife of text editors @ 2024-03-11 23:19 UTC (permalink / raw) To: 69739; +Cc: monnier Package: Emacs Version: 30.0.50 `type-of` is supposed to return "the" type of its argument. Given that ELisp has a notion of subtyping, "the" type is expected to mean "the most precise type". This is used in `cl-generic` to decide which method to apply, so it's important that it returns precise information. Currently, there `type-of` fails to return precise enough information in a few cases: - When the argument is nil, it returns `symbol`. The problem here is that `symbol` is not a subtype of `list`, whereas nil is a list. - When the argument is a special form, a C primitive, or a native-compiled function it returns `subr`. Currently our type hierarchy says that `subr` is a subtype of `compiled-function` (and hence of `function`), but a special form is *not* a function (it fails the `functionp` test and can't be `funcall`ed). Currently `cl-generic` works around the first point above by using (if FOO (type-of FOO) 'null) instead of calling `type-of` directly. Suggestion: I suggest we change `type-of` to return `null` for `nil`, `special-form` for subrs that are special forms, `subr-primitive` for C primitives, and `subr-native-elisp` for native-compiled subrs. There are a few other cases where we could improve the precision, tho they are less important because they don't cause problems w.r.t subtyping like the above does. Further improvements could include: - Return `boolean` for `t`. This would be nice otherwise (with the above suggestion) `cl-generic` can dispatch on "nil is a boolean" but not on "t is a boolean". - Return `keyword` for symbols that are keywords. - Return `fixnum` or `bignum` rather than just `integer`. Probably not worth the trouble. - We could go crazy and return `keyword-with-pos` for `symbols-with-pos` that are keywords. Of these further improvements, only the first (return `boolean` for `t`) seems worth the trouble. Still, any change as suggested here would be an incompatible change, so there's risk it'll break some code out there (`type-of` is not used very often, but it *is* used). Another option is to introduce a new function which does the same as `type-of` but with changes like the ones above. (we could even decide to give it a `cl-generic-` prefix to discourage its use elsewhere so we can be more free to change its return value in the future). Stefan ^ permalink raw reply [flat|nested] 20+ messages in thread
* bug#69739: 30.0.50; `type-of` is not precise enough 2024-03-11 23:19 bug#69739: 30.0.50; `type-of` is not precise enough Stefan Monnier via Bug reports for GNU Emacs, the Swiss army knife of text editors @ 2024-03-12 13:37 ` Stefan Monnier via Bug reports for GNU Emacs, the Swiss army knife of text editors 2024-03-12 14:40 ` Stefan Monnier via Bug reports for GNU Emacs, the Swiss army knife of text editors 2024-03-12 13:58 ` Eli Zaretskii 1 sibling, 1 reply; 20+ messages in thread From: Stefan Monnier via Bug reports for GNU Emacs, the Swiss army knife of text editors @ 2024-03-12 13:37 UTC (permalink / raw) To: 69739 [-- Attachment #1: Type: text/plain, Size: 94 bytes --] To make it more concrete, here's a suggested patch. Comments? Objections? Stefan [-- Warning: decoded text below may be mangled, UTF-8 assumed --] [-- Attachment #2: 0001-type-of-Return-more-precise-types-bug-69739.patch --] [-- Type: text/x-diff, Size: 8762 bytes --] From 7b017522a11f8d8e0bafab85b7032d4e9763aeff Mon Sep 17 00:00:00 2001 From: Stefan Monnier <monnier@iro.umontreal.ca> Date: Tue, 12 Mar 2024 09:26:24 -0400 Subject: [PATCH] (type-of): Return more precise types (bug#69739) * src/data.c (Ftype_of): Give more precise types for nil, t, and the three subcases of `subr`. (syms_of_data): Define the corresponding new symbols. * 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. (finalizer): New (previously missing) type. * lisp/emacs-lisp/seq.el (seq-remove-at-position): Adjust to `type-of` returning `null` for nil. * lisp/obsolete/eieio-compat.el (eieio--generic-static-object-generalizer): * lisp/emacs-lisp/cl-generic.el (cl--generic-typeof-generalizer): Simplify. * doc/lispref/objects.texi (Type Predicates): Update accordingly. --- doc/lispref/objects.texi | 13 +++++++------ etc/NEWS | 5 +++++ lisp/emacs-lisp/cl-generic.el | 3 +-- lisp/emacs-lisp/cl-preloaded.el | 11 +++++++---- lisp/emacs-lisp/seq.el | 2 +- lisp/obsolete/eieio-compat.el | 2 +- src/data.c | 14 +++++++++++--- 7 files changed, 33 insertions(+), 17 deletions(-) diff --git a/doc/lispref/objects.texi b/doc/lispref/objects.texi index 279f449a994..00581814825 100644 --- a/doc/lispref/objects.texi +++ b/doc/lispref/objects.texi @@ -1485,8 +1485,8 @@ Type Descriptors @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. @@ -2186,8 +2186,9 @@ Type Predicates @code{float}, @code{font-entity}, @code{font-object}, @code{font-spec}, @code{frame}, @code{hash-table}, @code{integer}, @code{marker}, @code{mutex}, @code{obarray}, @code{overlay}, @code{process}, -@code{string}, @code{subr}, @code{symbol}, @code{thread}, -@code{vector}, @code{window}, or @code{window-configuration}. +@code{string}, @code{subr-primitive}, @code{subr-native-elisp}, +@code{special-form}, @code{symbol}, @code{null}, @code{boolean}, +@code{thread}, @code{vector}, @code{window}, or @code{window-configuration}. However, if @var{object} is a record, the type specified by its first slot is returned; @ref{Records}. @@ -2196,9 +2197,9 @@ Type Predicates @result{} integer @group (type-of 'nil) - @result{} symbol + @result{} null (type-of '()) ; @r{@code{()} is @code{nil}.} - @result{} symbol + @result{} null (type-of '(x)) @result{} cons (type-of (record 'foo)) diff --git a/etc/NEWS b/etc/NEWS index 19cd170e5c7..d06b3f9c06d 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -62,6 +62,11 @@ more details. \f * Incompatible Changes in Emacs 30.1 +** 'type-of' sometimes returns more precise types. +More specifically, for nil and t it returns 'null' and 'boolean' +instead of just 'symbol' and for "subrs", it now returns one of +'special-form', 'subr-primitive', or 'subr-native-elisp'. + ** Tree-Sitter modes are now declared as submodes of the non-TS modes. In order to help the use of those Tree-Sitter modes, they are now declared to have the corresponding non-Tree-Sitter mode as an diff --git a/lisp/emacs-lisp/cl-generic.el b/lisp/emacs-lisp/cl-generic.el index 84eb800ec24..cc7773ad4a2 100644 --- a/lisp/emacs-lisp/cl-generic.el +++ b/lisp/emacs-lisp/cl-generic.el @@ -1339,8 +1339,7 @@ cl--generic-type-specializers (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 #'type-of #'cl--generic-type-specializers) (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 5743684fa89..7ab39fed222 100644 --- a/lisp/emacs-lisp/cl-preloaded.el +++ b/lisp/emacs-lisp/cl-preloaded.el @@ -355,6 +355,7 @@ frame (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 @@ -415,15 +416,17 @@ compiled-function "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)) diff --git a/lisp/emacs-lisp/seq.el b/lisp/emacs-lisp/seq.el index 20077db9e60..cd6f26abdb3 100644 --- a/lisp/emacs-lisp/seq.el +++ b/lisp/emacs-lisp/seq.el @@ -363,7 +363,7 @@ seq-remove-at-position 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 (memq type '(null cons)) 'list type)) (seq-subseq sequence 0 n) (seq-subseq sequence (1+ n)))) diff --git a/lisp/obsolete/eieio-compat.el b/lisp/obsolete/eieio-compat.el index 8fdcebbd1c4..c50524d359e 100644 --- a/lisp/obsolete/eieio-compat.el +++ b/lisp/obsolete/eieio-compat.el @@ -146,7 +146,7 @@ eieio--generic-static-symbol-generalizer (cl-generic-define-generalizer eieio--generic-static-object-generalizer ;; Give it a slightly higher priority than `class' so that the ;; interleaved list comes before the class's non-interleaved list. - 51 #'cl--generic-struct-tag + 51 #'type-of (lambda (tag &rest _) (and (symbolp tag) (setq tag (cl--find-class tag)) (eieio--class-p tag) diff --git a/src/data.c b/src/data.c index 35f4c82c68f..35bd3b19e45 100644 --- a/src/data.c +++ b/src/data.c @@ -202,7 +202,9 @@ DEFUN ("type-of", Ftype_of, Stype_of, 1, 1, 0, return Qinteger; case Lisp_Symbol: - return Qsymbol; + return NILP (object) ? Qnull + : EQ (object, Qt) ? Qboolean + : Qsymbol; case Lisp_String: return Qstring; @@ -224,7 +226,10 @@ DEFUN ("type-of", Ftype_of, Stype_of, 1, 1, 0, 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,6 +4207,7 @@ #define PUT_ERROR(sym, tail, msg) \ "Variable binding depth exceeds max-specpdl-size"); /* Types that type-of returns. */ + DEFSYM (Qboolean, "boolean"); DEFSYM (Qinteger, "integer"); DEFSYM (Qsymbol, "symbol"); DEFSYM (Qstring, "string"); @@ -4217,7 +4223,9 @@ #define PUT_ERROR(sym, tail, msg) \ DEFSYM (Qwindow_configuration, "window-configuration"); 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"); -- 2.43.0 ^ permalink raw reply related [flat|nested] 20+ messages in thread
* bug#69739: 30.0.50; `type-of` is not precise enough 2024-03-12 13:37 ` Stefan Monnier via Bug reports for GNU Emacs, the Swiss army knife of text editors @ 2024-03-12 14:40 ` Stefan Monnier via Bug reports for GNU Emacs, the Swiss army knife of text editors 2024-03-12 16:07 ` Rudolf Schlatte 0 siblings, 1 reply; 20+ messages in thread From: Stefan Monnier via Bug reports for GNU Emacs, the Swiss army knife of text editors @ 2024-03-12 14:40 UTC (permalink / raw) To: 69739 [-- Attachment #1: Type: text/plain, Size: 184 bytes --] > To make it more concrete, here's a suggested patch. Hmm... got a bit carried away on the simplification of cl-generic, sorry. Here's a better patch that does work. Stefan [-- Warning: decoded text below may be mangled, UTF-8 assumed --] [-- Attachment #2: 0001-type-of-Return-more-precise-types-bug-69739.patch --] [-- Type: text/x-diff, Size: 10720 bytes --] From dcea90a70e96d238650f771b2a26348d73b1fa05 Mon Sep 17 00:00:00 2001 From: Stefan Monnier <monnier@iro.umontreal.ca> Date: Tue, 12 Mar 2024 09:26:24 -0400 Subject: [PATCH] (type-of): Return more precise types (bug#69739) * src/data.c (Ftype_of): Give more precise types for nil, t, and the three subcases of `subr`. (syms_of_data): Define the corresponding new symbols. * 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. (finalizer): New (previously missing) type. * lisp/emacs-lisp/seq.el (seq-remove-at-position): Adjust to `type-of` returning `null` for nil. * lisp/obsolete/eieio-core.el (cl--generic-struct-tag): * lisp/emacs-lisp/cl-generic.el (cl--generic-typeof-generalizer): Simplify. * test/lisp/emacs-lisp/ert-tests.el (ert-test-plist-difference-explanation) (ert-test-explain-equal, ert-test-explain-equal-string-properties): Adjust expected answers. * doc/lispref/objects.texi (Type Predicates): Update. --- doc/lispref/objects.texi | 13 +++++++------ etc/NEWS | 5 +++++ lisp/emacs-lisp/cl-generic.el | 3 +-- lisp/emacs-lisp/cl-preloaded.el | 11 +++++++---- lisp/emacs-lisp/eieio-core.el | 2 +- lisp/emacs-lisp/seq.el | 2 +- src/data.c | 14 +++++++++++--- test/lisp/emacs-lisp/ert-tests.el | 8 ++++---- 8 files changed, 37 insertions(+), 21 deletions(-) diff --git a/doc/lispref/objects.texi b/doc/lispref/objects.texi index 279f449a994..00581814825 100644 --- a/doc/lispref/objects.texi +++ b/doc/lispref/objects.texi @@ -1485,8 +1485,8 @@ Type Descriptors @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. @@ -2186,8 +2186,9 @@ Type Predicates @code{float}, @code{font-entity}, @code{font-object}, @code{font-spec}, @code{frame}, @code{hash-table}, @code{integer}, @code{marker}, @code{mutex}, @code{obarray}, @code{overlay}, @code{process}, -@code{string}, @code{subr}, @code{symbol}, @code{thread}, -@code{vector}, @code{window}, or @code{window-configuration}. +@code{string}, @code{subr-primitive}, @code{subr-native-elisp}, +@code{special-form}, @code{symbol}, @code{null}, @code{boolean}, +@code{thread}, @code{vector}, @code{window}, or @code{window-configuration}. However, if @var{object} is a record, the type specified by its first slot is returned; @ref{Records}. @@ -2196,9 +2197,9 @@ Type Predicates @result{} integer @group (type-of 'nil) - @result{} symbol + @result{} null (type-of '()) ; @r{@code{()} is @code{nil}.} - @result{} symbol + @result{} null (type-of '(x)) @result{} cons (type-of (record 'foo)) diff --git a/etc/NEWS b/etc/NEWS index 19cd170e5c7..d06b3f9c06d 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -62,6 +62,11 @@ more details. \f * Incompatible Changes in Emacs 30.1 +** 'type-of' sometimes returns more precise types. +More specifically, for nil and t it returns 'null' and 'boolean' +instead of just 'symbol' and for "subrs", it now returns one of +'special-form', 'subr-primitive', or 'subr-native-elisp'. + ** Tree-Sitter modes are now declared as submodes of the non-TS modes. In order to help the use of those Tree-Sitter modes, they are now declared to have the corresponding non-Tree-Sitter mode as an diff --git a/lisp/emacs-lisp/cl-generic.el b/lisp/emacs-lisp/cl-generic.el index 84eb800ec24..06f70146808 100644 --- a/lisp/emacs-lisp/cl-generic.el +++ b/lisp/emacs-lisp/cl-generic.el @@ -1339,8 +1339,7 @@ cl--generic-type-specializers (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 _) `(type-of ,name)) #'cl--generic-type-specializers) (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 5743684fa89..7ab39fed222 100644 --- a/lisp/emacs-lisp/cl-preloaded.el +++ b/lisp/emacs-lisp/cl-preloaded.el @@ -355,6 +355,7 @@ frame (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 @@ -415,15 +416,17 @@ compiled-function "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)) diff --git a/lisp/emacs-lisp/eieio-core.el b/lisp/emacs-lisp/eieio-core.el index a2f7c4172a3..8221625d885 100644 --- a/lisp/emacs-lisp/eieio-core.el +++ b/lisp/emacs-lisp/eieio-core.el @@ -1046,7 +1046,7 @@ 'inconsistent-class-hierarchy (defun cl--generic-struct-tag (name &rest _) ;; Use exactly the same code as for `typeof'. - `(if ,name (type-of ,name) 'null)) + `(type-of ,name)) (cl-generic-define-generalizer eieio--generic-generalizer ;; Use the exact same tagcode as for cl-struct, so that methods diff --git a/lisp/emacs-lisp/seq.el b/lisp/emacs-lisp/seq.el index 20077db9e60..cd6f26abdb3 100644 --- a/lisp/emacs-lisp/seq.el +++ b/lisp/emacs-lisp/seq.el @@ -363,7 +363,7 @@ seq-remove-at-position 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 (memq type '(null cons)) 'list type)) (seq-subseq sequence 0 n) (seq-subseq sequence (1+ n)))) diff --git a/src/data.c b/src/data.c index 35f4c82c68f..35bd3b19e45 100644 --- a/src/data.c +++ b/src/data.c @@ -202,7 +202,9 @@ DEFUN ("type-of", Ftype_of, Stype_of, 1, 1, 0, return Qinteger; case Lisp_Symbol: - return Qsymbol; + return NILP (object) ? Qnull + : EQ (object, Qt) ? Qboolean + : Qsymbol; case Lisp_String: return Qstring; @@ -224,7 +226,10 @@ DEFUN ("type-of", Ftype_of, Stype_of, 1, 1, 0, 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,6 +4207,7 @@ #define PUT_ERROR(sym, tail, msg) \ "Variable binding depth exceeds max-specpdl-size"); /* Types that type-of returns. */ + DEFSYM (Qboolean, "boolean"); DEFSYM (Qinteger, "integer"); DEFSYM (Qsymbol, "symbol"); DEFSYM (Qstring, "string"); @@ -4217,7 +4223,9 @@ #define PUT_ERROR(sym, tail, msg) \ DEFSYM (Qwindow_configuration, "window-configuration"); 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"); diff --git a/test/lisp/emacs-lisp/ert-tests.el b/test/lisp/emacs-lisp/ert-tests.el index 1aff73d66f6..289069362b3 100644 --- a/test/lisp/emacs-lisp/ert-tests.el +++ b/test/lisp/emacs-lisp/ert-tests.el @@ -674,7 +674,7 @@ ert-test-string-first-line (ert-deftest ert-test-explain-equal () (should (equal (ert--explain-equal nil 'foo) - '(different-atoms nil foo))) + '(different-types nil foo))) (should (equal (ert--explain-equal '(a a) '(a b)) '(list-elt 1 (different-atoms a b)))) (should (equal (ert--explain-equal '(1 48) '(1 49)) @@ -732,10 +732,10 @@ ert-test-plist-difference-explanation nil)) (should (equal (ert--plist-difference-explanation '(a b c t) '(a b)) - '(different-properties-for-key c (different-atoms t nil)))) + '(different-properties-for-key c (different-types t nil)))) (should (equal (ert--plist-difference-explanation '(a b c t) '(c nil a b)) - '(different-properties-for-key c (different-atoms t nil)))) + '(different-properties-for-key c (different-types t nil)))) (should (equal (ert--plist-difference-explanation '(a b c (foo . bar)) '(c (foo . baz) a b)) '(different-properties-for-key c @@ -778,7 +778,7 @@ ert-test-explain-equal-string-properties #("foo" 0 1 (a b)) "foo") '(char 0 "f" - (different-properties-for-key a (different-atoms b nil)) + (different-properties-for-key a (different-types b nil)) context-before "" context-after "oo"))) (should (equal (ert--explain-equal-including-properties-rec -- 2.43.0 ^ permalink raw reply related [flat|nested] 20+ messages in thread
* bug#69739: 30.0.50; `type-of` is not precise enough 2024-03-12 14:40 ` Stefan Monnier via Bug reports for GNU Emacs, the Swiss army knife of text editors @ 2024-03-12 16:07 ` Rudolf Schlatte 2024-03-13 22:10 ` Stefan Monnier via Bug reports for GNU Emacs, the Swiss army knife of text editors 0 siblings, 1 reply; 20+ messages in thread From: Rudolf Schlatte @ 2024-03-12 16:07 UTC (permalink / raw) To: 69739 Stefan Monnier via "Bug reports for GNU Emacs, the Swiss army knife of text editors" <bug-gnu-emacs@gnu.org> writes: > * Incompatible Changes in Emacs 30.1 > > +** 'type-of' sometimes returns more precise types. > +More specifically, for nil and t it returns 'null' and 'boolean' > +instead of just 'symbol' and for "subrs", it now returns one of > +'special-form', 'subr-primitive', or 'subr-native-elisp'. Is it true that in all cases, if `(type-of x)' used to return `foo' before the change, `(typep x 'foo)' still returns t after the change? If so, that might be worth mentioning, since it makes fixing broken code easier. ^ permalink raw reply [flat|nested] 20+ messages in thread
* bug#69739: 30.0.50; `type-of` is not precise enough 2024-03-12 16:07 ` Rudolf Schlatte @ 2024-03-13 22:10 ` Stefan Monnier via Bug reports for GNU Emacs, the Swiss army knife of text editors 0 siblings, 0 replies; 20+ messages in thread From: Stefan Monnier via Bug reports for GNU Emacs, the Swiss army knife of text editors @ 2024-03-13 22:10 UTC (permalink / raw) To: Rudolf Schlatte; +Cc: 69739 >> * Incompatible Changes in Emacs 30.1 >> >> +** 'type-of' sometimes returns more precise types. >> +More specifically, for nil and t it returns 'null' and 'boolean' >> +instead of just 'symbol' and for "subrs", it now returns one of >> +'special-form', 'subr-primitive', or 'subr-native-elisp'. > > Is it true that in all cases, if `(type-of x)' used to return `foo' > before the change, `(typep x 'foo)' still returns t after the change? > If so, that might be worth mentioning, since it makes fixing broken code > easier. Yes. But in any case, it seems we'll go with a different patch which leaves `type-of` unchanged. Stefan ^ permalink raw reply [flat|nested] 20+ messages in thread
* bug#69739: 30.0.50; `type-of` is not precise enough 2024-03-11 23:19 bug#69739: 30.0.50; `type-of` is not precise enough Stefan Monnier via Bug reports for GNU Emacs, the Swiss army knife of text editors 2024-03-12 13:37 ` Stefan Monnier via Bug reports for GNU Emacs, the Swiss army knife of text editors @ 2024-03-12 13:58 ` Eli Zaretskii 2024-03-12 14:43 ` Stefan Monnier via Bug reports for GNU Emacs, the Swiss army knife of text editors 1 sibling, 1 reply; 20+ messages in thread From: Eli Zaretskii @ 2024-03-12 13:58 UTC (permalink / raw) To: Stefan Monnier; +Cc: 69739 > Cc: monnier@iro.umontreal.ca > Date: Mon, 11 Mar 2024 19:19:40 -0400 > From: Stefan Monnier via "Bug reports for GNU Emacs, > the Swiss army knife of text editors" <bug-gnu-emacs@gnu.org> > > Still, any change as suggested here would be an incompatible change, so > there's risk it'll break some code out there (`type-of` is not used very > often, but it *is* used). Another option is to introduce a new function > which does the same as `type-of` but with changes like the ones above. > (we could even decide to give it a `cl-generic-` prefix to discourage > its use elsewhere so we can be more free to change its return value in > the future). I'd prefer a non-breaking change, if possible and reasonable. Thanks. ^ permalink raw reply [flat|nested] 20+ messages in thread
* bug#69739: 30.0.50; `type-of` is not precise enough 2024-03-12 13:58 ` Eli Zaretskii @ 2024-03-12 14:43 ` Stefan Monnier via Bug reports for GNU Emacs, the Swiss army knife of text editors 2024-03-12 15:02 ` Eli Zaretskii 0 siblings, 1 reply; 20+ messages in thread From: Stefan Monnier via Bug reports for GNU Emacs, the Swiss army knife of text editors @ 2024-03-12 14:43 UTC (permalink / raw) To: Eli Zaretskii; +Cc: 69739 >> Still, any change as suggested here would be an incompatible change, so >> there's risk it'll break some code out there (`type-of` is not used very >> often, but it *is* used). Another option is to introduce a new function >> which does the same as `type-of` but with changes like the ones above. >> (we could even decide to give it a `cl-generic-` prefix to discourage >> its use elsewhere so we can be more free to change its return value in >> the future). > I'd prefer a non-breaking change, if possible and reasonable. Suggestion for a good name for that new function? Should it make `type-of` obsolete? Stefan ^ permalink raw reply [flat|nested] 20+ messages in thread
* bug#69739: 30.0.50; `type-of` is not precise enough 2024-03-12 14:43 ` Stefan Monnier via Bug reports for GNU Emacs, the Swiss army knife of text editors @ 2024-03-12 15:02 ` Eli Zaretskii 2024-03-12 15:39 ` Stefan Monnier via Bug reports for GNU Emacs, the Swiss army knife of text editors 0 siblings, 1 reply; 20+ messages in thread From: Eli Zaretskii @ 2024-03-12 15:02 UTC (permalink / raw) To: Stefan Monnier; +Cc: 69739 > From: Stefan Monnier <monnier@iro.umontreal.ca> > Cc: 69739@debbugs.gnu.org > Date: Tue, 12 Mar 2024 10:43:24 -0400 > > >> Still, any change as suggested here would be an incompatible change, so > >> there's risk it'll break some code out there (`type-of` is not used very > >> often, but it *is* used). Another option is to introduce a new function > >> which does the same as `type-of` but with changes like the ones above. > >> (we could even decide to give it a `cl-generic-` prefix to discourage > >> its use elsewhere so we can be more free to change its return value in > >> the future). > > I'd prefer a non-breaking change, if possible and reasonable. > > Suggestion for a good name for that new function? cl-generic-type-of is one. > Should it make `type-of` obsolete? I don't have an opinion on this. Depends on how much type-of is used, I guess. Maybe we should wait for the next major release and decide whether to deprecate then? ^ permalink raw reply [flat|nested] 20+ messages in thread
* bug#69739: 30.0.50; `type-of` is not precise enough 2024-03-12 15:02 ` Eli Zaretskii @ 2024-03-12 15:39 ` Stefan Monnier via Bug reports for GNU Emacs, the Swiss army knife of text editors 2024-03-13 11:55 ` Eli Zaretskii 0 siblings, 1 reply; 20+ messages in thread From: Stefan Monnier via Bug reports for GNU Emacs, the Swiss army knife of text editors @ 2024-03-12 15:39 UTC (permalink / raw) To: Eli Zaretskii; +Cc: 69739 >> >> Still, any change as suggested here would be an incompatible change, so >> >> there's risk it'll break some code out there (`type-of` is not used very >> >> often, but it *is* used). Another option is to introduce a new function >> >> which does the same as `type-of` but with changes like the ones above. >> >> (we could even decide to give it a `cl-generic-` prefix to discourage >> >> its use elsewhere so we can be more free to change its return value in >> >> the future). >> > I'd prefer a non-breaking change, if possible and reasonable. >> >> Suggestion for a good name for that new function? > > cl-generic-type-of is one. > >> Should it make `type-of` obsolete? > > I don't have an opinion on this. Depends on how much type-of is used, > I guess. Maybe we should wait for the next major release and decide > whether to deprecate then? In my review of uses of `type-of`, I found: - The most common seemed to be when generating error messages like (error "Arg should be an int rather than a %s" (type-of arg)) - The next most common was misuses like (eq (type-of FOO) 'BAR) which should be using `BAR-p` instead (or `cl-typep`). - The next most common was when `type-of` is applied to a struct (i.e. it returns the same as (aref FOO 0)). Since this was new functionality in Emacs-26, I'm surprised at how widespread it is. - There were a few that are combined with case/pcase and would probably be better served by `cl-typecase`. I think that marking it obsolete wouldn't be an option if the new function is named something like `cl-generic-type-of`: it would have to be a more "neutral" name. The reason why I'd like to make it obsolete (if it can't be changed) is to try and avoid having two slightly-different functions where most users don't actually care about the difference. Stefan ^ permalink raw reply [flat|nested] 20+ messages in thread
* bug#69739: 30.0.50; `type-of` is not precise enough 2024-03-12 15:39 ` Stefan Monnier via Bug reports for GNU Emacs, the Swiss army knife of text editors @ 2024-03-13 11:55 ` Eli Zaretskii 2024-03-14 16:56 ` Stefan Monnier via Bug reports for GNU Emacs, the Swiss army knife of text editors 0 siblings, 1 reply; 20+ messages in thread From: Eli Zaretskii @ 2024-03-13 11:55 UTC (permalink / raw) To: Stefan Monnier; +Cc: 69739 > From: Stefan Monnier <monnier@iro.umontreal.ca> > Cc: 69739@debbugs.gnu.org > Date: Tue, 12 Mar 2024 11:39:56 -0400 > > I think that marking it obsolete wouldn't be an option if the new > function is named something like `cl-generic-type-of`: it would have to > be a more "neutral" name. I just picked up your own suggestion. I'm not wedded to that name, so if there are better alternatives, let's hear them. > The reason why I'd like to make it obsolete (if it can't be changed) is > to try and avoid having two slightly-different functions where most > users don't actually care about the difference. Sure, but we don't usually declare such APIs obsolete the very day the better one is introduced. We usually let the community some time to adjust, and maybe tell us where our ideas were wrong or need some adjustments. ^ permalink raw reply [flat|nested] 20+ messages in thread
* bug#69739: 30.0.50; `type-of` is not precise enough 2024-03-13 11:55 ` Eli Zaretskii @ 2024-03-14 16:56 ` Stefan Monnier via Bug reports for GNU Emacs, the Swiss army knife of text editors 2024-03-15 9:42 ` Andrea Corallo ` (2 more replies) 0 siblings, 3 replies; 20+ messages in thread From: Stefan Monnier via Bug reports for GNU Emacs, the Swiss army knife of text editors @ 2024-03-14 16:56 UTC (permalink / raw) To: Eli Zaretskii; +Cc: 69739 [-- Attachment #1: Type: text/plain, Size: 215 bytes --] OK, here's a new patch. I ended up with `cl-type-of` as the name of the new function (after all, it tries to better match the expected semantics of Common Lisp's `type-of`). Comments? Objections? Stefan [-- Warning: decoded text below may be mangled, UTF-8 assumed --] [-- Attachment #2: 0001-cl-type-of-New-function-to-return-more-precise-types.patch --] [-- Type: text/x-diff, Size: 9196 bytes --] From a1d656bf089af3e55fd08ef5ed3e33f207360593 Mon Sep 17 00:00:00 2001 From: Stefan Monnier <monnier@iro.umontreal.ca> Date: Tue, 12 Mar 2024 09:26:24 -0400 Subject: [PATCH 1/2] (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. * 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. --- etc/NEWS | 5 +++++ lisp/emacs-lisp/cl-generic.el | 6 ++--- lisp/emacs-lisp/cl-preloaded.el | 12 +++++----- lisp/emacs-lisp/eieio-core.el | 2 +- src/comp.c | 2 +- src/data.c | 40 ++++++++++++++++++++++++++++----- 6 files changed, 50 insertions(+), 17 deletions(-) diff --git a/etc/NEWS b/etc/NEWS index 2985169ea91..17f6916cebf 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -1628,6 +1628,11 @@ values. \f * 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 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 @@ cl-generic-generalizers (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 @@ cl--generic-type-specializers (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) 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 @@ cl--define-built-in-type ',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 @@ compiled-function "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)) 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 @@ 'inconsistent-class-hierarchy (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 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) { 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)); diff --git a/src/data.c b/src/data.c index 35f4c82c68f..a961dd3108c 100644 --- a/src/data.c +++ b/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 symbol returned names the most specific possible type of the object. +for example, (object-type 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 @@ DEFUN ("type-of", Ftype_of, Stype_of, 1, 1, 0, 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 @@ DEFUN ("type-of", Ftype_of, Stype_of, 1, 1, 0, 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 @@ #define PUT_ERROR(sym, tail, msg) \ "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 @@ #define PUT_ERROR(sym, tail, msg) \ 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 @@ #define PUT_ERROR(sym, tail, msg) \ defsubr (&Seq); defsubr (&Snull); defsubr (&Stype_of); + defsubr (&Scl_type_of); defsubr (&Slistp); defsubr (&Snlistp); defsubr (&Sconsp); -- 2.43.0 [-- Warning: decoded text below may be mangled, UTF-8 assumed --] [-- Attachment #3: 0002-Followup-changes-to-cl-type-of.patch --] [-- Type: text/x-diff, Size: 5512 bytes --] From 637b22ee275762e3a88342f7c0c1e9a997f2ed97 Mon Sep 17 00:00:00 2001 From: Stefan Monnier <monnier@iro.umontreal.ca> Date: Thu, 14 Mar 2024 12:49:08 -0400 Subject: [PATCH 2/2] Followup changes to `cl-type-of` These changes came up while working on `cl-type-of` but are not directly related to the new `cl-type-of`. The BASE_PURESIZE bump was needed at some point on one of my machine, not sure why. * src/puresize.h (BASE_PURESIZE): Bump up. * src/sqlite.c (bind_value): Don't use `Ftype_of`. * lisp/emacs-lisp/seq.el (seq-remove-at-position): Simplify. * lisp/emacs-lisp/cl-preloaded.el (finalizer): New (previously missing) type. * doc/lispref/objects.texi (Type Predicates): Minor tweaks. --- doc/lispref/objects.texi | 6 +++--- lisp/emacs-lisp/cl-preloaded.el | 1 + lisp/emacs-lisp/seq.el | 3 +-- src/lisp.h | 6 ++---- src/puresize.h | 2 +- src/sqlite.c | 17 ++++++----------- 6 files changed, 14 insertions(+), 21 deletions(-) diff --git a/doc/lispref/objects.texi b/doc/lispref/objects.texi index 279f449a994..804f52ba8ac 100644 --- a/doc/lispref/objects.texi +++ b/doc/lispref/objects.texi @@ -1485,8 +1485,8 @@ Type Descriptors @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 @@ Type Predicates 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 diff --git a/lisp/emacs-lisp/cl-preloaded.el b/lisp/emacs-lisp/cl-preloaded.el index 3e89afea452..0e8704a93c1 100644 --- a/lisp/emacs-lisp/cl-preloaded.el +++ b/lisp/emacs-lisp/cl-preloaded.el @@ -365,6 +365,7 @@ frame (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 diff --git a/lisp/emacs-lisp/seq.el b/lisp/emacs-lisp/seq.el index 20077db9e60..a20cff16982 100644 --- a/lisp/emacs-lisp/seq.el +++ b/lisp/emacs-lisp/seq.el @@ -362,8 +362,7 @@ seq-remove-at-position 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)))) diff --git a/src/lisp.h b/src/lisp.h index f353e4956eb..f86758c88fb 100644 --- a/src/lisp.h +++ b/src/lisp.h @@ -569,10 +569,8 @@ #define ENUM_BF(TYPE) enum 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 diff --git a/src/puresize.h b/src/puresize.h index ac5d2da30dc..2a716872832 100644 --- a/src/puresize.h +++ b/src/puresize.h @@ -47,7 +47,7 @@ #define SITELOAD_PURESIZE_EXTRA 0 #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. */ diff --git a/src/sqlite.c b/src/sqlite.c index 7a018b28aa4..261080da673 100644 --- a/src/sqlite.c +++ b/src/sqlite.c @@ -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); -- 2.43.0 ^ permalink raw reply related [flat|nested] 20+ messages in thread
* bug#69739: 30.0.50; `type-of` is not precise enough 2024-03-14 16:56 ` Stefan Monnier via Bug reports for GNU Emacs, the Swiss army knife of text editors @ 2024-03-15 9:42 ` Andrea Corallo 2024-03-15 10:54 ` Andrea Corallo 2024-03-15 9:51 ` Basil L. Contovounesios 2024-03-15 11:50 ` Eli Zaretskii 2 siblings, 1 reply; 20+ messages in thread From: Andrea Corallo @ 2024-03-15 9:42 UTC (permalink / raw) To: 69739; +Cc: eliz, monnier Stefan Monnier via "Bug reports for GNU Emacs, the Swiss army knife of text editors" <bug-gnu-emacs@gnu.org> writes: > OK, here's a new patch. > I ended up with `cl-type-of` as the name of the new function (after > all, it tries to better match the expected semantics of Common Lisp's > `type-of`). > > Comments? Objections? FWIW: I think this is a good change and agree `cl-type-of` is a good name for the new function. I'm just wondering if we should have some test this. Bests! Andrea ^ permalink raw reply [flat|nested] 20+ messages in thread
* bug#69739: 30.0.50; `type-of` is not precise enough 2024-03-15 9:42 ` Andrea Corallo @ 2024-03-15 10:54 ` Andrea Corallo 0 siblings, 0 replies; 20+ messages in thread From: Andrea Corallo @ 2024-03-15 10:54 UTC (permalink / raw) To: 69739; +Cc: eliz, monnier Andrea Corallo <acorallo@gnu.org> writes: > Stefan Monnier via "Bug reports for GNU Emacs, the Swiss army knife of > text editors" <bug-gnu-emacs@gnu.org> writes: > >> OK, here's a new patch. >> I ended up with `cl-type-of` as the name of the new function (after >> all, it tries to better match the expected semantics of Common Lisp's >> `type-of`). >> >> Comments? Objections? > > FWIW: I think this is a good change and agree `cl-type-of` is a good > name for the new function. I'm just wondering if we should have some > test this. ^^^ to cover Andrea ^ permalink raw reply [flat|nested] 20+ messages in thread
* bug#69739: 30.0.50; `type-of` is not precise enough 2024-03-14 16:56 ` Stefan Monnier via Bug reports for GNU Emacs, the Swiss army knife of text editors 2024-03-15 9:42 ` Andrea Corallo @ 2024-03-15 9:51 ` Basil L. Contovounesios 2024-03-15 14:09 ` Stefan Monnier via Bug reports for GNU Emacs, the Swiss army knife of text editors 2024-03-15 11:50 ` Eli Zaretskii 2 siblings, 1 reply; 20+ messages in thread From: Basil L. Contovounesios @ 2024-03-15 9:51 UTC (permalink / raw) To: Eli Zaretskii; +Cc: Stefan Monnier, 69739 Stefan Monnier [2024-03-14 12:56 -0400] wrote: > I ended up with `cl-type-of` as the name of the new function (after > all, it tries to better match the expected semantics of Common Lisp's > `type-of`). > > Comments? Objections? No objections here, just one question: did you consider extending type-of with an optional argument instead of introducing cl-type-of? Or would that be too messy? Thanks, -- Basil ^ permalink raw reply [flat|nested] 20+ messages in thread
* bug#69739: 30.0.50; `type-of` is not precise enough 2024-03-15 9:51 ` Basil L. Contovounesios @ 2024-03-15 14:09 ` Stefan Monnier via Bug reports for GNU Emacs, the Swiss army knife of text editors 2024-03-15 15:18 ` Eli Zaretskii 0 siblings, 1 reply; 20+ messages in thread From: Stefan Monnier via Bug reports for GNU Emacs, the Swiss army knife of text editors @ 2024-03-15 14:09 UTC (permalink / raw) To: Basil L. Contovounesios, Andrea Corallo, Eli Zaretskii; +Cc: 69739 Basil L. Contovounesios [2024-03-15 10:51:33] wrote: > No objections here, just one question: did you consider extending > type-of with an optional argument instead of introducing cl-type-of? > Or would that be too messy? I did. I decided to introduce a new function for the following reasons: - The main use for the new function is in the dispatch code of `cl-generic` where it's on the critical path, so one less argument is better than one more argument. Not sure how much impact it would really have, tho, so it's not a very strong argument. - I don't know of a good reason to prefer the `type-of` behavior other than backward compatibility, and requiring a non-nil arg to get the "good" behavior would make it difficult to get rid of the legacy behavior. Andrea Corallo [2024-03-15 06:54:36] wrote: > FWIW: I think this is a good change and agree `cl-type-of` is a good > name for the new function. I'm just wondering if we should have some > test to cover this. Are you suggesting that my code can be anything else than perfect? More seriously, I couldn't think of a good test, but I'll try harder. Eli Zaretskii [2024-03-15 13:50:56] wrote: > Should we document this new function in the ELisp manual? I was thinking that we should do that only when/if we demote `type-of` (`(cl-)type-of` is an operation that's used very rarely, so I think it's best to keep it to a minimum in the manual), Of course, the name also begs the question whether it should be in the ELisp manual or in the CL manual. In a previous version of the patch I had it documented alongside `type-of`, so I can easily dig that up if you think it's best. Stefan ^ permalink raw reply [flat|nested] 20+ messages in thread
* bug#69739: 30.0.50; `type-of` is not precise enough 2024-03-15 14:09 ` Stefan Monnier via Bug reports for GNU Emacs, the Swiss army knife of text editors @ 2024-03-15 15:18 ` Eli Zaretskii 2024-03-17 22:29 ` Stefan Monnier via Bug reports for GNU Emacs, the Swiss army knife of text editors 0 siblings, 1 reply; 20+ messages in thread From: Eli Zaretskii @ 2024-03-15 15:18 UTC (permalink / raw) To: Stefan Monnier; +Cc: acorallo, basil, 69739 > From: Stefan Monnier <monnier@iro.umontreal.ca> > Cc: 69739@debbugs.gnu.org > Date: Fri, 15 Mar 2024 10:09:11 -0400 > > Eli Zaretskii [2024-03-15 13:50:56] wrote: > > Should we document this new function in the ELisp manual? > > I was thinking that we should do that only when/if we demote `type-of` > (`(cl-)type-of` is an operation that's used very rarely, so I think it's > best to keep it to a minimum in the manual), > Of course, the name also begs the question whether it should be in the > ELisp manual or in the CL manual. > > In a previous version of the patch I had it documented alongside > `type-of`, so I can easily dig that up if you think it's best. Yes, I think we should document it near type-of, as the explanation when and why to prefer cl-type-of is quite simple and easily understandable. ^ permalink raw reply [flat|nested] 20+ messages in thread
* bug#69739: 30.0.50; `type-of` is not precise enough 2024-03-15 15:18 ` Eli Zaretskii @ 2024-03-17 22:29 ` Stefan Monnier via Bug reports for GNU Emacs, the Swiss army knife of text editors 2024-03-18 12:56 ` Eli Zaretskii 0 siblings, 1 reply; 20+ messages in thread From: Stefan Monnier via Bug reports for GNU Emacs, the Swiss army knife of text editors @ 2024-03-17 22:29 UTC (permalink / raw) To: Eli Zaretskii; +Cc: acorallo, basil, 69739 [-- Attachment #1: Type: text/plain, Size: 402 bytes --] > Yes, I think we should document it near type-of, as the explanation > when and why to prefer cl-type-of is quite simple and easily > understandable. OK, here's a new (set of) patches (also available in the `scratch/object-type` branch). I added the doc as well as a test (which pointed to the `subr-primitive-p` problem), and an additional hunk which fixes the `subr-primitive-p`. Stefan [-- Warning: decoded text below may be mangled, UTF-8 assumed --] [-- Attachment #2: 0001-cl-type-of-New-function-to-return-more-precise-types.patch --] [-- Type: text/x-diff, Size: 12593 bytes --] From 3917c4be34f5c1d6249da474337f8f23544dcc9b Mon Sep 17 00:00:00 2001 From: Stefan Monnier <monnier@iro.umontreal.ca> Date: Tue, 12 Mar 2024 09:26:24 -0400 Subject: [PATCH 1/3] (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. --- doc/lispref/objects.texi | 21 +++++++++++++++++ etc/NEWS | 5 +++++ lisp/emacs-lisp/cl-generic.el | 6 ++--- lisp/emacs-lisp/cl-preloaded.el | 12 +++++----- lisp/emacs-lisp/eieio-core.el | 2 +- src/comp.c | 2 +- src/data.c | 40 ++++++++++++++++++++++++++++----- 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..6e92f9bcc2a 100644 --- a/doc/lispref/objects.texi +++ b/doc/lispref/objects.texi @@ -2207,6 +2207,27 @@ Type Predicates @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 +(object-type 1) + @result{} fixnum +@group +(object-type 'nil) + @result{} null +(object-type (record 'foo)) + @result{} foo +@end group +@end example +@end defun + + @node Equality Predicates @section Equality Predicates @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. \f * 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 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 @@ cl-generic-generalizers (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 @@ cl--generic-type-specializers (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) 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 @@ cl--define-built-in-type ',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 @@ compiled-function "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)) 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 @@ 'inconsistent-class-hierarchy (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 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) { 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)); diff --git a/src/data.c b/src/data.c index 35f4c82c68f..a961dd3108c 100644 --- a/src/data.c +++ b/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 symbol returned names the most specific possible type of the object. +for example, (object-type 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 @@ DEFUN ("type-of", Ftype_of, Stype_of, 1, 1, 0, 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 @@ DEFUN ("type-of", Ftype_of, Stype_of, 1, 1, 0, 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 @@ #define PUT_ERROR(sym, tail, msg) \ "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 @@ #define PUT_ERROR(sym, tail, msg) \ 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 @@ #define PUT_ERROR(sym, tail, msg) \ defsubr (&Seq); defsubr (&Snull); defsubr (&Stype_of); + defsubr (&Scl_type_of); defsubr (&Slistp); defsubr (&Snlistp); 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 @@ data-tests-bare-symbol (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 -- 2.43.0 [-- Warning: decoded text below may be mangled, UTF-8 assumed --] [-- Attachment #3: 0002-primitive-function-New-type.patch --] [-- Type: text/x-diff, Size: 5122 bytes --] From 41184670fd4b720350da58f6051212e5cb3ea7a7 Mon Sep 17 00:00:00 2001 From: Stefan Monnier <monnier@iro.umontreal.ca> Date: Sun, 17 Mar 2024 17:29:02 -0400 Subject: [PATCH 2/3] (primitive-function): New type The type hierarchy and `cl-type-of` code assumed that `subr-primitive` only applies to functions, but since it also accepts special-forms it makes it an unsuitable choice since it can't be a subtype of `compiled-function`. So, use a new type `primitive-function` instead. * lisp/subr.el (subr-primitive-p): Fix docstring (bug#69832). (primitive-function-p): New function. * lisp/emacs-lisp/cl-preloaded.el (primitive-function): Rename from `subr-primitive` since `subr-primitive-p` means something else. * src/data.c (Fcl_type_of): Return `primitive-function` instead of `subr-primitive` for C functions. (syms_of_data): Adjust accordingly. * test/src/data-tests.el (data-tests--cl-type-of): Remove workaround. --- etc/NEWS | 4 ++++ lisp/emacs-lisp/cl-preloaded.el | 2 +- lisp/subr.el | 9 ++++++++- src/data.c | 4 ++-- test/src/data-tests.el | 4 +--- 5 files changed, 16 insertions(+), 7 deletions(-) diff --git a/etc/NEWS b/etc/NEWS index b522fbd338b..69e61d91b0e 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -1652,6 +1652,10 @@ 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 diff --git a/lisp/emacs-lisp/cl-preloaded.el b/lisp/emacs-lisp/cl-preloaded.el index 3e89afea452..d11c97a3e3a 100644 --- a/lisp/emacs-lisp/cl-preloaded.el +++ b/lisp/emacs-lisp/cl-preloaded.el @@ -436,7 +436,7 @@ special-form "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) +(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)) diff --git a/lisp/subr.el b/lisp/subr.el index 38a3f6edb34..9e63e409349 100644 --- a/lisp/subr.el +++ b/lisp/subr.el @@ -312,11 +312,18 @@ unless 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." (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." + (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 diff --git a/src/data.c b/src/data.c index a961dd3108c..fe631b27ccc 100644 --- a/src/data.c +++ b/src/data.c @@ -248,7 +248,7 @@ DEFUN ("cl-type-of", Fcl_type_of, Scl_type_of, 1, 1, 0, case PVEC_SUBR: return XSUBR (object)->max_args == UNEVALLED ? Qspecial_form : SUBR_NATIVE_COMPILEDP (object) ? Qsubr_native_elisp - : Qsubr_primitive; + : Qprimitive_function; case PVEC_COMPILED: return Qcompiled_function; case PVEC_BUFFER: return Qbuffer; case PVEC_CHAR_TABLE: return Qchar_table; @@ -4245,7 +4245,7 @@ #define PUT_ERROR(sym, tail, msg) \ DEFSYM (Qwindow, "window"); DEFSYM (Qsubr, "subr"); DEFSYM (Qspecial_form, "special-form"); - DEFSYM (Qsubr_primitive, "subr-primitive"); + DEFSYM (Qprimitive_function, "primitive-function"); DEFSYM (Qsubr_native_elisp, "subr-native-elisp"); DEFSYM (Qcompiled_function, "compiled-function"); DEFSYM (Qbuffer, "buffer"); diff --git a/test/src/data-tests.el b/test/src/data-tests.el index 9d76c58224d..daa49e671b5 100644 --- a/test/src/data-tests.el +++ b/test/src/data-tests.el @@ -869,9 +869,7 @@ data-tests--cl-type-of 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)) + function)) (should-not (cl-typep val subtype))))))))) -- 2.43.0 [-- Warning: decoded text below may be mangled, UTF-8 assumed --] [-- Attachment #4: 0003-Followup-changes-to-cl-type-of.patch --] [-- Type: text/x-diff, Size: 5512 bytes --] From 3bab41044eb2c3c9ec373eae4efb92832c2fe6c3 Mon Sep 17 00:00:00 2001 From: Stefan Monnier <monnier@iro.umontreal.ca> Date: Thu, 14 Mar 2024 12:49:08 -0400 Subject: [PATCH 3/3] Followup changes to `cl-type-of` These changes came up while working on `cl-type-of` but are not directly related to the new `cl-type-of`. The BASE_PURESIZE bump was needed at some point on one of my machine, not sure why. * src/puresize.h (BASE_PURESIZE): Bump up. * src/sqlite.c (bind_value): Don't use `Ftype_of`. * lisp/emacs-lisp/seq.el (seq-remove-at-position): Simplify. * lisp/emacs-lisp/cl-preloaded.el (finalizer): New (previously missing) type. * doc/lispref/objects.texi (Type Predicates): Minor tweaks. --- doc/lispref/objects.texi | 6 +++--- lisp/emacs-lisp/cl-preloaded.el | 1 + lisp/emacs-lisp/seq.el | 3 +-- src/lisp.h | 6 ++---- src/puresize.h | 2 +- src/sqlite.c | 17 ++++++----------- 6 files changed, 14 insertions(+), 21 deletions(-) diff --git a/doc/lispref/objects.texi b/doc/lispref/objects.texi index 6e92f9bcc2a..50dddf9f0e5 100644 --- a/doc/lispref/objects.texi +++ b/doc/lispref/objects.texi @@ -1485,8 +1485,8 @@ Type Descriptors @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 @@ Type Predicates 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 diff --git a/lisp/emacs-lisp/cl-preloaded.el b/lisp/emacs-lisp/cl-preloaded.el index d11c97a3e3a..cba56e0bbd4 100644 --- a/lisp/emacs-lisp/cl-preloaded.el +++ b/lisp/emacs-lisp/cl-preloaded.el @@ -365,6 +365,7 @@ frame (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 diff --git a/lisp/emacs-lisp/seq.el b/lisp/emacs-lisp/seq.el index 20077db9e60..a20cff16982 100644 --- a/lisp/emacs-lisp/seq.el +++ b/lisp/emacs-lisp/seq.el @@ -362,8 +362,7 @@ seq-remove-at-position 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)))) diff --git a/src/lisp.h b/src/lisp.h index f353e4956eb..f86758c88fb 100644 --- a/src/lisp.h +++ b/src/lisp.h @@ -569,10 +569,8 @@ #define ENUM_BF(TYPE) enum 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 diff --git a/src/puresize.h b/src/puresize.h index ac5d2da30dc..2a716872832 100644 --- a/src/puresize.h +++ b/src/puresize.h @@ -47,7 +47,7 @@ #define SITELOAD_PURESIZE_EXTRA 0 #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. */ diff --git a/src/sqlite.c b/src/sqlite.c index 7a018b28aa4..261080da673 100644 --- a/src/sqlite.c +++ b/src/sqlite.c @@ -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); -- 2.43.0 ^ permalink raw reply related [flat|nested] 20+ messages in thread
* bug#69739: 30.0.50; `type-of` is not precise enough 2024-03-17 22:29 ` Stefan Monnier via Bug reports for GNU Emacs, the Swiss army knife of text editors @ 2024-03-18 12:56 ` Eli Zaretskii 2024-03-18 13:33 ` Stefan Monnier via Bug reports for GNU Emacs, the Swiss army knife of text editors 0 siblings, 1 reply; 20+ messages in thread From: Eli Zaretskii @ 2024-03-18 12:56 UTC (permalink / raw) To: Stefan Monnier; +Cc: acorallo, basil, 69739 > From: Stefan Monnier <monnier@iro.umontreal.ca> > Cc: basil@contovou.net, acorallo@gnu.org, 69739@debbugs.gnu.org > Date: Sun, 17 Mar 2024 18:29:10 -0400 > > > Yes, I think we should document it near type-of, as the explanation > > when and why to prefer cl-type-of is quite simple and easily > > understandable. > > OK, here's a new (set of) patches (also available in the > `scratch/object-type` branch). I added the doc as well as a test > (which pointed to the `subr-primitive-p` problem), and an additional > hunk which fixes the `subr-primitive-p`. Thanks, a few nits below. > +@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 > +(object-type 1) > + @result{} fixnum > +@group > +(object-type 'nil) > + @result{} null > +(object-type (record 'foo)) > + @result{} foo "object-type"? > 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 ^^ I think we want a comma there. > +DEFUN ("cl-type-of", Fcl_type_of, Scl_type_of, 1, 1, 0, > + doc: /* Return a symbol representing the type of OBJECT. > +The symbol returned names the most specific possible type of the object. ^^^^^^^^^^^^^^^^^^^ I think "The returned symbol" is better here, as it prevents a possible confusion (whether "returned" alludes to "symbol" or to "names"). > +for example, (object-type nil) returns `null'. ^^^^^^^^^^^ "object-type"? > (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." > (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." > + (declare (side-effect-free error-free)) > + (and (subrp object) > + (not (or (subr-native-elisp-p object) > + (eq (cdr (subr-arity object)) 'unevalled))))) Should these doc strings mention the special case of special form, which each one of them treats differently? ^ permalink raw reply [flat|nested] 20+ messages in thread
* bug#69739: 30.0.50; `type-of` is not precise enough 2024-03-18 12:56 ` Eli Zaretskii @ 2024-03-18 13:33 ` Stefan Monnier via Bug reports for GNU Emacs, the Swiss army knife of text editors 0 siblings, 0 replies; 20+ messages in thread From: Stefan Monnier via Bug reports for GNU Emacs, the Swiss army knife of text editors @ 2024-03-18 13:33 UTC (permalink / raw) To: Eli Zaretskii; +Cc: acorallo, basil, 69739-done >> +@example >> +(object-type 1) >> + @result{} fixnum >> +@group >> +(object-type 'nil) >> + @result{} null >> +(object-type (record 'foo)) >> + @result{} foo > > "object-type"? Oops! thanks. >> 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 > ^^ > I think we want a comma there. >> +DEFUN ("cl-type-of", Fcl_type_of, Scl_type_of, 1, 1, 0, >> + doc: /* Return a symbol representing the type of OBJECT. >> +The symbol returned names the most specific possible type of the object. > ^^^^^^^^^^^^^^^^^^^ > I think "The returned symbol" is better here, as it prevents a > possible confusion (whether "returned" alludes to "symbol" or to > "names"). Agreed. >> +for example, (object-type nil) returns `null'. > ^^^^^^^^^^^ > "object-type"? As you can see I had used `object-type` instead of `cl-type-of` in some prior version of the code :-) >> (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." >> (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." >> + (declare (side-effect-free error-free)) >> + (and (subrp object) >> + (not (or (subr-native-elisp-p object) >> + (eq (cdr (subr-arity object)) 'unevalled))))) > > Should these doc strings mention the special case of special form, > which each one of them treats differently? OK. Pushed, thanks, Stefan ^ permalink raw reply [flat|nested] 20+ messages in thread
* bug#69739: 30.0.50; `type-of` is not precise enough 2024-03-14 16:56 ` Stefan Monnier via Bug reports for GNU Emacs, the Swiss army knife of text editors 2024-03-15 9:42 ` Andrea Corallo 2024-03-15 9:51 ` Basil L. Contovounesios @ 2024-03-15 11:50 ` Eli Zaretskii 2 siblings, 0 replies; 20+ messages in thread From: Eli Zaretskii @ 2024-03-15 11:50 UTC (permalink / raw) To: Stefan Monnier; +Cc: 69739 > From: Stefan Monnier <monnier@iro.umontreal.ca> > Cc: 69739@debbugs.gnu.org > Date: Thu, 14 Mar 2024 12:56:03 -0400 > > OK, here's a new patch. > I ended up with `cl-type-of` as the name of the new function (after > all, it tries to better match the expected semantics of Common Lisp's > `type-of`). > > Comments? Objections? Should we document this new function in the ELisp manual? ^ permalink raw reply [flat|nested] 20+ messages in thread
end of thread, other threads:[~2024-03-18 13:33 UTC | newest] Thread overview: 20+ messages (download: mbox.gz follow: Atom feed -- links below jump to the message on this page -- 2024-03-11 23:19 bug#69739: 30.0.50; `type-of` is not precise enough Stefan Monnier via Bug reports for GNU Emacs, the Swiss army knife of text editors 2024-03-12 13:37 ` Stefan Monnier via Bug reports for GNU Emacs, the Swiss army knife of text editors 2024-03-12 14:40 ` Stefan Monnier via Bug reports for GNU Emacs, the Swiss army knife of text editors 2024-03-12 16:07 ` Rudolf Schlatte 2024-03-13 22:10 ` Stefan Monnier via Bug reports for GNU Emacs, the Swiss army knife of text editors 2024-03-12 13:58 ` Eli Zaretskii 2024-03-12 14:43 ` Stefan Monnier via Bug reports for GNU Emacs, the Swiss army knife of text editors 2024-03-12 15:02 ` Eli Zaretskii 2024-03-12 15:39 ` Stefan Monnier via Bug reports for GNU Emacs, the Swiss army knife of text editors 2024-03-13 11:55 ` Eli Zaretskii 2024-03-14 16:56 ` Stefan Monnier via Bug reports for GNU Emacs, the Swiss army knife of text editors 2024-03-15 9:42 ` Andrea Corallo 2024-03-15 10:54 ` Andrea Corallo 2024-03-15 9:51 ` Basil L. Contovounesios 2024-03-15 14:09 ` Stefan Monnier via Bug reports for GNU Emacs, the Swiss army knife of text editors 2024-03-15 15:18 ` Eli Zaretskii 2024-03-17 22:29 ` Stefan Monnier via Bug reports for GNU Emacs, the Swiss army knife of text editors 2024-03-18 12:56 ` Eli Zaretskii 2024-03-18 13:33 ` Stefan Monnier via Bug reports for GNU Emacs, the Swiss army knife of text editors 2024-03-15 11:50 ` Eli Zaretskii
Code repositories for project(s) associated with this public inbox https://git.savannah.gnu.org/cgit/emacs.git This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox; as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).