From 6bf61df8ab359f1371ab2e3e278bc8642d65a985 Mon Sep 17 00:00:00 2001 From: Stefan Monnier Date: Mon, 16 Feb 2015 01:37:57 -0500 Subject: * lisp/emacs-lisp/cl-macs.el (cl-defstruct): Keep type=nil by default. * lisp/emacs-lisp/cl-preloaded.el (cl-struct-define): Add sanity checks about relationship between `type', `named', and `slots'. * lisp/emacs-lisp/cl-generic.el (cl--generic-struct-tagcode): Adjust to new value of `cl-struct-type' property. --- lisp/emacs-lisp/cl-generic.el | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'lisp/emacs-lisp/cl-generic.el') diff --git a/lisp/emacs-lisp/cl-generic.el b/lisp/emacs-lisp/cl-generic.el index c4232863cfc..ccd5bec5685 100644 --- a/lisp/emacs-lisp/cl-generic.el +++ b/lisp/emacs-lisp/cl-generic.el @@ -731,7 +731,7 @@ Can only be used from within the lexical body of a primary or around method." (defun cl--generic-struct-tagcode (type name) (and (symbolp type) (get type 'cl-struct-type) - (or (eq 'vector (car (get type 'cl-struct-type))) + (or (null (car (get type 'cl-struct-type))) (error "Can't dispatch on cl-struct %S: type is %S" type (car (get type 'cl-struct-type)))) (or (equal '(cl-tag-slot) (car (get type 'cl-struct-slots))) @@ -761,7 +761,7 @@ Can only be used from within the lexical body of a primary or around method." (let ((types (list (intern (substring (symbol-name tag) 10))))) (while (get (car types) 'cl-struct-include) (push (get (car types) 'cl-struct-include) types)) - (push 'cl-struct types) ;The "parent type" of all cl-structs. + (push 'cl-structure-object types) ;The "parent type" of all cl-structs. (nreverse types)))) ;;; Dispatch on "system types". -- cgit v1.2.3 From e846bbf360d1bcee3a35dd05a57bc76cbb22a6f0 Mon Sep 17 00:00:00 2001 From: Stefan Monnier Date: Sun, 22 Feb 2015 23:50:03 -0500 Subject: * lisp/emacs-lisp/macroexp.el (macroexp-parse-body): Handle cl-declare and :documentation. Change return value format accordingly. * lisp/emacs-lisp/cl-generic.el (cl--generic-lambda): * lisp/emacs-lisp/pcase.el (pcase-lambda): Adjust accordingly. * lisp/emacs-lisp/cl-macs.el (cl--transform-lambda): Use macroexp-parse-body. --- lisp/ChangeLog | 8 ++++++++ lisp/emacs-lisp/cl-generic.el | 2 +- lisp/emacs-lisp/cl-macs.el | 35 ++++++++++++++++------------------- lisp/emacs-lisp/macroexp.el | 19 ++++++++++--------- lisp/emacs-lisp/pcase.el | 2 +- 5 files changed, 36 insertions(+), 30 deletions(-) (limited to 'lisp/emacs-lisp/cl-generic.el') diff --git a/lisp/ChangeLog b/lisp/ChangeLog index ced342baeb9..6352d77ca3a 100644 --- a/lisp/ChangeLog +++ b/lisp/ChangeLog @@ -1,3 +1,11 @@ +2015-02-23 Stefan Monnier + + * emacs-lisp/macroexp.el (macroexp-parse-body): Handle cl-declare + and :documentation. Change return value format accordingly. + * emacs-lisp/cl-generic.el (cl--generic-lambda): + * emacs-lisp/pcase.el (pcase-lambda): Adjust accordingly. + * emacs-lisp/cl-macs.el (cl--transform-lambda): Use macroexp-parse-body. + 2015-02-23 Dmitry Gutov Introduce `xref-etags-mode'. diff --git a/lisp/emacs-lisp/cl-generic.el b/lisp/emacs-lisp/cl-generic.el index ccd5bec5685..99924ba288f 100644 --- a/lisp/emacs-lisp/cl-generic.el +++ b/lisp/emacs-lisp/cl-generic.el @@ -278,7 +278,7 @@ This macro can only be used within the lexical scope of a cl-generic method." (uses-cnm (cl--generic-fgrep (list cnm nmp) nbody))) (cons (not (not uses-cnm)) `#'(lambda (,@(if uses-cnm (list cnm)) ,@args) - ,@(delq nil (car parsed-body)) + ,@(car parsed-body) ,(if (not (memq nmp uses-cnm)) nbody `(let ((,nmp (lambda () diff --git a/lisp/emacs-lisp/cl-macs.el b/lisp/emacs-lisp/cl-macs.el index c5f49b0ed91..c3da091fb00 100644 --- a/lisp/emacs-lisp/cl-macs.el +++ b/lisp/emacs-lisp/cl-macs.el @@ -234,10 +234,9 @@ FORM is of the form (ARGS . BODY)." (let* ((args (car form)) (body (cdr form)) (orig-args args) (cl--bind-block bind-block) (cl--bind-defs nil) (cl--bind-enquote nil) (cl--bind-lets nil) (cl--bind-forms nil) - (header nil) (simple-args nil)) - (while (or (stringp (car body)) - (memq (car-safe (car body)) '(interactive declare cl-declare))) - (push (pop body) header)) + (parsed-body (macroexp-parse-body body)) + (header (car parsed-body)) (simple-args nil)) + (setq body (cdr parsed-body)) (setq args (if (listp args) (cl-copy-list args) (list '&rest args))) (let ((p (last args))) (if (cdr p) (setcdr p (list '&rest (cdr p))))) (if (setq cl--bind-defs (cadr (memq '&cl-defs args))) @@ -258,7 +257,7 @@ FORM is of the form (ARGS . BODY)." (or (eq cl--bind-block 'cl-none) (setq body (list `(cl-block ,cl--bind-block ,@body)))) (if (null args) - (cl-list* nil (nreverse simple-args) (nconc (nreverse header) body)) + (cl-list* nil (nreverse simple-args) (nconc header body)) (if (memq '&optional simple-args) (push '&optional args)) (cl--do-arglist args nil (- (length simple-args) (if (memq '&optional simple-args) 1 0))) @@ -266,20 +265,18 @@ FORM is of the form (ARGS . BODY)." (cl-list* nil (nconc (nreverse simple-args) (list '&rest (car (pop cl--bind-lets)))) - (nconc (let ((hdr (nreverse header))) - ;; Macro expansion can take place in the middle of - ;; apparently harmless computation, so it should not - ;; touch the match-data. - (save-match-data - (require 'help-fns) - (cons (help-add-fundoc-usage - (if (stringp (car hdr)) (pop hdr)) - ;; Be careful with make-symbol and (back)quote, - ;; see bug#12884. - (let ((print-gensym nil) (print-quoted t)) - (format "%S" (cons 'fn (cl--make-usage-args - orig-args))))) - hdr))) + (nconc (save-match-data ;; Macro expansion can take place in the + ;; middle of apparently harmless computation, so it + ;; should not touch the match-data. + (require 'help-fns) + (cons (help-add-fundoc-usage + (if (stringp (car header)) (pop header)) + ;; Be careful with make-symbol and (back)quote, + ;; see bug#12884. + (let ((print-gensym nil) (print-quoted t)) + (format "%S" (cons 'fn (cl--make-usage-args + orig-args))))) + header)) (list `(let* ,cl--bind-lets ,@(nreverse cl--bind-forms) ,@body))))))) diff --git a/lisp/emacs-lisp/macroexp.el b/lisp/emacs-lisp/macroexp.el index b75c8cc50a7..68bf4f62c34 100644 --- a/lisp/emacs-lisp/macroexp.el +++ b/lisp/emacs-lisp/macroexp.el @@ -297,15 +297,16 @@ definitions to shadow the loaded ones for use in file byte-compilation." ;;; Handy functions to use in macros. -(defun macroexp-parse-body (exps) - "Parse EXPS into ((DOC DECLARE-FORM INTERACTIVE-FORM) . BODY)." - `((,(and (stringp (car exps)) - (pop exps)) - ,(and (eq (car-safe (car exps)) 'declare) - (pop exps)) - ,(and (eq (car-safe (car exps)) 'interactive) - (pop exps))) - ,@exps)) +(defun macroexp-parse-body (body) + "Parse a function BODY into (DECLARATIONS . EXPS)." + (let ((decls ())) + (while (and (cdr body) + (let ((e (car body))) + (or (stringp e) + (memq (car-safe e) + '(:documentation declare interactive cl-declare))))) + (push (pop body) decls)) + (cons (nreverse decls) body))) (defun macroexp-progn (exps) "Return an expression equivalent to `(progn ,@EXPS)." diff --git a/lisp/emacs-lisp/pcase.el b/lisp/emacs-lisp/pcase.el index 057b12894f9..4706be5e57c 100644 --- a/lisp/emacs-lisp/pcase.el +++ b/lisp/emacs-lisp/pcase.el @@ -180,7 +180,7 @@ like `(,a . ,(pred (< a))) or, with more checks: (when (eq nil (car (last pats 2))) (setq pats (append (butlast pats 2) (car (last pats))))) `(lambda (&rest ,args) - ,@(remq nil (car body)) + ,@(car body) (pcase ,args (,(list '\` pats) . ,(cdr body)))))) -- cgit v1.2.3 From 21c547863d5950a9d623d62ab743e92c0e1fd95f Mon Sep 17 00:00:00 2001 From: Stefan Monnier Date: Wed, 4 Mar 2015 20:04:57 -0500 Subject: Replace *-function vars with generic functions in cl-generic. * lisp/emacs-lisp/cl-generic.el (cl--generic-generalizer): New struct. (cl-generic-tagcode-function, cl-generic-tag-types-function): Remove. (cl--generic-t-generalizer): New const. (cl--generic-make-method): Rename from `cl--generic-method-make'. (cl--generic-make): Change calling convention. (cl--generic): Add `options' field. (cl-generic-function-options): New function. (cl-defgeneric): Rewrite handling of options. Add support for :method options and allow the use of a default body. (cl-generic-define): Save options in the corresponding new field. (cl-defmethod): Fix ordering of qualifiers. (cl-generic-define-method): Use cl-generic-generalizers. (cl--generic-get-dispatcher): Change calling convention, and change calling convention of the returned function as well so as to take the list of methods separately from the generic function object, so that it can receive the original generic function object. (cl--generic-make-next-function): New function, extracted from cl--generic-make-function. (cl--generic-make-function): Use it. (cl-generic-method-combination-function): Remove. (cl--generic-cyclic-definition): New error. (cl-generic-call-method): Take a generic function object rather than its name. (cl-method-qualifiers): New alias. (cl--generic-build-combined-method): Use cl-generic-combine-methods, don't segregate by qualifiers here any more. (cl--generic-standard-method-combination): Segregate by qualifiers here instead. Add support for the `:extra' qualifier. (cl--generic-cache-miss): Move earlier, adjust to new calling convention. (cl-generic-generalizers, cl-generic-combine-methods): New generic functions. (cl-no-next-method, cl-no-applicable-method, cl-no-primary-method): Use the new "default method in defgeneric" functionality, change calling convention to receive a generic function object. (cl--generic-head-used): New var. (cl--generic-head-generalizer, cl--generic-eql-generalizer) (cl--generic-struct-generalizer, cl--generic-typeof-generalizer): New consts. * lisp/emacs-lisp/eieio-core.el (eieio--generic-generalizer) (eieio--generic-subclass-generalizer): New consts. (cl-generic-generalizers): New methods. * lisp/emacs-lisp/eieio-compat.el (eieio--generic-static-symbol-generalizer) (eieio--generic-static-object-generalizer): New consts. (cl-generic-generalizers) <(head eieio--static)>: New method. * lisp/emacs-lisp/byte-opt.el (byte-optimize-form-code-walker): Unfold closures like lambdas. --- lisp/ChangeLog | 74 ++++- lisp/emacs-lisp/byte-opt.el | 2 +- lisp/emacs-lisp/cl-generic.el | 625 ++++++++++++++++++++++++---------------- lisp/emacs-lisp/eieio-compat.el | 56 ++-- lisp/emacs-lisp/eieio-core.el | 54 ++-- 5 files changed, 504 insertions(+), 307 deletions(-) (limited to 'lisp/emacs-lisp/cl-generic.el') diff --git a/lisp/ChangeLog b/lisp/ChangeLog index cdd4bf8557f..d4bc0af3eff 100644 --- a/lisp/ChangeLog +++ b/lisp/ChangeLog @@ -1,3 +1,53 @@ +2015-03-05 Stefan Monnier + + Replace *-function vars with generic functions in cl-generic. + * emacs-lisp/cl-generic.el (cl--generic-generalizer): New struct. + (cl-generic-tagcode-function, cl-generic-tag-types-function): Remove. + (cl--generic-t-generalizer): New const. + (cl--generic-make-method): Rename from `cl--generic-method-make'. + (cl--generic-make): Change calling convention. + (cl--generic): Add `options' field. + (cl-generic-function-options): New function. + (cl-defgeneric): Rewrite handling of options. Add support for :method + options and allow the use of a default body. + (cl-generic-define): Save options in the corresponding new field. + (cl-defmethod): Fix ordering of qualifiers. + (cl-generic-define-method): Use cl-generic-generalizers. + (cl--generic-get-dispatcher): Change calling convention, and change + calling convention of the returned function as well so as to take the + list of methods separately from the generic function object, so that it + can receive the original generic function object. + (cl--generic-make-next-function): New function, extracted from + cl--generic-make-function. + (cl--generic-make-function): Use it. + (cl-generic-method-combination-function): Remove. + (cl--generic-cyclic-definition): New error. + (cl-generic-call-method): Take a generic function object rather than + its name. + (cl-method-qualifiers): New alias. + (cl--generic-build-combined-method): Use cl-generic-combine-methods, + don't segregate by qualifiers here any more. + (cl--generic-standard-method-combination): Segregate by qualifiers + here instead. Add support for the `:extra' qualifier. + (cl--generic-cache-miss): Move earlier, adjust to new calling convention. + (cl-generic-generalizers, cl-generic-combine-methods): + New generic functions. + (cl-no-next-method, cl-no-applicable-method, cl-no-primary-method): + Use the new "default method in defgeneric" functionality, change + calling convention to receive a generic function object. + (cl--generic-head-used): New var. + (cl--generic-head-generalizer, cl--generic-eql-generalizer) + (cl--generic-struct-generalizer, cl--generic-typeof-generalizer): + New consts. + * emacs-lisp/eieio-core.el (eieio--generic-generalizer) + (eieio--generic-subclass-generalizer): New consts. + (cl-generic-generalizers): New methods. + * emacs-lisp/eieio-compat.el (eieio--generic-static-symbol-generalizer) + (eieio--generic-static-object-generalizer): New consts. + (cl-generic-generalizers) <(head eieio--static)>: New method. + * emacs-lisp/byte-opt.el (byte-optimize-form-code-walker): + Unfold closures like lambdas. + 2015-03-04 Filipp Gunbin * autorevert.el (auto-revert-notify-add-watch): @@ -142,8 +192,8 @@ 2015-03-03 Eli Zaretskii * frame.el (frame-notice-user-settings): Refresh the value of - frame parameters after calling tty-handle-reverse-video. Call - face-set-after-frame-default with the actual parameters, to avoid + frame parameters after calling tty-handle-reverse-video. + Call face-set-after-frame-default with the actual parameters, to avoid resetting colors back to unspecified. (set-background-color, set-foreground-color): Pass the foreground and background colors to face-set-after-frame-default. (Bug#19802) @@ -176,8 +226,8 @@ 2015-03-03 Eli Zaretskii - * textmodes/artist.el (artist-ellipse-compute-fill-info): Use - mapcar, not mapc, to create the other half of fill-info. + * textmodes/artist.el (artist-ellipse-compute-fill-info): + Use mapcar, not mapc, to create the other half of fill-info. (Bug#19763) 2015-03-03 Nicolas Petton @@ -323,8 +373,8 @@ Handle "#" operator properly inside macro. Fix coding bug. - * progmodes/cc-mode.el (c-neutralize-syntax-in-and-mark-CPP): On - finding a "#" which looks like the start of a macro, check it + * progmodes/cc-mode.el (c-neutralize-syntax-in-and-mark-CPP): + On finding a "#" which looks like the start of a macro, check it isn't already inside a macro. * progmodes/cc-engine.el (c-state-safe-place): Don't record a new @@ -364,15 +414,15 @@ 2015-02-25 Oleh Krehel - * emacs-lisp/check-declare.el (check-declare-warn): Use - compilation-style warnings. + * emacs-lisp/check-declare.el (check-declare-warn): + Use compilation-style warnings. (check-declare-files): Make sure that `check-declare-warning-buffer' is in `compilation-mode'. 2015-02-25 Oleh Krehel - * emacs-lisp/check-declare.el (check-declare-ext-errors): New - defcustom. + * emacs-lisp/check-declare.el (check-declare-ext-errors): + New defcustom. (check-declare): New defgroup. (check-declare-verify): When `check-declare-ext-errors' is non-nil, warn about an unfound function, instead of saying @@ -380,8 +430,8 @@ 2015-02-25 Tassilo Horn - * textmodes/reftex-vars.el (reftex-include-file-commands): Call - reftex-set-dirty on changes. + * textmodes/reftex-vars.el (reftex-include-file-commands): + Call reftex-set-dirty on changes. 2015-02-25 Stefan Monnier diff --git a/lisp/emacs-lisp/byte-opt.el b/lisp/emacs-lisp/byte-opt.el index 149c4723199..e149f80db8e 100644 --- a/lisp/emacs-lisp/byte-opt.el +++ b/lisp/emacs-lisp/byte-opt.el @@ -390,7 +390,7 @@ (and (nth 1 form) (not for-effect) form)) - ((eq 'lambda (car-safe fn)) + ((memq (car-safe fn) '(lambda closure)) (let ((newform (byte-compile-unfold-lambda form))) (if (eq newform form) ;; Some error occurred, avoid infinite recursion diff --git a/lisp/emacs-lisp/cl-generic.el b/lisp/emacs-lisp/cl-generic.el index 99924ba288f..a8483ea1355 100644 --- a/lisp/emacs-lisp/cl-generic.el +++ b/lisp/emacs-lisp/cl-generic.el @@ -31,37 +31,51 @@ ;; from a significant problem: the method-combination code returns a sexp ;; that needs to be `eval'uated or compiled. IOW it requires run-time ;; code generation. Given how rarely method-combinations are used, -;; I just provided a cl-generic-method-combination-function, which -;; people can use if they are really desperate for such functionality. +;; I just provided a cl-generic-combine-methods generic function, to which +;; people can add methods if they are really desperate for such functionality. ;; - In defgeneric we don't support the options: -;; declare, :method-combination, :generic-function-class, :method-class, -;; :method. +;; declare, :method-combination, :generic-function-class, :method-class. ;; Added elements: ;; - We support aliases to generic functions. -;; - The kind of thing on which to dispatch can be extended. -;; There is support in this file for dispatch on: +;; - cl-generic-generalizers. This generic function lets you extend the kind +;; of thing on which to dispatch. There is support in this file for +;; dispatch on: ;; - (eql ) +;; - (head ) which checks that the arg is a cons with as its head. ;; - plain old types ;; - type of CL structs ;; eieio-core adds dispatch on: ;; - class of eieio objects ;; - actual class argument, using the syntax (subclass ). -;; - cl-generic-method-combination-function (i.s.o define-method-combination). +;; - cl-generic-combine-methods (i.s.o define-method-combination and +;; compute-effective-method). ;; - cl-generic-call-method (which replaces make-method and call-method). +;; - The standard method combination supports ":extra STRING" qualifiers +;; which simply allows adding more methods for the same +;; specializers&qualifiers. ;; Efficiency considerations: overall, I've made an effort to make this fairly ;; efficient for the expected case (e.g. no constant redefinition of methods). ;; - Generic functions which do not dispatch on any argument are implemented ;; optimally (just as efficient as plain old functions). ;; - Generic functions which only dispatch on one argument are fairly efficient -;; (not a lot of room for improvement, I think). +;; (not a lot of room for improvement without changes to the byte-compiler, +;; I think). ;; - Multiple dispatch is implemented rather naively. There's an extra `apply' ;; function call for every dispatch; we don't optimize each dispatch ;; based on the set of candidate methods remaining; we don't optimize the -;; order in which we performs the dispatches either; If/when this -;; becomes a problem, we can try and optimize it. +;; order in which we performs the dispatches either; +;; If/when this becomes a problem, we can try and optimize it. ;; - call-next-method could be made more efficient, but isn't too terrible. +;; TODO: +;; +;; - A generic "filter" generalizer (e.g. could be used to cleanly adds methods +;; to cl-generic-combine-methods with a specializer that says it applies only +;; when some particular qualifier is used). +;; - A way to dispatch on the context (e.g. the major-mode, some global +;; variable, you name it). + ;;; Code: ;; Note: For generic functions that dispatch on several arguments (i.e. those @@ -70,40 +84,24 @@ ;; often suboptimal since after one dispatch, the remaining dispatches can ;; usually be simplified, or even completely skipped. -;; TODO/FIXME: -;; - WIBNI we could use something like -;; (add-function :before (cl-method-function (cl-find-method ...)) ...) - (eval-when-compile (require 'cl-lib)) (eval-when-compile (require 'pcase)) -(defvar cl-generic-tagcode-function - (lambda (type _name) - (if (eq type t) '(0 . 'cl--generic-type) - (error "Unknown specializer %S" type))) - "Function to get the Elisp code to extract the tag on which we dispatch. -Takes a \"parameter-specializer-name\" and a variable name, and returns -a pair (PRIORITY . CODE) where CODE is an Elisp expression that should be -used to extract the \"tag\" (from the object held in the named variable) -that should uniquely determine if we have a match -\(i.e. the \"tag\" is the value that will be used to dispatch to the proper -method(s)). -Such \"tagcodes\" will be or'd together. -PRIORITY is an integer from 0 to 100 which is used to sort the tagcodes -in the `or'. The higher the priority, the more specific the tag should be. -More specifically, if PRIORITY is N and we have two objects X and Y -whose tag (according to TAGCODE) is `eql', then it should be the case -that for all other (PRIORITY . TAGCODE) where PRIORITY ≤ N, then -\(eval TAGCODE) for X is `eql' to (eval TAGCODE) for Y.") - -(defvar cl-generic-tag-types-function - (lambda (tag) (if (eq tag 'cl--generic-type) '(t))) - "Function to get the list of types that a given \"tag\" matches. -They should be sorted from most specific to least specific.") +(cl-defstruct (cl--generic-generalizer + (:constructor nil) + (:constructor cl-generic-make-generalizer + (priority tagcode-function specializers-function))) + (priority nil :type integer) + tagcode-function + specializers-function) + +(defconst cl--generic-t-generalizer + (cl-generic-make-generalizer + 0 (lambda (_name) nil) (lambda (_tag) '(t)))) (cl-defstruct (cl--generic-method (:constructor nil) - (:constructor cl--generic-method-make + (:constructor cl--generic-make-method (specializers qualifiers uses-cnm function)) (:predicate nil)) (specializers nil :read-only t :type list) @@ -115,8 +113,7 @@ They should be sorted from most specific to least specific.") (cl-defstruct (cl--generic (:constructor nil) - (:constructor cl--generic-make - (name &optional dispatches method-table)) + (:constructor cl--generic-make (name)) (:predicate nil)) (name nil :type symbol :read-only t) ;Pointer back to the symbol. ;; `dispatches' holds a list of (ARGNUM . TAGCODES) where ARGNUM is the index @@ -125,8 +122,13 @@ They should be sorted from most specific to least specific.") ;; on which to dispatch and PRIORITY is the priority of each expression to ;; decide in which order to sort them. ;; The most important dispatch is last in the list (and the least is first). - (dispatches nil :type (list-of (cons natnum (list-of tagcode)))) - (method-table nil :type (list-of cl--generic-method))) + (dispatches nil :type (list-of (cons natnum (list-of generalizers)))) + (method-table nil :type (list-of cl--generic-method)) + (options nil :type list)) + +(defun cl-generic-function-options (generic) + "Return the options of the generic function GENERIC." + (cl--generic-options generic)) (defmacro cl--generic (name) `(get ,name 'cl--generic)) @@ -170,20 +172,34 @@ is appropriate to use. Specific methods are defined with `cl-defmethod'. With this implementation the ARGS are currently ignored. OPTIONS-AND-METHODS currently understands: - (:documentation DOCSTRING) -- (declare DECLARATIONS)" +- (declare DECLARATIONS) +- (:argument-precedence-order &rest ARGS) +- (:method [QUALIFIERS...] ARGS &rest BODY) +BODY, if present, is used as the body of a default method. + +\(fn NAME ARGS [DOC-STRING] [OPTIONS-AND-METHODS...] &rest BODY)" (declare (indent 2) (doc-string 3)) - (let* ((docprop (assq :documentation options-and-methods)) - (doc (cond ((stringp (car-safe options-and-methods)) - (pop options-and-methods)) - (docprop - (prog1 - (cadr docprop) - (setq options-and-methods - (delq docprop options-and-methods)))))) - (declarations (assq 'declare options-and-methods))) - (when declarations - (setq options-and-methods - (delq declarations options-and-methods))) + (let* ((doc (if (stringp (car-safe options-and-methods)) + (pop options-and-methods))) + (declarations nil) + (methods ()) + (options ()) + next-head) + (while (progn (setq next-head (car-safe (car options-and-methods))) + (or (keywordp next-head) + (eq next-head 'declare))) + (pcase next-head + (`:documentation + (when doc (error "Multiple doc strings for %S" name)) + (setq doc (cadr (pop options-and-methods)))) + (`declare + (when declarations (error "Multiple `declare' for %S" name)) + (setq declarations (pop options-and-methods))) + (`:method (push (cdr (pop options-and-methods)) methods)) + (_ (push (pop options-and-methods) options)))) + (when options-and-methods + ;; Anything remaining is assumed to be a default method body. + (push `(,args ,@options-and-methods) methods)) `(progn ,(when (eq 'setf (car-safe name)) (pcase-let ((`(,setter . ,code) (cl--generic-setf-rewrite @@ -200,8 +216,10 @@ OPTIONS-AND-METHODS currently understands: nil)))) (cdr declarations)) (defalias ',name - (cl-generic-define ',name ',args ',options-and-methods) - ,(help-add-fundoc-usage doc args))))) + (cl-generic-define ',name ',args ',(nreverse options)) + ,(help-add-fundoc-usage doc args)) + ,@(mapcar (lambda (method) `(cl-defmethod ,name ,@method)) + (nreverse methods))))) (defun cl--generic-mandatory-args (args) (let ((res ())) @@ -210,10 +228,10 @@ OPTIONS-AND-METHODS currently understands: (nreverse res))) ;;;###autoload -(defun cl-generic-define (name args options-and-methods) +(defun cl-generic-define (name args options) (let ((generic (cl-generic-ensure-function name)) (mandatory (cl--generic-mandatory-args args)) - (apo (assq :argument-precedence-order options-and-methods))) + (apo (assq :argument-precedence-order options))) (setf (cl--generic-dispatches generic) nil) (when apo (dolist (arg (cdr apo)) @@ -222,6 +240,7 @@ OPTIONS-AND-METHODS currently understands: (push (list (- (length mandatory) (length pos))) (cl--generic-dispatches generic))))) (setf (cl--generic-method-table generic) nil) + (setf (cl--generic-options generic) options) (cl--generic-make-function generic))) (defmacro cl-generic-current-method-specializers () @@ -341,7 +360,7 @@ which case this method will be invoked when the argument is `eql' to VAL. ;; But in practice, it's common to use `cl-defmethod' ;; without a previous `cl-defgeneric'. (declare-function ,name "") - (cl-generic-define-method ',name ',qualifiers ',args + (cl-generic-define-method ',name ',(nreverse qualifiers) ',args ,uses-cnm ,fun))))) (defun cl--generic-member-method (specializers qualifiers methods) @@ -359,28 +378,33 @@ which case this method will be invoked when the argument is `eql' to VAL. (mandatory (cl--generic-mandatory-args args)) (specializers (mapcar (lambda (arg) (if (consp arg) (cadr arg) t)) mandatory)) - (method (cl--generic-method-make + (method (cl--generic-make-method specializers qualifiers uses-cnm function)) (mt (cl--generic-method-table generic)) (me (cl--generic-member-method specializers qualifiers mt)) (dispatches (cl--generic-dispatches generic)) (i 0)) (dolist (specializer specializers) - (let* ((tagcode (funcall cl-generic-tagcode-function specializer 'arg)) + (let* ((generalizers (cl-generic-generalizers specializer)) (x (assq i dispatches))) (unless x - (setq x (list i (funcall cl-generic-tagcode-function t 'arg))) + (setq x (cons i (cl-generic-generalizers t))) (setf (cl--generic-dispatches generic) (setq dispatches (cons x dispatches)))) - (unless (member tagcode (cdr x)) - (setf (cdr x) - (nreverse (sort (cons tagcode (cdr x)) - #'car-less-than-car)))) + (dolist (generalizer generalizers) + (unless (member generalizer (cdr x)) + (setf (cdr x) + (sort (cons generalizer (cdr x)) + (lambda (x y) + (> (cl--generic-generalizer-priority x) + (cl--generic-generalizer-priority y))))))) (setq i (1+ i)))) (if me (setcar me method) (setf (cl--generic-method-table generic) (cons method mt))) (cl-pushnew `(cl-defmethod . (,(cl--generic-name generic) . ,specializers)) current-load-list :test #'equal) + ;; FIXME: Try to avoid re-constructing a new function if the old one + ;; is still valid (e.g. still empty method cache)? (let ((gfun (cl--generic-make-function generic)) ;; Prevent `defalias' from recording this as the definition site of ;; the generic function. @@ -399,62 +423,73 @@ which case this method will be invoked when the argument is `eql' to VAL. (defvar cl--generic-dispatchers (make-hash-table :test #'equal)) -(defun cl--generic-get-dispatcher (tagcodes dispatch-arg) +(defun cl--generic-get-dispatcher (dispatch) (cl--generic-with-memoization - (gethash (cons dispatch-arg tagcodes) cl--generic-dispatchers) - (let ((lexical-binding t) - (tag-exp `(or ,@(mapcar #'cdr - ;; Minor optimization: since this tag-exp is - ;; only used to lookup the method-cache, it - ;; doesn't matter if the default value is some - ;; constant or nil. - (if (macroexp-const-p (car (last tagcodes))) - (butlast tagcodes) - tagcodes)))) - (extraargs ())) + (gethash dispatch cl--generic-dispatchers) + (let* ((dispatch-arg (car dispatch)) + (generalizers (cdr dispatch)) + (lexical-binding t) + (tagcodes + (mapcar (lambda (generalizer) + (funcall (cl--generic-generalizer-tagcode-function + generalizer) + 'arg)) + generalizers)) + (typescodes + (mapcar (lambda (generalizer) + `(funcall ',(cl--generic-generalizer-specializers-function + generalizer) + ,(funcall (cl--generic-generalizer-tagcode-function + generalizer) + 'arg))) + generalizers)) + (tag-exp + ;; Minor optimization: since this tag-exp is + ;; only used to lookup the method-cache, it + ;; doesn't matter if the default value is some + ;; constant or nil. + `(or ,@(if (macroexp-const-p (car (last tagcodes))) + (butlast tagcodes) + tagcodes))) + (extraargs ())) (dotimes (_ dispatch-arg) (push (make-symbol "arg") extraargs)) + ;; FIXME: For generic functions with a single method (or with 2 methods, + ;; one of which always matches), using a tagcode + hash-table is + ;; overkill: better just use a `cl-typep' test. (byte-compile - `(lambda (generic dispatches-left) + `(lambda (generic dispatches-left methods) (let ((method-cache (make-hash-table :test #'eql))) (lambda (,@extraargs arg &rest args) (apply (cl--generic-with-memoization (gethash ,tag-exp method-cache) (cl--generic-cache-miss - generic ',dispatch-arg dispatches-left - (list ,@(mapcar #'cdr tagcodes)))) + generic ',dispatch-arg dispatches-left methods + ,(if (cdr typescodes) + `(append ,@typescodes) (car typescodes)))) ,@extraargs arg args)))))))) (defun cl--generic-make-function (generic) - (let* ((dispatches (cl--generic-dispatches generic)) - (dispatch + (cl--generic-make-next-function generic + (cl--generic-dispatches generic) + (cl--generic-method-table generic))) + +(defun cl--generic-make-next-function (generic dispatches methods) + (let* ((dispatch (progn (while (and dispatches - (member (cdar dispatches) - '(nil ((0 . 'cl--generic-type))))) + (let ((x (nth 1 (car dispatches)))) + ;; No need to dispatch for `t' specializers. + (or (null x) (equal x cl--generic-t-generalizer)))) (setq dispatches (cdr dispatches))) (pop dispatches)))) - (if (null dispatch) - (cl--generic-build-combined-method - (cl--generic-name generic) - (cl--generic-method-table generic)) - (let ((dispatcher (cl--generic-get-dispatcher - (cdr dispatch) (car dispatch)))) - (funcall dispatcher generic dispatches))))) - -(defvar cl-generic-method-combination-function - #'cl--generic-standard-method-combination - "Function to build the effective method. -Called with 2 arguments: NAME and METHOD-ALIST. -It should return an effective method, i.e. a function that expects the same -arguments as the methods, and calls those methods in some appropriate order. -NAME is the name (a symbol) of the corresponding generic function. -METHOD-ALIST is a list of elements (QUALIFIERS . METHODS) where -QUALIFIERS is a list of qualifiers, and METHODS is a list of the selected -methods for that qualifier list. -The METHODS lists are sorted from most generic first to most specific last. -The function can use `cl-generic-call-method' to create functions that call those -methods.") + (if (not (and dispatch + ;; If there's no method left, there's no point checking + ;; further arguments. + methods)) + (cl--generic-build-combined-method generic methods) + (let ((dispatcher (cl--generic-get-dispatcher dispatch))) + (funcall dispatcher generic dispatches methods))))) (defvar cl--generic-combined-method-memoization (make-hash-table :test #'equal :weakness 'value) @@ -463,27 +498,37 @@ This is particularly useful when many different tags select the same set of methods, since this table then allows us to share a single combined-method for all those different tags in the method-cache.") -(defun cl--generic-build-combined-method (generic-name methods) - (cl--generic-with-memoization - (gethash (cons generic-name methods) - cl--generic-combined-method-memoization) - (let ((mets-by-qual ())) - (dolist (method methods) - (let* ((qualifiers (cl--generic-method-qualifiers method)) - (x (assoc qualifiers mets-by-qual))) - ;; FIXME: sadly, alist-get only uses `assq' and we need `assoc'. - ;;(push (cdr qm) (alist-get qualifiers mets-by-qual))) - (if x - (push method (cdr x)) - (push (list qualifiers method) mets-by-qual)))) - (funcall cl-generic-method-combination-function - generic-name mets-by-qual)))) +(define-error 'cl--generic-cyclic-definition "Cyclic definition: %S") + +(defun cl--generic-build-combined-method (generic methods) + (if (null methods) + ;; Special case needed to fix a circularity during bootstrap. + (cl--generic-standard-method-combination generic methods) + (let ((f + (cl--generic-with-memoization + ;; FIXME: Since the fields of `generic' are modified, this + ;; hash-table won't work right, because the hashes will change! + ;; It's not terribly serious, but reduces the effectiveness of + ;; the table. + (gethash (cons generic methods) + cl--generic-combined-method-memoization) + (puthash (cons generic methods) :cl--generic--under-construction + cl--generic-combined-method-memoization) + (condition-case nil + (cl-generic-combine-methods generic methods) + ;; Special case needed to fix a circularity during bootstrap. + (cl--generic-cyclic-definition + (cl--generic-standard-method-combination generic methods)))))) + (if (eq f :cl--generic--under-construction) + (signal 'cl--generic-cyclic-definition + (list (cl--generic-name generic))) + f)))) (defun cl--generic-no-next-method-function (generic method) (lambda (&rest args) (apply #'cl-no-next-method generic method args))) -(defun cl-generic-call-method (generic-name method &optional fun) +(defun cl-generic-call-method (generic method &optional fun) "Return a function that calls METHOD. FUN is the function that should be called when METHOD calls `call-next-method'." @@ -491,7 +536,7 @@ FUN is the function that should be called when METHOD calls (cl--generic-method-function method) (let ((met-fun (cl--generic-method-function method)) (next (or fun (cl--generic-no-next-method-function - generic-name method)))) + generic method)))) (lambda (&rest args) (apply met-fun ;; FIXME: This sucks: passing just `next' would @@ -503,42 +548,122 @@ FUN is the function that should be called when METHOD calls (apply next (or cnm-args args))) args))))) -(defun cl--generic-standard-method-combination (generic-name mets-by-qual) - (dolist (x mets-by-qual) - (unless (member (car x) '(() (:after) (:before) (:around))) - (error "Unsupported qualifiers in function %S: %S" generic-name (car x)))) - (cond - ((null mets-by-qual) - (lambda (&rest args) - (apply #'cl-no-applicable-method generic-name args))) - ((null (alist-get nil mets-by-qual)) - (lambda (&rest args) - (apply #'cl-no-primary-method generic-name args))) - (t - (let* ((fun nil) - (ab-call (lambda (m) (cl-generic-call-method generic-name m))) - (before - (mapcar ab-call (reverse (cdr (assoc '(:before) mets-by-qual))))) - (after (mapcar ab-call (cdr (assoc '(:after) mets-by-qual))))) - (dolist (method (cdr (assoc nil mets-by-qual))) - (setq fun (cl-generic-call-method generic-name method fun))) - (when (or after before) - (let ((next fun)) - (setq fun (lambda (&rest args) - (dolist (bf before) - (apply bf args)) - (prog1 - (apply next args) - (dolist (af after) - (apply af args))))))) - (dolist (method (cdr (assoc '(:around) mets-by-qual))) - (setq fun (cl-generic-call-method generic-name method fun))) - fun)))) +;; Standard CLOS name. +(defalias 'cl-method-qualifiers #'cl--generic-method-qualifiers) + +(defun cl--generic-standard-method-combination (generic methods) + (let ((mets-by-qual ())) + (dolist (method methods) + (let ((qualifiers (cl-method-qualifiers method))) + (if (eq (car qualifiers) :extra) (setq qualifiers (cddr qualifiers))) + (unless (member qualifiers '(() (:after) (:before) (:around))) + (error "Unsupported qualifiers in function %S: %S" + (cl--generic-name generic) qualifiers)) + (push method (alist-get (car qualifiers) mets-by-qual)))) + (cond + ((null mets-by-qual) + (lambda (&rest args) + (apply #'cl-no-applicable-method generic args))) + ((null (alist-get nil mets-by-qual)) + (lambda (&rest args) + (apply #'cl-no-primary-method generic args))) + (t + (let* ((fun nil) + (ab-call (lambda (m) (cl-generic-call-method generic m))) + (before + (mapcar ab-call (reverse (cdr (assoc :before mets-by-qual))))) + (after (mapcar ab-call (cdr (assoc :after mets-by-qual))))) + (dolist (method (cdr (assoc nil mets-by-qual))) + (setq fun (cl-generic-call-method generic method fun))) + (when (or after before) + (let ((next fun)) + (setq fun (lambda (&rest args) + (dolist (bf before) + (apply bf args)) + (prog1 + (apply next args) + (dolist (af after) + (apply af args))))))) + (dolist (method (cdr (assoc :around mets-by-qual))) + (setq fun (cl-generic-call-method generic method fun))) + fun))))) + +(defun cl--generic-cache-miss (generic + dispatch-arg dispatches-left methods-left types) + (let ((methods '())) + (dolist (method methods-left) + (let* ((specializer (or (nth dispatch-arg + (cl--generic-method-specializers method)) + t)) + (m (member specializer types))) + (when m + (push (cons (length m) method) methods)))) + ;; Sort the methods, most specific first. + ;; It would be tempting to sort them once and for all in the method-table + ;; rather than here, but the order might depend on the actual argument + ;; (e.g. for multiple inheritance with defclass). + (setq methods (nreverse (mapcar #'cdr (sort methods #'car-less-than-car)))) + (cl--generic-make-next-function generic dispatches-left methods))) + +(cl-defgeneric cl-generic-generalizers (specializer) + "Return a list of generalizers for a given SPECIALIZER. +To each kind of `specializer', corresponds a `generalizer' which describes +how to extract a \"tag\" from an object which will then let us check if this +object matches the specializer. A typical example of a \"tag\" would be the +type of an object. It's called a `generalizer' because it +takes a specific object and returns a more general approximation, +denoting a set of objects to which it belongs. +A generalizer gives us the chunk of code which the +dispatch function needs to use to extract the \"tag\" of an object, as well +as a function which turns this tag into an ordered list of +`specializers' that this object matches. +The code which extracts the tag should be as fast as possible. +The tags should be chosen according to the following rules: +- The tags should not be too specific: similar objects which match the + same list of specializers should ideally use the same (`eql') tag. + This insures that the cached computation of the applicable + methods for one object can be reused for other objects. +- Corollary: objects which don't match any of the relevant specializers + should ideally all use the same tag (typically nil). + This insures that this cache does not grow unnecessarily large. +- Two different generalizers G1 and G2 should not use the same tag + unless they use it for the same set of objects. IOW, if G1.tag(X1) = + G2.tag(X2) then G1.tag(X1) = G2.tag(X1) = G1.tag(X2) = G2.tag(X2). +- If G1.priority > G2.priority and G1.tag(X1) = G1.tag(X2) and this tag is + non-nil, then you have to make sure that the G2.tag(X1) = G2.tag(X2). + This is because the method-cache is only indexed with the first non-nil + tag (by order of decreasing priority).") + + +(cl-defgeneric cl-generic-combine-methods (generic methods) + "Build the effective method made of METHODS. +It should return a function that expects the same arguments as the methods, and + calls those methods in some appropriate order. +GENERIC is the generic function (mostly used for its name). +METHODS is the list of the selected methods. +The METHODS list is sorted from most specific first to most generic last. +The function can use `cl-generic-call-method' to create functions that call those +methods.") + +;; Temporary definition to let the next defmethod succeed. +(fset 'cl-generic-generalizers + (lambda (_specializer) (list cl--generic-t-generalizer))) +(fset 'cl-generic-combine-methods + #'cl--generic-standard-method-combination) + +(cl-defmethod cl-generic-generalizers (specializer) + "Support for the catch-all `t' specializer." + (if (eq specializer t) (list cl--generic-t-generalizer) + (error "Unknown specializer %S" specializer))) + +(cl-defmethod cl-generic-combine-methods (generic methods) + "Standard support for :after, :before, :around, and `:extra NAME' qualifiers." + (cl--generic-standard-method-combination generic methods)) (defconst cl--generic-nnm-sample (cl--generic-no-next-method-function t t)) (defconst cl--generic-cnm-sample (funcall (cl--generic-build-combined-method - nil (list (cl--generic-method-make () () t #'identity))))) + nil (list (cl--generic-make-method () () t #'identity))))) (defun cl--generic-isnot-nnm-p (cnm) "Return non-nil if CNM is the function that calls `cl-no-next-method'." @@ -566,24 +691,6 @@ FUN is the function that should be called when METHOD calls (setq cnm-env (cdr cnm-env))))) (error "Haven't found no-next-method-sample in cnm-sample"))) -(defun cl--generic-cache-miss (generic dispatch-arg dispatches-left tags) - (let ((types (apply #'append (mapcar cl-generic-tag-types-function tags))) - (methods '())) - (dolist (method (cl--generic-method-table generic)) - (let* ((specializer (or (nth dispatch-arg - (cl--generic-method-specializers method)) - t)) - (m (member specializer types))) - (when m - (push (cons (length m) method) methods)))) - ;; Sort the methods, most specific first. - ;; It would be tempting to sort them once and for all in the method-table - ;; rather than here, but the order might depend on the actual argument - ;; (e.g. for multiple inheritance with defclass). - (setq methods (nreverse (mapcar #'cdr (sort methods #'car-less-than-car)))) - (cl--generic-make-function (cl--generic-make (cl--generic-name generic) - dispatches-left methods)))) - ;;; Define some pre-defined generic functions, used internally. (define-error 'cl-no-method "No method for %S") @@ -593,19 +700,16 @@ FUN is the function that should be called when METHOD calls 'cl-no-method) (cl-defgeneric cl-no-next-method (generic method &rest args) - "Function called when `cl-call-next-method' finds no next method.") -(cl-defmethod cl-no-next-method (generic method &rest args) - (signal 'cl-no-next-method `(,generic ,method ,@args))) + "Function called when `cl-call-next-method' finds no next method." + (signal 'cl-no-next-method `(,(cl--generic-name generic) ,method ,@args))) (cl-defgeneric cl-no-applicable-method (generic &rest args) - "Function called when a method call finds no applicable method.") -(cl-defmethod cl-no-applicable-method (generic &rest args) - (signal 'cl-no-applicable-method `(,generic ,@args))) + "Function called when a method call finds no applicable method." + (signal 'cl-no-applicable-method `(,(cl--generic-name generic) ,@args))) (cl-defgeneric cl-no-primary-method (generic &rest args) - "Function called when a method call finds no primary method.") -(cl-defmethod cl-no-primary-method (generic &rest args) - (signal 'cl-no-primary-method `(,generic ,@args))) + "Function called when a method call finds no primary method." + (signal 'cl-no-primary-method `(,(cl--generic-name generic) ,@args))) (defun cl-call-next-method (&rest _args) "Function to call the next applicable method. @@ -700,27 +804,57 @@ Can only be used from within the lexical body of a primary or around method." (insert "'.\n"))) (insert "\n" (or (nth 2 info) "Undocumented") "\n\n"))))))) +;;; Support for (head ) specializers. + +;; For both the `eql' and the `head' specializers, the dispatch +;; is unsatisfactory. Basically, in the "common&fast case", we end up doing +;; +;; (let ((tag (gethash value ))) +;; (funcall (gethash tag ))) +;; +;; whereas we'd like to just do +;; +;; (funcall (gethash value ))) +;; +;; but the problem is that the method-cache is normally "open ended", so +;; a nil means "not computed yet" and if we bump into it, we dutifully fill the +;; corresponding entry, whereas we'd want to just fallback on some default +;; effective method (so as not to fill the cache with lots of redundant +;; entries). + +(defvar cl--generic-head-used (make-hash-table :test #'eql)) + +(defconst cl--generic-head-generalizer + (cl-generic-make-generalizer + 80 (lambda (name) `(gethash (car-safe ,name) cl--generic-head-used)) + (lambda (tag) (if (eq (car-safe tag) 'head) (list tag))))) + +(cl-defmethod cl-generic-generalizers :extra "head" (specializer) + "Support for the `(head VAL)' specializers." + ;; We have to implement `head' here using the :extra qualifier, + ;; since we can't use the `head' specializer to implement itself. + (if (not (eq (car-safe specializer) 'head)) + (cl-call-next-method) + (cl--generic-with-memoization + (gethash (cadr specializer) cl--generic-head-used) specializer) + (list cl--generic-head-generalizer))) + ;;; Support for (eql ) specializers. (defvar cl--generic-eql-used (make-hash-table :test #'eql)) -(add-function :before-until cl-generic-tagcode-function - #'cl--generic-eql-tagcode) -(defun cl--generic-eql-tagcode (type name) - (when (eq (car-safe type) 'eql) - (puthash (cadr type) type cl--generic-eql-used) - `(100 . (gethash ,name cl--generic-eql-used)))) +(defconst cl--generic-eql-generalizer + (cl-generic-make-generalizer + 100 (lambda (name) `(gethash ,name cl--generic-eql-used)) + (lambda (tag) (if (eq (car-safe tag) 'eql) (list tag))))) -(add-function :before-until cl-generic-tag-types-function - #'cl--generic-eql-tag-types) -(defun cl--generic-eql-tag-types (tag) - (if (eq (car-safe tag) 'eql) (list tag))) +(cl-defmethod cl-generic-generalizers ((specializer (head eql))) + "Support for the `(eql VAL)' specializers." + (puthash (cadr specializer) specializer cl--generic-eql-used) + (list cl--generic-eql-generalizer)) ;;; Support for cl-defstructs specializers. -(add-function :before-until cl-generic-tagcode-function - #'cl--generic-struct-tagcode) - (defun cl--generic-struct-tag (name) `(and (vectorp ,name) (> (length ,name) 0) @@ -728,41 +862,46 @@ Can only be used from within the lexical body of a primary or around method." (if (eq (symbol-function tag) :quick-object-witness-check) tag)))) -(defun cl--generic-struct-tagcode (type name) - (and (symbolp type) - (get type 'cl-struct-type) - (or (null (car (get type 'cl-struct-type))) - (error "Can't dispatch on cl-struct %S: type is %S" - type (car (get type 'cl-struct-type)))) - (or (equal '(cl-tag-slot) (car (get type 'cl-struct-slots))) - (error "Can't dispatch on cl-struct %S: no tag in slot 0" - type)) - ;; It's tempting to use (and (vectorp ,name) (aref ,name 0)) - ;; but that would suffer from some problems: - ;; - the vector may have size 0. - ;; - when called on an actual vector (rather than an object), we'd - ;; end up returning an arbitrary value, possibly colliding with - ;; other tagcode's values. - ;; - it can also result in returning all kinds of irrelevant - ;; values which would end up filling up the method-cache with - ;; lots of irrelevant/redundant entries. - ;; FIXME: We could speed this up by introducing a dedicated - ;; vector type at the C level, so we could do something like - ;; (and (vector-objectp ,name) (aref ,name 0)) - `(50 . ,(cl--generic-struct-tag name)))) - -(add-function :before-until cl-generic-tag-types-function - #'cl--generic-struct-tag-types) -(defun cl--generic-struct-tag-types (tag) - ;; FIXME: cl-defstruct doesn't make it easy for us. +(defun cl--generic-struct-specializers (tag) (and (symbolp tag) ;; A method call shouldn't itself mess with the match-data. (string-match-p "\\`cl-struct-\\(.*\\)" (symbol-name tag)) (let ((types (list (intern (substring (symbol-name tag) 10))))) - (while (get (car types) 'cl-struct-include) - (push (get (car types) 'cl-struct-include) types)) - (push 'cl-structure-object types) ;The "parent type" of all cl-structs. - (nreverse types)))) + (while (get (car types) 'cl-struct-include) + (push (get (car types) 'cl-struct-include) types)) + (push 'cl-structure-object types) ;The "parent type" of all cl-structs. + (nreverse types)))) + +(defconst cl--generic-struct-generalizer + (cl-generic-make-generalizer + 50 #'cl--generic-struct-tag + #'cl--generic-struct-specializers)) + +(cl-defmethod cl-generic-generalizers :extra "cl-struct" (type) + "Support for dispatch on cl-struct types." + (or + (and (symbolp type) + (get type 'cl-struct-type) + (or (null (car (get type 'cl-struct-type))) + (error "Can't dispatch on cl-struct %S: type is %S" + type (car (get type 'cl-struct-type)))) + (or (equal '(cl-tag-slot) (car (get type 'cl-struct-slots))) + (error "Can't dispatch on cl-struct %S: no tag in slot 0" + type)) + ;; It's tempting to use (and (vectorp ,name) (aref ,name 0)) + ;; but that would suffer from some problems: + ;; - the vector may have size 0. + ;; - when called on an actual vector (rather than an object), we'd + ;; end up returning an arbitrary value, possibly colliding with + ;; other tagcode's values. + ;; - it can also result in returning all kinds of irrelevant + ;; values which would end up filling up the method-cache with + ;; lots of irrelevant/redundant entries. + ;; FIXME: We could speed this up by introducing a dedicated + ;; vector type at the C level, so we could do something like + ;; (and (vector-objectp ,name) (aref ,name 0)) + (list cl--generic-struct-generalizer)) + (cl-call-next-method))) ;;; Dispatch on "system types". @@ -784,23 +923,23 @@ Can only be used from within the lexical body of a primary or around method." (sequence) (number))) -(add-function :before-until cl-generic-tagcode-function - #'cl--generic-typeof-tagcode) -(defun cl--generic-typeof-tagcode (type name) +(defconst cl--generic-typeof-generalizer + (cl-generic-make-generalizer + ;; FIXME: We could also change `type-of' to return `null' for nil. + 10 (lambda (name) `(if ,name (type-of ,name) 'null)) + (lambda (tag) (and (symbolp tag) (assq tag cl--generic-typeof-types))))) + +(cl-defmethod cl-generic-generalizers :extra "typeof" (type) + "Support for dispatch on builtin types." ;; FIXME: Add support for other types accepted by `cl-typep' such ;; as `character', `atom', `face', `function', ... - (and (assq type cl--generic-typeof-types) - (progn - (if (memq type '(vector array sequence)) - (message "`%S' also matches CL structs and EIEIO classes" type)) - ;; FIXME: We could also change `type-of' to return `null' for nil. - `(10 . (if ,name (type-of ,name) 'null))))) - -(add-function :before-until cl-generic-tag-types-function - #'cl--generic-typeof-types) -(defun cl--generic-typeof-types (tag) - (and (symbolp tag) - (assq tag cl--generic-typeof-types))) + (or + (and (assq type cl--generic-typeof-types) + (progn + (if (memq type '(vector array sequence)) + (message "`%S' also matches CL structs and EIEIO classes" type)) + (list cl--generic-typeof-generalizer))) + (cl-call-next-method))) ;;; Just for kicks: dispatch on major-mode ;; @@ -814,7 +953,7 @@ Can only be used from within the lexical body of a primary or around method." ;; (defvar cl--generic-major-modes (make-hash-table :test #'eq)) ;; -;; (add-function :before-until cl-generic-tagcode-function +;; (add-function :before-until cl-generic-generalizer-function ;; #'cl--generic-major-mode-tagcode) ;; (defun cl--generic-major-mode-tagcode (type name) ;; (if (eq 'major-mode (car-safe type)) diff --git a/lisp/emacs-lisp/eieio-compat.el b/lisp/emacs-lisp/eieio-compat.el index 7468c040e10..ee8e731b043 100644 --- a/lisp/emacs-lisp/eieio-compat.el +++ b/lisp/emacs-lisp/eieio-compat.el @@ -124,30 +124,38 @@ Summary: (defgeneric ,method ,args) (eieio--defmethod ',method ',key ',class #',code)))) -(add-function :before-until cl-generic-tagcode-function - #'eieio--generic-static-tagcode) -(defun eieio--generic-static-tagcode (type name) - (and (eq 'eieio--static (car-safe type)) - `(40 . (cond - ((symbolp ,name) (eieio--class-v ,name)) - ((vectorp ,name) (aref ,name 0)))))) - -(add-function :around cl-generic-tag-types-function - #'eieio--generic-static-tag-types) -(defun eieio--generic-static-tag-types (orig-fun tag) - (cond - ((or (eieio--class-p tag) - (and (symbolp tag) (boundp tag) (eieio--class-p (symbol-value tag)))) - (let ((superclasses (funcall orig-fun tag)) - (types ())) - ;; Interleave: (subclass ) (eieio--static ) ) .. - (dolist (superclass superclasses) - (push superclass types) - (push `(eieio--static - ,(if (consp superclass) (cadr superclass) superclass)) - types)) - (nreverse types))) - (t (funcall orig-fun tag)))) +(defconst eieio--generic-static-symbol-generalizer + (cl-generic-make-generalizer + ;; Give it a slightly higher priority than `subclass' so that the + ;; interleaved list comes before subclass's non-interleaved list. + 61 (lambda (name) `(and (symbolp ,name) (eieio--class-v ,name))) + (lambda (tag) + (when (eieio--class-p tag) + (let ((superclasses (eieio--generic-subclass-specializers tag)) + (specializers ())) + (dolist (superclass superclasses) + (push superclass specializers) + (push `(eieio--static ,(cadr superclass)) specializers)) + (nreverse specializers)))))) +(defconst eieio--generic-static-object-generalizer + (cl-generic-make-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 + (lambda (tag) + (and (symbolp tag) (boundp tag) (setq tag (symbol-value tag)) + (eieio--class-p tag) + (let ((superclasses (eieio--class-precedence-list tag)) + (specializers ())) + (dolist (superclass superclasses) + (setq superclass (eieio--class-symbol superclass)) + (push superclass specializers) + (push `(eieio--static ,superclass) specializers)) + (nreverse specializers)))))) + +(cl-defmethod cl-generic-generalizers ((_specializer (head eieio--static))) + (list eieio--generic-static-symbol-generalizer + eieio--generic-static-object-generalizer)) ;;;###autoload (defun eieio--defgeneric-init-form (method doc-string) diff --git a/lisp/emacs-lisp/eieio-core.el b/lisp/emacs-lisp/eieio-core.el index 408922a2daa..1e226c154e2 100644 --- a/lisp/emacs-lisp/eieio-core.el +++ b/lisp/emacs-lisp/eieio-core.el @@ -1203,25 +1203,26 @@ method invocation orders of the involved classes." ;;;; General support to dispatch based on the type of the argument. -(add-function :before-until cl-generic-tagcode-function - #'eieio--generic-tagcode) -(defun eieio--generic-tagcode (type name) +(defconst eieio--generic-generalizer + (cl-generic-make-generalizer + ;; Use the exact same tagcode as for cl-struct, so that methods + ;; that dispatch on both kinds of objects get to share this + ;; part of the dispatch code. + 50 #'cl--generic-struct-tag + (lambda (tag) + (and (symbolp tag) (boundp tag) (eieio--class-p (symbol-value tag)) + (mapcar #'eieio--class-symbol + (eieio--class-precedence-list (symbol-value tag))))))) + +(cl-defmethod cl-generic-generalizers :extra "class" (specializer) ;; CLHS says: ;; A class must be defined before it can be used as a parameter ;; specializer in a defmethod form. ;; So we can ignore types that are not known to denote classes. - (and (eieio--class-p (eieio--class-object type)) - ;; Use the exact same code as for cl-struct, so that methods - ;; that dispatch on both kinds of objects get to share this - ;; part of the dispatch code. - `(50 . ,(cl--generic-struct-tag name)))) - -(add-function :before-until cl-generic-tag-types-function - #'eieio--generic-tag-types) -(defun eieio--generic-tag-types (tag) - (and (symbolp tag) (boundp tag) (eieio--class-p (symbol-value tag)) - (mapcar #'eieio--class-symbol - (eieio--class-precedence-list (symbol-value tag))))) + (or + (and (eieio--class-p (eieio--class-object specializer)) + (list eieio--generic-generalizer)) + (cl-call-next-method))) ;;;; Dispatch for arguments which are classes. @@ -1231,23 +1232,22 @@ method invocation orders of the involved classes." ;; would not make much sense (e.g. to which argument should it apply?). ;; Instead, we add a new "subclass" specializer. -(add-function :before-until cl-generic-tagcode-function - #'eieio--generic-subclass-tagcode) -(defun eieio--generic-subclass-tagcode (type name) - (when (eq 'subclass (car-safe type)) - `(60 . (and (symbolp ,name) (eieio--class-v ,name))))) - -(add-function :before-until cl-generic-tag-types-function - #'eieio--generic-subclass-tag-types) -(defun eieio--generic-subclass-tag-types (tag) +(defun eieio--generic-subclass-specializers (tag) (when (eieio--class-p tag) (mapcar (lambda (class) - `(subclass - ,(if (symbolp class) class (eieio--class-symbol class)))) + `(subclass ,(eieio--class-symbol class))) (eieio--class-precedence-list tag)))) +(defconst eieio--generic-subclass-generalizer + (cl-generic-make-generalizer + 60 (lambda (name) `(and (symbolp ,name) (eieio--class-v ,name))) + #'eieio--generic-subclass-specializers)) + +(cl-defmethod cl-generic-generalizers ((_specializer (head subclass))) + (list eieio--generic-subclass-generalizer)) + -;;;### (autoloads nil "eieio-compat" "eieio-compat.el" "5b04c9a8fff2bd3f3d3ac54aba0f65b7") +;;;### (autoloads nil "eieio-compat" "eieio-compat.el" "25a66814a400e7dea16bf0f3bfe245ed") ;;; Generated autoloads from eieio-compat.el (autoload 'eieio--defalias "eieio-compat" "\ -- cgit v1.2.3 From 801eda8a2a00b3f28a69ffe51b05a649fffc5c58 Mon Sep 17 00:00:00 2001 From: Stefan Monnier Date: Mon, 16 Mar 2015 16:11:38 -0400 Subject: * lisp/emacs-lisp/cl-macs.el (cl--transform-lambda): Optimize &aux. Rework to avoid cl--do-arglist in more cases; add comments to explain what's going on. (cl--do-&aux): New function extracted from cl--do-arglist. (cl--do-arglist): Use it. * lisp/emacs-lisp/cl-generic.el: Add Version: header, for ELPA purposes. --- lisp/ChangeLog | 7 ++ lisp/emacs-lisp/cl-generic.el | 1 + lisp/emacs-lisp/cl-macs.el | 148 +++++++++++++++++++++++++++-------------- test/automated/cl-lib-tests.el | 17 +++++ 4 files changed, 124 insertions(+), 49 deletions(-) (limited to 'lisp/emacs-lisp/cl-generic.el') diff --git a/lisp/ChangeLog b/lisp/ChangeLog index e9e910a8857..41898bee686 100644 --- a/lisp/ChangeLog +++ b/lisp/ChangeLog @@ -1,5 +1,12 @@ 2015-03-16 Stefan Monnier + * emacs-lisp/cl-macs.el (cl--transform-lambda): Rework to avoid + cl--do-arglist in more cases; add comments to explain what's going on. + (cl--do-&aux): New function extracted from cl--do-arglist. + (cl--do-arglist): Use it. + + * emacs-lisp/cl-generic.el: Add Version: header, for ELPA purposes. + * obsolete/iswitchb.el (iswitchb-read-buffer): Add `predicate' arg. * isearchb.el (isearchb-iswitchb): Adjust accordingly. * ido.el (ido-read-buffer): Add `predicate' argument. diff --git a/lisp/emacs-lisp/cl-generic.el b/lisp/emacs-lisp/cl-generic.el index a8483ea1355..41c760e960e 100644 --- a/lisp/emacs-lisp/cl-generic.el +++ b/lisp/emacs-lisp/cl-generic.el @@ -3,6 +3,7 @@ ;; Copyright (C) 2015 Free Software Foundation, Inc. ;; Author: Stefan Monnier +;; Version: 1.0 ;; This file is part of GNU Emacs. diff --git a/lisp/emacs-lisp/cl-macs.el b/lisp/emacs-lisp/cl-macs.el index 36f263cd20a..712a7485167 100644 --- a/lisp/emacs-lisp/cl-macs.el +++ b/lisp/emacs-lisp/cl-macs.el @@ -220,7 +220,20 @@ The name is made by appending a number to PREFIX, default \"G\"." (defconst cl--lambda-list-keywords '(&optional &rest &key &allow-other-keys &aux &whole &body &environment)) -(defvar cl--bind-block) (defvar cl--bind-defs) (defvar cl--bind-enquote) +;; Internal hacks used in formal arg lists: +;; - &cl-quote: Added to formal-arglists to mean that any default value +;; mentioned in the formal arglist should be considered as implicitly +;; quoted rather than evaluated. This is used in `cl-defsubst' when +;; performing compiler-macro-expansion, since at that time the +;; arguments hold expressions rather than values. +;; - &cl-defs (DEF . DEFS): Gives the default value to use for missing +;; optional arguments which don't have an explicit default value. +;; DEFS is an alist mapping vars to their default default value. +;; and DEF is the default default to use for all other vars. + +(defvar cl--bind-block) ;Name of surrounding block, only use for `signal' data. +(defvar cl--bind-defs) ;(DEF . DEFS) giving the "default default" for optargs. +(defvar cl--bind-enquote) ;Non-nil if &cl-quote was in the formal arglist! (defvar cl--bind-lets) (defvar cl--bind-forms) (defun cl--transform-lambda (form bind-block) @@ -229,19 +242,26 @@ BIND-BLOCK is the name of the symbol to which the function will be bound, and which will be used for the name of the `cl-block' surrounding the function's body. FORM is of the form (ARGS . BODY)." - ;; FIXME: (lambda (a &aux b) 1) expands to (lambda (a &rest --cl-rest--) ...) - ;; where the --cl-rest-- is clearly undesired. (let* ((args (car form)) (body (cdr form)) (orig-args args) (cl--bind-block bind-block) (cl--bind-defs nil) (cl--bind-enquote nil) - (cl--bind-lets nil) (cl--bind-forms nil) (parsed-body (macroexp-parse-body body)) (header (car parsed-body)) (simple-args nil)) (setq body (cdr parsed-body)) + ;; "(. X) to (&rest X)" conversion already done in cl--do-arglist, but we + ;; do it here as well, so as to be able to see if we can avoid + ;; cl--do-arglist. (setq args (if (listp args) (cl-copy-list args) (list '&rest args))) (let ((p (last args))) (if (cdr p) (setcdr p (list '&rest (cdr p))))) - (if (setq cl--bind-defs (cadr (memq '&cl-defs args))) - (setq args (delq '&cl-defs (delq cl--bind-defs args)) - cl--bind-defs (cadr cl--bind-defs))) + (let ((cl-defs (memq '&cl-defs args))) + (when cl-defs + (setq cl--bind-defs (cadr cl-defs)) + ;; Remove "&cl-defs DEFS" from args. + (setcdr cl-defs (cddr cl-defs)) + (setq args (delq '&cl-defs args)) + ;; Optimize away trivial &cl-defs. + (if (and (null (car cl--bind-defs)) + (cl-every (lambda (x) (null (cadr x))) (cdr cl--bind-defs))) + (setq cl--bind-defs nil)))) (if (setq cl--bind-enquote (memq '&cl-quote args)) (setq args (delq '&cl-quote args))) (if (memq '&whole args) (error "&whole not currently implemented")) @@ -249,6 +269,9 @@ FORM is of the form (ARGS . BODY)." (v (cadr p))) (if p (setq args (nconc (delq (car p) (delq v args)) `(&aux (,v macroexpand-all-environment)))))) + ;; Take away all the simple args whose parsing can be handled more + ;; efficiently by a plain old `lambda' than the manual parsing generated + ;; by `cl--do-arglist'. (while (and args (symbolp (car args)) (not (memq (car args) '(nil &rest &body &key &aux))) (not (and (eq (car args) '&optional) @@ -256,30 +279,50 @@ FORM is of the form (ARGS . BODY)." (push (pop args) simple-args)) (or (eq cl--bind-block 'cl-none) (setq body (list `(cl-block ,cl--bind-block ,@body)))) - (if (null args) - (cl-list* nil (nreverse simple-args) (nconc header body)) - (if (memq '&optional simple-args) (push '&optional args)) - (cl--do-arglist args nil (- (length simple-args) - (if (memq '&optional simple-args) 1 0))) - (setq cl--bind-lets (nreverse cl--bind-lets)) - (cl-list* nil - (nconc (nreverse simple-args) - (list '&rest (car (pop cl--bind-lets)))) - (nconc (save-match-data ;; Macro expansion can take place in the - ;; middle of apparently harmless computation, so it - ;; should not touch the match-data. - (require 'help-fns) - (cons (help-add-fundoc-usage - (if (stringp (car header)) (pop header)) - ;; Be careful with make-symbol and (back)quote, - ;; see bug#12884. - (let ((print-gensym nil) (print-quoted t)) - (format "%S" (cons 'fn (cl--make-usage-args - orig-args))))) - header)) - (list `(let* ,cl--bind-lets - ,@(nreverse cl--bind-forms) - ,@body))))))) + (let* ((cl--bind-lets nil) (cl--bind-forms nil) + (rest-args + (cond + ((null args) nil) + ((eq (car args) '&aux) + (cl--do-&aux args) + (setq cl--bind-lets (nreverse cl--bind-lets)) + nil) + (t ;; `simple-args' doesn't handle all the parsing that we need, + ;; so we pass the rest to cl--do-arglist which will do + ;; "manual" parsing. + (let ((slen (length simple-args))) + (when (memq '&optional simple-args) + (push '&optional args) (cl-decf slen)) + (setq header + ;; Macro expansion can take place in the middle of + ;; apparently harmless computation, so it should not + ;; touch the match-data. + (save-match-data + (require 'help-fns) + (cons (help-add-fundoc-usage + (if (stringp (car header)) (pop header)) + ;; Be careful with make-symbol and (back)quote, + ;; see bug#12884. + (let ((print-gensym nil) (print-quoted t)) + (format "%S" (cons 'fn (cl--make-usage-args + orig-args))))) + header))) + ;; FIXME: we'd want to choose an arg name for the &rest param + ;; and pass that as `expr' to cl--do-arglist, but that ends up + ;; generating code with a redundant let-binding, so we instead + ;; pass a dummy and then look in cl--bind-lets to find what var + ;; this was bound to. + (cl--do-arglist args :dummy slen) + (setq cl--bind-lets (nreverse cl--bind-lets)) + ;; (cl-assert (eq :dummy (nth 1 (car cl--bind-lets)))) + (list '&rest (car (pop cl--bind-lets)))))))) + `(nil + (,@(nreverse simple-args) ,@rest-args) + ,@header + ,(macroexp-let* cl--bind-lets + (macroexp-progn + `(,@(nreverse cl--bind-forms) + ,@body))))))) ;;;###autoload (defmacro cl-defun (name args &rest body) @@ -422,8 +465,7 @@ its argument list allows full Common Lisp conventions." (setcdr last nil) (nconc (cl--make-usage-args arglist) (cl--make-usage-var tail))) (setcdr last tail))) - ;; `orig-args' can contain &cl-defs (an internal - ;; CL thingy I don't understand), so remove it. + ;; `orig-args' can contain &cl-defs. (let ((x (memq '&cl-defs arglist))) (when x (setq arglist (delq (car x) (remq (cadr x) arglist))))) (let ((state nil)) @@ -450,6 +492,17 @@ its argument list allows full Common Lisp conventions." )))) arglist)))) +(defun cl--do-&aux (args) + (while (and (eq (car args) '&aux) (pop args)) + (while (and args (not (memq (car args) cl--lambda-list-keywords))) + (if (consp (car args)) + (if (and cl--bind-enquote (cl-cadar args)) + (cl--do-arglist (caar args) + `',(cadr (pop args))) + (cl--do-arglist (caar args) (cadr (pop args)))) + (cl--do-arglist (pop args) nil)))) + (if args (error "Malformed argument list ends with: %S" args))) + (defun cl--do-arglist (args expr &optional num) ; uses cl--bind-* (if (nlistp args) (if (or (memq args cl--lambda-list-keywords) (not (symbolp args))) @@ -459,8 +512,7 @@ its argument list allows full Common Lisp conventions." (let ((p (last args))) (if (cdr p) (setcdr p (list '&rest (cdr p))))) (let ((p (memq '&body args))) (if p (setcar p '&rest))) (if (memq '&environment args) (error "&environment used incorrectly")) - (let ((save-args args) - (restarg (memq '&rest args)) + (let ((restarg (memq '&rest args)) (safety (if (cl--compiling-file) cl--optimize-safety 3)) (keys nil) (laterarg nil) (exactarg nil) minarg) @@ -530,7 +582,12 @@ its argument list allows full Common Lisp conventions." (intern (format ":%s" name))))) (varg (if (consp (car arg)) (cl-cadar arg) (car arg))) (def (if (cdr arg) (cadr arg) - (or (car cl--bind-defs) (cadr (assq varg cl--bind-defs))))) + ;; The ordering between those two or clauses is + ;; irrelevant, since in practice only one of the two + ;; is ever non-nil (the car is only used for + ;; cl-deftype which doesn't use the cdr). + (or (car cl--bind-defs) + (cadr (assq varg cl--bind-defs))))) (look `(plist-member ,restarg ',karg))) (and def cl--bind-enquote (setq def `',def)) (if (cddr arg) @@ -567,15 +624,8 @@ its argument list allows full Common Lisp conventions." keys) (car ,var))))))) (push `(let ((,var ,restarg)) ,check) cl--bind-forms))) - (while (and (eq (car args) '&aux) (pop args)) - (while (and args (not (memq (car args) cl--lambda-list-keywords))) - (if (consp (car args)) - (if (and cl--bind-enquote (cl-cadar args)) - (cl--do-arglist (caar args) - `',(cadr (pop args))) - (cl--do-arglist (caar args) (cadr (pop args)))) - (cl--do-arglist (pop args) nil)))) - (if args (error "Malformed argument list %s" save-args))))) + (cl--do-&aux args) + nil))) (defun cl--arglist-args (args) (if (nlistp args) (list args) @@ -2608,7 +2658,7 @@ non-nil value, that slot cannot be set via `setf'. (make (cl-mapcar (function (lambda (s d) (if (memq s anames) s d))) slots defaults))) (push `(cl-defsubst ,name - (&cl-defs '(nil ,@descs) ,@args) + (&cl-defs (nil ,@descs) ,@args) ,@(if (cl--safe-expr-p `(progn ,@(mapcar #'cl-second descs))) '((declare (side-effect-free t)))) (,(or type #'vector) ,@make)) @@ -2716,8 +2766,8 @@ Of course, we really can't know that for sure, so it's just a heuristic." (t (inline-quote (or (cl-typep ,val ',head) (cl-typep ,val ',rest))))))))) - (`(member . ,args) - (inline-quote (and (memql ,val ',args) t))) + (`(eql ,v) (inline-quote (and (eql ,val ',v) t))) + (`(member . ,args) (inline-quote (and (memql ,val ',args) t))) (`(satisfies ,pred) (inline-quote (funcall #',pred ,val))) ((and (pred symbolp) type (guard (get type 'cl-deftype-handler))) (inline-quote @@ -2977,7 +3027,7 @@ The type name can then be used in `cl-typecase', `cl-check-type', etc." (declare (debug cl-defmacro) (doc-string 3) (indent 2)) `(cl-eval-when (compile load eval) (put ',name 'cl-deftype-handler - (cl-function (lambda (&cl-defs '('*) ,@arglist) ,@body))))) + (cl-function (lambda (&cl-defs ('*) ,@arglist) ,@body))))) (cl-deftype extended-char () `(and character (not base-char))) diff --git a/test/automated/cl-lib-tests.el b/test/automated/cl-lib-tests.el index 1c36e7d7abf..2c188a40059 100644 --- a/test/automated/cl-lib-tests.el +++ b/test/automated/cl-lib-tests.el @@ -427,4 +427,21 @@ (ert-deftest cl-flet-test () (should (equal (cl-flet ((f1 (x) x)) (let ((x #'f1)) (funcall x 5))) 5))) +(ert-deftest cl-lib-test-typep () + (cl-deftype cl-lib-test-type (&optional x) `(member ,x)) + ;; Make sure we correctly implement the rule that deftype's optional args + ;; default to `*' rather than to nil. + (should (cl-typep '* 'cl-lib-test-type)) + (should-not (cl-typep 1 'cl-lib-test-type))) + +(ert-deftest cl-lib-arglist-performance () + ;; An `&aux' should not cause lambda's arglist to be turned into an &rest + ;; that's parsed by hand. + (should (eq () (nth 1 (nth 1 (macroexpand + '(cl-function (lambda (&aux (x 1)) x))))))) + (cl-defstruct (cl-lib--s (:constructor cl-lib--s-make (&optional a))) a) + ;; Similarly the &cl-defs thingy shouldn't cause fallback to manual parsing + ;; of args if the default for optional args is nil. + (should (equal '(&optional a) (help-function-arglist 'cl-lib--s-make)))) + ;;; cl-lib.el ends here -- cgit v1.2.3 From 872481d9e26d7569145c897fd319b1104e028878 Mon Sep 17 00:00:00 2001 From: Stefan Monnier Date: Wed, 18 Mar 2015 10:31:07 -0400 Subject: Add classes as run-time descriptors of cl-structs. * lisp/emacs-lisp/cl-preloaded.el (cl--struct-get-class): New function. (cl--make-slot-desc): New constructor. (cl--plist-remove, cl--struct-register-child): New functions. (cl-struct-define): Rewrite. (cl-structure-class, cl-structure-object, cl-slot-descriptor) (cl--class): New structs. (cl--struct-default-parent): Initialize it here. * lisp/emacs-lisp/cl-macs.el (cl--find-class): New macro. (cl-defsubst, cl--defsubst-expand, cl--sublis): Move before first use. (cl--struct-default-parent): New var. (cl-defstruct): Adjust to new representation of classes; add default parent. In accessors, signal `wrong-type-argument' rather than a generic error. (cl-struct-sequence-type, cl-struct-slot-info) (cl-struct-slot-offset): Rewrite. * lisp/emacs-lisp/cl-generic.el (cl--generic-struct-specializers) (cl-generic-generalizers): Rewrite. * src/alloc.c (purecopy): Handle hash-tables. * lisp/emacs-lisp/debug.el (debug--implement-debug-on-entry): Bind inhibit-debug-on-entry here... (debug): Instead of here. * lisp/emacs-lisp/macroexp.el (macroexp--debug-eager): New var. (internal-macroexpand-for-load): Use it. * lwlib/xlwmenu.c (pop_up_menu): Remove debugging code. --- lisp/ChangeLog | 62 +++++++---- lisp/emacs-lisp/cl-generic.el | 64 ++++++----- lisp/emacs-lisp/cl-macs.el | 198 +++++++++++++++++++--------------- lisp/emacs-lisp/cl-preloaded.el | 231 +++++++++++++++++++++++++++++++++++----- lisp/emacs-lisp/debug.el | 8 +- lisp/emacs-lisp/macroexp.el | 8 +- lwlib/ChangeLog | 4 + lwlib/xlwmenu.c | 58 +++++----- src/ChangeLog | 18 ++-- src/alloc.c | 43 ++++---- 10 files changed, 468 insertions(+), 226 deletions(-) (limited to 'lisp/emacs-lisp/cl-generic.el') diff --git a/lisp/ChangeLog b/lisp/ChangeLog index d61a0a67673..2db0f9a349a 100644 --- a/lisp/ChangeLog +++ b/lisp/ChangeLog @@ -1,3 +1,31 @@ +2015-03-18 Stefan Monnier + + Add classes as run-time descriptors of cl-structs. + * emacs-lisp/cl-preloaded.el (cl--struct-get-class): New function. + (cl--make-slot-desc): New constructor. + (cl--plist-remove, cl--struct-register-child): New functions. + (cl-struct-define): Rewrite. + (cl-structure-class, cl-structure-object, cl-slot-descriptor) + (cl--class): New structs. + (cl--struct-default-parent): Initialize it here. + * emacs-lisp/cl-macs.el (cl--find-class): New macro. + (cl-defsubst, cl--defsubst-expand, cl--sublis): Move before first use. + (cl--struct-default-parent): New var. + (cl-defstruct): Adjust to new representation of classes; add + default parent. In accessors, signal `wrong-type-argument' rather than + a generic error. + (cl-struct-sequence-type, cl-struct-slot-info) + (cl-struct-slot-offset): Rewrite. + * emacs-lisp/cl-generic.el (cl--generic-struct-specializers) + (cl-generic-generalizers): Rewrite. + + * emacs-lisp/macroexp.el (macroexp--debug-eager): New var. + (internal-macroexpand-for-load): Use it. + + * emacs-lisp/debug.el (debug--implement-debug-on-entry): + Bind inhibit-debug-on-entry here... + (debug): Instead of here. + 2015-03-18 Dima Kogan Have gud-display-line not display source buffer in gud window. @@ -6,13 +34,13 @@ 2015-03-17 Tassilo Horn - * emacs-lisp/byte-run.el (macro-declarations-alist): New - declaration no-font-lock-keyword. + * emacs-lisp/byte-run.el (macro-declarations-alist): + New declaration no-font-lock-keyword. (defmacro): Flush font-lock in existing elisp buffers. * emacs-lisp/lisp-mode.el (lisp--el-update-after-load) - (lisp--el-update-macro-regexp, lisp--el-macro-regexp): Delete - functions and defconst. + (lisp--el-update-macro-regexp, lisp--el-macro-regexp): + Delete functions and defconst. (lisp--el-match-keyword): Rename from lisp--el-match-macro. (lisp--el-font-lock-flush-elisp-buffers): New function. (lisp-mode-variables): Remove code for updating @@ -21,23 +49,17 @@ 2015-03-17 Simen Heggestøyl - * textmodes/css-mode.el (css--font-lock-keywords): Discriminate - between pseudo-classes and pseudo-elements. + * textmodes/css-mode.el (css--font-lock-keywords): + Discriminate between pseudo-classes and pseudo-elements. (css-pseudo-ids): Remove. - (css-pseudo-class-ids): New variable. - (css-pseudo-element-ids): New variable. - (css--complete-property): New function for completing CSS - properties. - (css--complete-pseudo-element-or-class): New function for + (css-pseudo-class-ids, css-pseudo-element-ids): New variables. + (css--complete-property): New function for completing CSS properties. + (css--complete-pseudo-element-or-class): New function completing CSS pseudo-elements and pseudo-classes. (css--complete-at-rule): New function for completing CSS at-rules. - (css-completion-at-point): New function providing completion for - `css-mode'. + (css-completion-at-point): New function. (css-mode): Add support for completion. - (css-extract-keyword-list): Remove function in favor of manual - extraction. - (css-extract-parse-val-grammar): Remove function in favor of - manual extraction. + (css-extract-keyword-list, css-extract-parse-val-grammar) (css-extract-props-and-vals): Remove function in favor of manual extraction. (css-at-ids): Update list of CSS at-rule ids. @@ -163,7 +185,7 @@ * progmodes/sql.el: Version 3.5 (sql-starts-with-prompt-re, sql-ends-with-prompt-re): Match password prompts. - (sql-interactive-remove-continuation-prompt): Fixed regression. (Bug#6686) + (sql-interactive-remove-continuation-prompt): Fix regression. (Bug#6686) 2015-03-14 Daniel Colascione @@ -178,8 +200,8 @@ info-look fixes for Texinfo 5 * info-look.el (c-mode, bison-mode, makefile-mode) (makefile-automake-mode, texinfo-mode, autoconf-mode, awk-mode) - (latex-mode, emacs-lisp-mode, sh-mode, cfengine-mode): Match - `foo' and 'foo' and ‘foo’ for @item and similar. + (latex-mode, emacs-lisp-mode, sh-mode, cfengine-mode): + Match `foo' and 'foo' and ‘foo’ for @item and similar. (latex-mode): Match multi-arg \frac{num}{den} or \sqrt[root]{n} in suffix regexp. diff --git a/lisp/emacs-lisp/cl-generic.el b/lisp/emacs-lisp/cl-generic.el index 41c760e960e..c9ca92d7c09 100644 --- a/lisp/emacs-lisp/cl-generic.el +++ b/lisp/emacs-lisp/cl-generic.el @@ -857,6 +857,18 @@ Can only be used from within the lexical body of a primary or around method." ;;; Support for cl-defstructs specializers. (defun cl--generic-struct-tag (name) + ;; It's tempting to use (and (vectorp ,name) (aref ,name 0)) + ;; but that would suffer from some problems: + ;; - the vector may have size 0. + ;; - when called on an actual vector (rather than an object), we'd + ;; end up returning an arbitrary value, possibly colliding with + ;; other tagcode's values. + ;; - it can also result in returning all kinds of irrelevant + ;; values which would end up filling up the method-cache with + ;; lots of irrelevant/redundant entries. + ;; FIXME: We could speed this up by introducing a dedicated + ;; vector type at the C level, so we could do something like + ;; (and (vector-objectp ,name) (aref ,name 0)) `(and (vectorp ,name) (> (length ,name) 0) (let ((tag (aref ,name 0))) @@ -864,14 +876,18 @@ Can only be used from within the lexical body of a primary or around method." tag)))) (defun cl--generic-struct-specializers (tag) - (and (symbolp tag) - ;; A method call shouldn't itself mess with the match-data. - (string-match-p "\\`cl-struct-\\(.*\\)" (symbol-name tag)) - (let ((types (list (intern (substring (symbol-name tag) 10))))) - (while (get (car types) 'cl-struct-include) - (push (get (car types) 'cl-struct-include) types)) - (push 'cl-structure-object types) ;The "parent type" of all cl-structs. - (nreverse types)))) + (and (symbolp tag) (boundp tag) + (let ((class (symbol-value tag))) + (when (cl-typep class 'cl-structure-class) + (let ((types ()) + (classes (list class))) + ;; BFS precedence. + (while (let ((class (pop classes))) + (push (cl--class-name class) types) + (setq classes + (append classes + (cl--class-parents class))))) + (nreverse types)))))) (defconst cl--generic-struct-generalizer (cl-generic-make-generalizer @@ -881,27 +897,17 @@ Can only be used from within the lexical body of a primary or around method." (cl-defmethod cl-generic-generalizers :extra "cl-struct" (type) "Support for dispatch on cl-struct types." (or - (and (symbolp type) - (get type 'cl-struct-type) - (or (null (car (get type 'cl-struct-type))) - (error "Can't dispatch on cl-struct %S: type is %S" - type (car (get type 'cl-struct-type)))) - (or (equal '(cl-tag-slot) (car (get type 'cl-struct-slots))) - (error "Can't dispatch on cl-struct %S: no tag in slot 0" - type)) - ;; It's tempting to use (and (vectorp ,name) (aref ,name 0)) - ;; but that would suffer from some problems: - ;; - the vector may have size 0. - ;; - when called on an actual vector (rather than an object), we'd - ;; end up returning an arbitrary value, possibly colliding with - ;; other tagcode's values. - ;; - it can also result in returning all kinds of irrelevant - ;; values which would end up filling up the method-cache with - ;; lots of irrelevant/redundant entries. - ;; FIXME: We could speed this up by introducing a dedicated - ;; vector type at the C level, so we could do something like - ;; (and (vector-objectp ,name) (aref ,name 0)) - (list cl--generic-struct-generalizer)) + (when (symbolp type) + ;; Use the "cl--struct-class*" (inlinable) functions/macros rather than + ;; the "cl-struct-*" variants which aren't inlined, so that dispatch can + ;; take place without requiring cl-lib. + (let ((class (cl--find-class type))) + (and (cl-typep class 'cl-structure-class) + (when (cl--struct-class-type class) + (error "Can't dispatch on cl-struct %S: type is %S" + type (cl--struct-class-type class))) + (progn (cl-assert (null (cl--struct-class-named class))) t) + (list cl--generic-struct-generalizer)))) (cl-call-next-method))) ;;; Dispatch on "system types". diff --git a/lisp/emacs-lisp/cl-macs.el b/lisp/emacs-lisp/cl-macs.el index 56fbcf0b2fd..d3866783447 100644 --- a/lisp/emacs-lisp/cl-macs.el +++ b/lisp/emacs-lisp/cl-macs.el @@ -2434,8 +2434,79 @@ Like `cl-callf', but PLACE is the second argument of FUNC, not the first. (if (symbolp func) (cons func rargs) `(funcall #',func ,@rargs)))))))) +;;;###autoload +(defmacro cl-defsubst (name args &rest body) + "Define NAME as a function. +Like `defun', except the function is automatically declared `inline' and +the arguments are immutable. +ARGLIST allows full Common Lisp conventions, and BODY is implicitly +surrounded by (cl-block NAME ...). +The function's arguments should be treated as immutable. + +\(fn NAME ARGLIST [DOCSTRING] BODY...)" + (declare (debug cl-defun) (indent 2)) + (let* ((argns (cl--arglist-args args)) + (p argns) + ;; (pbody (cons 'progn body)) + ) + (while (and p (eq (cl--expr-contains args (car p)) 1)) (pop p)) + `(progn + ,(if p nil ; give up if defaults refer to earlier args + `(cl-define-compiler-macro ,name + ,(if (memq '&key args) + `(&whole cl-whole &cl-quote ,@args) + (cons '&cl-quote args)) + (cl--defsubst-expand + ',argns '(cl-block ,name ,@body) + ;; We used to pass `simple' as + ;; (not (or unsafe (cl-expr-access-order pbody argns))) + ;; But this is much too simplistic since it + ;; does not pay attention to the argvs (and + ;; cl-expr-access-order itself is also too naive). + nil + ,(and (memq '&key args) 'cl-whole) nil ,@argns))) + (cl-defun ,name ,args ,@body)))) + +(defun cl--defsubst-expand (argns body simple whole _unsafe &rest argvs) + (if (and whole (not (cl--safe-expr-p (cons 'progn argvs)))) whole + (if (cl--simple-exprs-p argvs) (setq simple t)) + (let* ((substs ()) + (lets (delq nil + (cl-mapcar (lambda (argn argv) + (if (or simple (macroexp-const-p argv)) + (progn (push (cons argn argv) substs) + nil) + (list argn argv))) + argns argvs)))) + ;; FIXME: `sublis/subst' will happily substitute the symbol + ;; `argn' in places where it's not used as a reference + ;; to a variable. + ;; FIXME: `sublis/subst' will happily copy `argv' to a different + ;; scope, leading to name capture. + (setq body (cond ((null substs) body) + ((null (cdr substs)) + (cl-subst (cdar substs) (caar substs) body)) + (t (cl--sublis substs body)))) + (if lets `(let ,lets ,body) body)))) + +(defun cl--sublis (alist tree) + "Perform substitutions indicated by ALIST in TREE (non-destructively)." + (let ((x (assq tree alist))) + (cond + (x (cdr x)) + ((consp tree) + (cons (cl--sublis alist (car tree)) (cl--sublis alist (cdr tree)))) + (t tree)))) + ;;; Structures. +(defmacro cl--find-class (type) + `(get ,type 'cl--class)) + +;; Rather than hard code cl-structure-object, we indirect through this variable +;; for bootstrapping reasons. +(defvar cl--struct-default-parent nil) + ;;;###autoload (defmacro cl-defstruct (struct &rest descs) "Define a struct type. @@ -2491,6 +2562,7 @@ non-nil value, that slot cannot be set via `setf'. (tag (intern (format "cl-struct-%s" name))) (tag-symbol (intern (format "cl-struct-%s-tags" name))) (include-descs nil) + (include-name nil) (type nil) (named nil) (forms nil) @@ -2520,12 +2592,14 @@ non-nil value, that slot cannot be set via `setf'. ((eq opt :predicate) (if args (setq predicate (car args)))) ((eq opt :include) - (when include (error "Can't :include more than once")) - (setq include (car args) - include-descs (mapcar (function - (lambda (x) - (if (consp x) x (list x)))) - (cdr args)))) + ;; FIXME: Actually, we can include more than once as long as + ;; we include EIEIO classes rather than cl-structs! + (when include-name (error "Can't :include more than once")) + (setq include-name (car args)) + (setq include-descs (mapcar (function + (lambda (x) + (if (consp x) x (list x)))) + (cdr args)))) ((eq opt :print-function) (setq print-func (car args))) ((eq opt :type) @@ -2537,19 +2611,21 @@ non-nil value, that slot cannot be set via `setf'. descs))) (t (error "Slot option %s unrecognized" opt))))) + (unless (or include-name type) + (setq include-name cl--struct-default-parent)) + (when include-name (setq include (cl--struct-get-class include-name))) (if print-func (setq print-func `(progn (funcall #',print-func cl-x cl-s cl-n) t)) - (or type (and include (not (get include 'cl-struct-print))) + (or type (and include (not (cl--struct-class-print include))) (setq print-auto t print-func (and (or (not (or include type)) (null print-func)) `(progn (princ ,(format "#S(%s" name) cl-s)))))) (if include - (let ((inc-type (get include 'cl-struct-type)) - (old-descs (get include 'cl-struct-slots))) - (or inc-type (error "%s is not a struct name" include)) - (and type (not (eq (car inc-type) type)) + (let* ((inc-type (cl--struct-class-type include)) + (old-descs (cl-struct-slot-info include))) + (and type (not (eq inc-type type)) (error ":type disagrees with :include for %s" name)) (while include-descs (setcar (memq (or (assq (caar include-descs) old-descs) @@ -2558,9 +2634,9 @@ non-nil value, that slot cannot be set via `setf'. old-descs) (pop include-descs))) (setq descs (append old-descs (delq (assq 'cl-tag-slot descs) descs)) - type (car inc-type) - named (assq 'cl-tag-slot descs)) - (if (cadr inc-type) (setq tag name named t))) + type inc-type + named (if type (assq 'cl-tag-slot descs) 'true)) + (if (cl--struct-class-named include) (setq tag name named t))) (if type (progn (or (memq type '(vector list)) @@ -2605,8 +2681,8 @@ non-nil value, that slot cannot be set via `setf'. (declare (side-effect-free t)) ,@(and pred-check (list `(or ,pred-check - (error "%s accessing a non-%s" - ',accessor ',name)))) + (signal 'wrong-type-argument + (list ',name cl-x))))) ,(if (memq type '(nil vector)) `(aref cl-x ,pos) (if (= pos 0) '(car cl-x) `(nth ,pos cl-x)))) @@ -2682,8 +2758,11 @@ non-nil value, that slot cannot be set via `setf'. `(progn (defvar ,tag-symbol) ,@(nreverse forms) + ;; Call cl-struct-define during compilation as well, so that + ;; a subsequent cl-defstruct in the same file can correctly include this + ;; struct as a parent. (eval-and-compile - (cl-struct-define ',name ,docstring ',include + (cl-struct-define ',name ,docstring ',include-name ',type ,(eq named t) ',descs ',tag-symbol ',tag ',print-auto)) ',name))) @@ -2693,7 +2772,7 @@ non-nil value, that slot cannot be set via `setf'. STRUCT-TYPE is a symbol naming a struct type. Return 'vector or 'list, or nil if STRUCT-TYPE is not a struct type. " (declare (side-effect-free t) (pure t)) - (car (get struct-type 'cl-struct-type))) + (cl--struct-class-type (cl--struct-get-class struct-type))) (defun cl-struct-slot-info (struct-type) "Return a list of slot names of struct STRUCT-TYPE. @@ -2702,7 +2781,19 @@ slot name symbol and OPTS is a list of slot options given to `cl-defstruct'. Dummy slots that represent the struct name and slots skipped by :initial-offset may appear in the list." (declare (side-effect-free t) (pure t)) - (get struct-type 'cl-struct-slots)) + (let* ((class (cl--struct-get-class struct-type)) + (slots (cl--struct-class-slots class)) + (type (cl--struct-class-type class)) + (descs (if type () (list '(cl-tag-slot))))) + (dotimes (i (length slots)) + (let ((slot (aref slots i))) + (push `(,(cl--slot-descriptor-name slot) + ,(cl--slot-descriptor-initform slot) + ,@(if (not (eq (cl--slot-descriptor-type slot) t)) + `(:type ,(cl--slot-descriptor-type slot))) + ,@(cl--slot-descriptor-props slot)) + descs))) + (nreverse descs))) (defun cl-struct-slot-offset (struct-type slot-name) "Return the offset of slot SLOT-NAME in STRUCT-TYPE. @@ -2711,9 +2802,8 @@ the structure data type and is adjusted for any structure name and :initial-offset slots. Signal error if struct STRUCT-TYPE does not contain SLOT-NAME." (declare (side-effect-free t) (pure t)) - (or (cl-position slot-name - (cl-struct-slot-info struct-type) - :key #'car :test #'eq) + (or (gethash slot-name + (cl--class-index-table (cl--struct-get-class struct-type))) (error "struct %s has no slot %s" struct-type slot-name))) (defvar byte-compile-function-environment) @@ -2898,70 +2988,6 @@ macro that returns its `&whole' argument." (if cl-found (setcdr cl-found t))) `(throw ,cl-tag ,cl-value)) -;;;###autoload -(defmacro cl-defsubst (name args &rest body) - "Define NAME as a function. -Like `defun', except the function is automatically declared `inline' and -the arguments are immutable. -ARGLIST allows full Common Lisp conventions, and BODY is implicitly -surrounded by (cl-block NAME ...). -The function's arguments should be treated as immutable. - -\(fn NAME ARGLIST [DOCSTRING] BODY...)" - (declare (debug cl-defun) (indent 2)) - (let* ((argns (cl--arglist-args args)) - (p argns) - ;; (pbody (cons 'progn body)) - ) - (while (and p (eq (cl--expr-contains args (car p)) 1)) (pop p)) - `(progn - ,(if p nil ; give up if defaults refer to earlier args - `(cl-define-compiler-macro ,name - ,(if (memq '&key args) - `(&whole cl-whole &cl-quote ,@args) - (cons '&cl-quote args)) - (cl--defsubst-expand - ',argns '(cl-block ,name ,@body) - ;; We used to pass `simple' as - ;; (not (or unsafe (cl-expr-access-order pbody argns))) - ;; But this is much too simplistic since it - ;; does not pay attention to the argvs (and - ;; cl-expr-access-order itself is also too naive). - nil - ,(and (memq '&key args) 'cl-whole) nil ,@argns))) - (cl-defun ,name ,args ,@body)))) - -(defun cl--defsubst-expand (argns body simple whole _unsafe &rest argvs) - (if (and whole (not (cl--safe-expr-p (cons 'progn argvs)))) whole - (if (cl--simple-exprs-p argvs) (setq simple t)) - (let* ((substs ()) - (lets (delq nil - (cl-mapcar (lambda (argn argv) - (if (or simple (macroexp-const-p argv)) - (progn (push (cons argn argv) substs) - nil) - (list argn argv))) - argns argvs)))) - ;; FIXME: `sublis/subst' will happily substitute the symbol - ;; `argn' in places where it's not used as a reference - ;; to a variable. - ;; FIXME: `sublis/subst' will happily copy `argv' to a different - ;; scope, leading to name capture. - (setq body (cond ((null substs) body) - ((null (cdr substs)) - (cl-subst (cdar substs) (caar substs) body)) - (t (cl--sublis substs body)))) - (if lets `(let ,lets ,body) body)))) - -(defun cl--sublis (alist tree) - "Perform substitutions indicated by ALIST in TREE (non-destructively)." - (let ((x (assq tree alist))) - (cond - (x (cdr x)) - ((consp tree) - (cons (cl--sublis alist (car tree)) (cl--sublis alist (cdr tree)))) - (t tree)))) - ;; Compile-time optimizations for some functions defined in this package. (defun cl--compiler-macro-member (form a list &rest keys) diff --git a/lisp/emacs-lisp/cl-preloaded.el b/lisp/emacs-lisp/cl-preloaded.el index 401d34b449e..a18e0e57b05 100644 --- a/lisp/emacs-lisp/cl-preloaded.el +++ b/lisp/emacs-lisp/cl-preloaded.el @@ -21,36 +21,22 @@ ;;; Commentary: -;; The expectation is that structs defined with cl-defstruct do not -;; need cl-lib at run-time, but we'd like to hide the details of the -;; cl-struct metadata behind the cl-struct-define function, so we put -;; it in this pre-loaded file. +;; The cl-defstruct macro is full of circularities, since it uses the +;; cl-structure-class type (and its accessors) which is defined with itself, +;; and it setups a default parent (cl-structure-object) which is also defined +;; with cl-defstruct, and to make things more interesting, the class of +;; cl-structure-object is of course an object of type cl-structure-class while +;; cl-structure-class's parent is cl-structure-object. +;; Furthermore, the code generated by cl-defstruct generally assumes that the +;; parent will be loaded when the child is loaded. But at the same time, the +;; expectation is that structs defined with cl-defstruct do not need cl-lib at +;; run-time, which means that the `cl-structure-object' parent can't be in +;; cl-lib but should be preloaded. So here's this preloaded circular setup. ;;; Code: (eval-when-compile (require 'cl-lib)) - -(defun cl-struct-define (name docstring parent type named slots children-sym - tag print-auto) - (cl-assert (or type (equal '(cl-tag-slot) (car slots)))) - (cl-assert (or type (not named))) - (if (boundp children-sym) - (add-to-list children-sym tag) - (set children-sym (list tag))) - (let* ((parent-class parent)) - (while parent-class - (add-to-list (intern (format "cl-struct-%s-tags" parent-class)) tag) - (setq parent-class (get parent-class 'cl-struct-include)))) - ;; If the cl-generic support, we need to be able to check - ;; if a vector is a cl-struct object, without knowing its particular type. - ;; So we use the (otherwise) unused function slots of the tag symbol - ;; to put a special witness value, to make the check easy and reliable. - (unless named (fset tag :quick-object-witness-check)) - (put name 'cl-struct-slots slots) - (put name 'cl-struct-type (list type named)) - (if parent (put name 'cl-struct-include parent)) - (if print-auto (put name 'cl-struct-print print-auto)) - (if docstring (put name 'structure-documentation docstring))) +(eval-when-compile (require 'cl-macs)) ;For cl--struct-class. ;; The `assert' macro from the cl package signals ;; `cl-assertion-failed' at runtime so always define it. @@ -63,6 +49,199 @@ (apply #'error string (append sargs args)) (signal 'cl-assertion-failed `(,form ,@sargs))))) +;; When we load this (compiled) file during pre-loading, the cl--struct-class +;; code below will need to access the `cl-struct' info, since it's considered +;; already as its parent (because `cl-struct' was defined while the file was +;; compiled). So let's temporarily setup a fake. +(defvar cl-struct-cl-structure-object-tags nil) +(unless (cl--find-class 'cl-structure-object) + (setf (cl--find-class 'cl-structure-object) 'dummy)) + +(fset 'cl--make-slot-desc + ;; To break circularity, we pre-define the slot constructor by hand. + ;; It's redefined a bit further down as part of the cl-defstruct of + ;; cl--slot-descriptor. + ;; BEWARE: Obviously, it's important to keep the two in sync! + (lambda (name &optional initform type props) + (vector 'cl-struct-cl-slot-descriptor + name initform type props))) + +(defun cl--struct-get-class (name) + (or (if (not (symbolp name)) name) + (cl--find-class name) + (if (not (get name 'cl-struct-type)) + ;; FIXME: Add a conversion for `eieio--class' so we can + ;; create a cl-defstruct that inherits from an eieio class? + (error "%S is not a struct name" name) + ;; Backward compatibility with a defstruct compiled with a version + ;; cl-defstruct from Emacs<25. Convert to new format. + (let ((tag (intern (format "cl-struct-%s" name))) + (type-and-named (get name 'cl-struct-type)) + (descs (get name 'cl-struct-slots))) + (cl-struct-define name nil (get name 'cl-struct-include) + (unless (and (eq (car type-and-named) 'vector) + (null (cadr type-and-named)) + (assq 'cl-tag-slot descs)) + (car type-and-named)) + (cadr type-and-named) + descs + (intern (format "cl-struct-%s-tags" name)) + tag + (get name 'cl-struct-print)) + (cl--find-class name))))) + +(defun cl--plist-remove (plist member) + (cond + ((null plist) nil) + ((null member) plist) + ((eq plist member) (cddr plist)) + (t `(,(car plist) ,(cadr plist) ,@(cl--plist-remove (cddr plist) member))))) + +(defun cl--struct-register-child (parent tag) + ;; Can't use (cl-typep parent 'cl-structure-class) at this stage + ;; because `cl-structure-class' is defined later. + (while (vectorp parent) + (add-to-list (cl--struct-class-children-sym parent) tag) + ;; Only register ourselves as a child of the leftmost parent since structs + ;; can only only have one parent. + (setq parent (car (cl--struct-class-parents parent))))) + +;;;###autoload +(defun cl-struct-define (name docstring parent type named slots children-sym + tag print) + (cl-assert (or type (not named))) + (if (boundp children-sym) + (add-to-list children-sym tag) + (set children-sym (list tag))) + (and (null type) (eq (caar slots) 'cl-tag-slot) + ;; Hide the tag slot from "standard" (i.e. non-`type'd) structs. + (setq slots (cdr slots))) + (let* ((parent-class (when parent (cl--struct-get-class parent))) + (n (length slots)) + (index-table (make-hash-table :test 'eq :size n)) + (vslots (let ((v (make-vector n nil)) + (i 0) + (offset (if type 0 1))) + (dolist (slot slots) + (let* ((props (cddr slot)) + (typep (plist-member props :type)) + (type (if typep (cadr typep) t))) + (aset v i (cl--make-slot-desc + (car slot) (nth 1 slot) + type (cl--plist-remove props typep)))) + (puthash (car slot) (+ i offset) index-table) + (cl-incf i)) + v)) + (class (cl--struct-new-class + name docstring + (unless (symbolp parent-class) (list parent-class)) + type named vslots index-table children-sym tag print))) + (unless (symbolp parent-class) + (let ((pslots (cl--struct-class-slots parent-class))) + (or (>= n (length pslots)) + (let ((ok t)) + (dotimes (i (length pslots)) + (unless (eq (cl--slot-descriptor-name (aref pslots i)) + (cl--slot-descriptor-name (aref vslots i))) + (setq ok nil))) + ok) + (error "Included struct %S has changed since compilation of %S" + parent name)))) + (cl--struct-register-child parent-class tag) + (unless (eq named t) + (eval `(defconst ,tag ',class) t) + ;; In the cl-generic support, we need to be able to check + ;; if a vector is a cl-struct object, without knowing its particular type. + ;; So we use the (otherwise) unused function slots of the tag symbol + ;; to put a special witness value, to make the check easy and reliable. + (fset tag :quick-object-witness-check)) + (setf (cl--find-class name) class))) + +(cl-defstruct (cl-structure-class + (:conc-name cl--struct-class-) + (:predicate cl--struct-class-p) + (:constructor nil) + (:constructor cl--struct-new-class + (name docstring parents type named slots index-table + children-sym tag print)) + (:copier nil)) + "The type of CL structs descriptors." + ;; The first few fields here are actually inherited from cl--class, but we + ;; have to define this one before, to break the circularity, so we manually + ;; list the fields here and later "backpatch" cl--class as the parent. + ;; BEWARE: Obviously, it's indispensable to keep these two structs in sync! + (name nil :type symbol) ;The type name. + (docstring nil :type string) + (parents nil :type (list-of cl--class)) ;The included struct. + (slots nil :type (vector cl--slot-descriptor)) + (index-table nil :type hash-table) + (tag nil :type symbol) ;Placed in cl-tag-slot. Holds the struct-class object. + (type nil :type (memq (vector list))) + (named nil :type bool) + (print nil :type bool) + (children-sym nil :type symbol) ;This sym's value holds the tags of children. + ) + +(cl-defstruct (cl-structure-object + (:predicate cl-struct-p) + (:constructor nil) + (:copier nil)) + "The root parent of all \"normal\" CL structs") + +(setq cl--struct-default-parent 'cl-structure-object) + +(cl-defstruct (cl-slot-descriptor + (:conc-name cl--slot-descriptor-) + (:constructor nil) + (:constructor cl--make-slot-descriptor + (name &optional initform type props)) + (:copier cl--copy-slot-descriptor)) + ;; FIXME: This is actually not used yet, for circularity reasons! + "Descriptor of structure slot." + name ;Attribute name (symbol). + initform + type + ;; Extra properties, kept in an alist, can include: + ;; :documentation, :protection, :custom, :label, :group, :printer. + (props nil :type alist)) + +(cl-defstruct (cl--class + (:constructor nil) + (:copier nil)) + "Type of descriptors for any kind of structure-like data." + ;; Intended to be shared between defstruct and defclass. + (name nil :type symbol) ;The type name. + (docstring nil :type string) + (parents nil :type (or cl--class (list-of cl--class))) + (slots nil :type (vector cl-slot-descriptor)) + (index-table nil :type hash-table)) + +(cl-assert + (let ((sc-slots (cl--struct-class-slots (cl--find-class 'cl-structure-class))) + (c-slots (cl--struct-class-slots (cl--find-class 'cl--class))) + (eq t)) + (dotimes (i (length c-slots)) + (let ((sc-slot (aref sc-slots i)) + (c-slot (aref c-slots i))) + (unless (eq (cl--slot-descriptor-name sc-slot) + (cl--slot-descriptor-name c-slot)) + (setq eq nil)))) + eq)) + +;; Close the recursion between cl-structure-object and cl-structure-class. +(setf (cl--struct-class-parents (cl--find-class 'cl-structure-class)) + (list (cl--find-class 'cl--class))) +(cl--struct-register-child + (cl--find-class 'cl--class) + (cl--struct-class-tag (cl--find-class 'cl-structure-class))) + +(cl-assert (cl--find-class 'cl-structure-class)) +(cl-assert (cl--find-class 'cl-structure-object)) +(cl-assert (cl-struct-p (cl--find-class 'cl-structure-class))) +(cl-assert (cl-struct-p (cl--find-class 'cl-structure-object))) +(cl-assert (cl--class-p (cl--find-class 'cl-structure-class))) +(cl-assert (cl--class-p (cl--find-class 'cl-structure-object))) + ;; Make sure functions defined with cl-defsubst can be inlined even in ;; packages which do not require CL. We don't put an autoload cookie ;; directly on that function, since those cookies only go to cl-loaddefs. diff --git a/lisp/emacs-lisp/debug.el b/lisp/emacs-lisp/debug.el index 8c1440d02f3..83213285d4e 100644 --- a/lisp/emacs-lisp/debug.el +++ b/lisp/emacs-lisp/debug.el @@ -106,10 +106,10 @@ This is to optimize `debugger-make-xrefs'.") "Non-nil if we expect to get back in the debugger soon.") (defvar inhibit-debug-on-entry nil - "Non-nil means that debug-on-entry is disabled.") + "Non-nil means that `debug-on-entry' is disabled.") (defvar debugger-jumping-flag nil - "Non-nil means that debug-on-entry is disabled. + "Non-nil means that `debug-on-entry' is disabled. This variable is used by `debugger-jump', `debugger-step-through', and `debugger-reenable' to temporarily disable debug-on-entry.") @@ -165,7 +165,6 @@ first will be printed into the backtrace buffer." ;; Don't let these magic variables affect the debugger itself. (let ((last-command nil) this-command track-mouse (inhibit-trace t) - (inhibit-debug-on-entry t) unread-command-events unread-post-input-method-events last-input-event last-command-event last-nonmenu-event @@ -763,7 +762,8 @@ A call to this function is inserted by `debug-on-entry' to cause functions to break on entry." (if (or inhibit-debug-on-entry debugger-jumping-flag) nil - (funcall debugger 'debug))) + (let ((inhibit-debug-on-entry t)) + (funcall debugger 'debug)))) ;;;###autoload (defun debug-on-entry (function) diff --git a/lisp/emacs-lisp/macroexp.el b/lisp/emacs-lisp/macroexp.el index 68bf4f62c34..f0410f87447 100644 --- a/lisp/emacs-lisp/macroexp.el +++ b/lisp/emacs-lisp/macroexp.el @@ -465,6 +465,8 @@ itself or not." (defvar macroexp--pending-eager-loads nil "Stack of files currently undergoing eager macro-expansion.") +(defvar macroexp--debug-eager nil) + (defun internal-macroexpand-for-load (form full-p) ;; Called from the eager-macroexpansion in readevalloop. (cond @@ -480,8 +482,10 @@ itself or not." (tail (member elem (cdr (member elem bt))))) (if tail (setcdr tail (list '…))) (if (eq (car-safe (car bt)) 'macroexpand-all) (setq bt (cdr bt))) - (message "Warning: Eager macro-expansion skipped due to cycle:\n %s" - (mapconcat #'prin1-to-string (nreverse bt) " => ")) + (if macroexp--debug-eager + (debug 'eager-macroexp-cycle) + (message "Warning: Eager macro-expansion skipped due to cycle:\n %s" + (mapconcat #'prin1-to-string (nreverse bt) " => "))) (push 'skip macroexp--pending-eager-loads) form)) (t diff --git a/lwlib/ChangeLog b/lwlib/ChangeLog index c98d72575a8..e5dfed2342a 100644 --- a/lwlib/ChangeLog +++ b/lwlib/ChangeLog @@ -1,3 +1,7 @@ +2015-03-18 Stefan Monnier + + * xlwmenu.c (pop_up_menu): Remove debugging code. + 2015-02-28 Jan Djärv * xlwmenu.c (remap_menubar): Re-realize menu to force move under diff --git a/lwlib/xlwmenu.c b/lwlib/xlwmenu.c index f781b7ee54c..9317dea02b0 100644 --- a/lwlib/xlwmenu.c +++ b/lwlib/xlwmenu.c @@ -1719,7 +1719,7 @@ make_shadow_gcs (XlwMenuWidget mw) 1.2, 0x8000)) #else XQueryColor (dpy, cmap, &topc); - /* don't overflow/wrap! */ + /* Don't overflow/wrap! */ topc.red = MINL (65535, topc.red * 1.2); topc.green = MINL (65535, topc.green * 1.2); topc.blue = MINL (65535, topc.blue * 1.2); @@ -1780,8 +1780,8 @@ make_shadow_gcs (XlwMenuWidget mw) } } - if (!mw->menu.top_shadow_pixmap && - mw->menu.top_shadow_color == mw->core.background_pixel) + if (!mw->menu.top_shadow_pixmap + && mw->menu.top_shadow_color == mw->core.background_pixel) { mw->menu.top_shadow_pixmap = mw->menu.gray_pixmap; if (mw->menu.free_top_shadow_color_p) @@ -1791,8 +1791,8 @@ make_shadow_gcs (XlwMenuWidget mw) } mw->menu.top_shadow_color = mw->menu.foreground; } - if (!mw->menu.bottom_shadow_pixmap && - mw->menu.bottom_shadow_color == mw->core.background_pixel) + if (!mw->menu.bottom_shadow_pixmap + && mw->menu.bottom_shadow_color == mw->core.background_pixel) { mw->menu.bottom_shadow_pixmap = mw->menu.gray_pixmap; if (mw->menu.free_bottom_shadow_color_p) @@ -1856,7 +1856,7 @@ openXftFont (XlwMenuWidget mw) if (fname && strcmp (fname, "none") != 0) { int screen = XScreenNumberOfScreen (mw->core.screen); - int len = strlen (fname), i = len-1; + int len = strlen (fname), i = len - 1; /* Try to convert Gtk-syntax (Sans 9) to Xft syntax Sans-9. */ while (i > 0 && '0' <= fname[i] && fname[i] <= '9') --i; @@ -1880,7 +1880,7 @@ openXftFont (XlwMenuWidget mw) static void XlwMenuInitialize (Widget request, Widget w, ArgList args, Cardinal *num_args) { - /* Get the GCs and the widget size */ + /* Get the GCs and the widget size. */ XlwMenuWidget mw = (XlwMenuWidget) w; Window window = RootWindowOfScreen (DefaultScreenOfDisplay (XtDisplay (mw))); Display* display = XtDisplay (mw); @@ -2014,7 +2014,7 @@ XlwMenuRealize (Widget w, Mask *valueMask, XSetWindowAttributes *attributes) /* Only the toplevel menubar/popup is a widget so it's the only one that receives expose events through Xt. So we repaint all the other panes - when receiving an Expose event. */ + when receiving an Expose event. */ static void XlwMenuRedisplay (Widget w, XEvent *ev, Region region) { @@ -2056,14 +2056,14 @@ XlwMenuDestroy (Widget w) release_drawing_gcs (mw); release_shadow_gcs (mw); - /* this doesn't come from the resource db but is created explicitly - so we must free it ourselves. */ + /* This doesn't come from the resource db but is created explicitly + so we must free it ourselves. */ XFreePixmap (XtDisplay (mw), mw->menu.gray_pixmap); mw->menu.gray_pixmap = (Pixmap) -1; /* Don't free mw->menu.contents because that comes from our creator. The `*_stack' elements are just pointers into `contents' so leave - that alone too. But free the stacks themselves. */ + that alone too. But free the stacks themselves. */ if (mw->menu.old_stack) XtFree ((char *) mw->menu.old_stack); if (mw->menu.new_stack) XtFree ((char *) mw->menu.new_stack); @@ -2093,7 +2093,7 @@ XlwMenuDestroy (Widget w) if (mw->menu.windows [0].pixmap != None) XFreePixmap (XtDisplay (mw), mw->menu.windows [0].pixmap); - /* start from 1 because the one in slot 0 is w->core.window */ + /* Start from 1 because the one in slot 0 is w->core.window. */ for (i = 1; i < mw->menu.windows_length; i++) { if (mw->menu.windows [i].pixmap != None) @@ -2170,7 +2170,7 @@ XlwMenuSetValues (Widget current, Widget request, Widget new, XSetWindowBackground (XtDisplay (oldmw), oldmw->menu.windows [i].window, newmw->core.background_pixel); - /* clear windows and generate expose events */ + /* Clear windows and generate expose events. */ XClearArea (XtDisplay (oldmw), oldmw->menu.windows[i].window, 0, 0, 0, 0, True); } @@ -2244,7 +2244,7 @@ handle_single_motion_event (XlwMenuWidget mw, XMotionEvent *ev) set_new_state (mw, val, level); remap_menubar (mw); - /* Sync with the display. Makes it feel better on X terms. */ + /* Sync with the display. Makes it feel better on X terms. */ XSync (XtDisplay (mw), False); } @@ -2256,7 +2256,7 @@ handle_motion_event (XlwMenuWidget mw, XMotionEvent *ev) int state = ev->state; XMotionEvent oldev = *ev; - /* allow motion events to be generated again */ + /* Allow motion events to be generated again. */ if (ev->is_hint && XQueryPointer (XtDisplay (mw), ev->window, &ev->root, &ev->subwindow, @@ -2293,11 +2293,11 @@ Start (Widget w, XEvent *ev, String *params, Cardinal *num_params) releasing the button should always pop the menu down. */ next_release_must_exit = 1; - /* notes the absolute position of the menubar window */ + /* Notes the absolute position of the menubar window. */ mw->menu.windows [0].x = ev->xmotion.x_root - ev->xmotion.x; mw->menu.windows [0].y = ev->xmotion.y_root - ev->xmotion.y; - /* handles the down like a move, slots are compatible */ + /* Handles the down like a move, slots are compatible. */ ev->xmotion.is_hint = 0; handle_motion_event (mw, &ev->xmotion); } @@ -2327,7 +2327,7 @@ find_first_selectable (XlwMenuWidget mw, widget_value *item, int skip_titles) while (lw_separator_p (current->name, &separator, 0) || !current->enabled || (skip_titles && !current->call_data && !current->contents)) if (current->next) - current=current->next; + current = current->next; else return NULL; @@ -2340,9 +2340,9 @@ find_next_selectable (XlwMenuWidget mw, widget_value *item, int skip_titles) widget_value *current = item; enum menu_separator separator; - while (current->next && (current=current->next) && - (lw_separator_p (current->name, &separator, 0) || !current->enabled - || (skip_titles && !current->call_data && !current->contents))) + while (current->next && (current = current->next) + && (lw_separator_p (current->name, &separator, 0) || !current->enabled + || (skip_titles && !current->call_data && !current->contents))) ; if (current == item) @@ -2357,7 +2357,7 @@ find_next_selectable (XlwMenuWidget mw, widget_value *item, int skip_titles) && !current->contents)) { if (current->next) - current=current->next; + current = current->next; if (current == item) break; @@ -2374,12 +2374,12 @@ find_prev_selectable (XlwMenuWidget mw, widget_value *item, int skip_titles) widget_value *current = item; widget_value *prev = item; - while ((current=find_next_selectable (mw, current, skip_titles)) + while ((current = find_next_selectable (mw, current, skip_titles)) != item) { if (prev == current) break; - prev=current; + prev = current; } return prev; @@ -2560,7 +2560,7 @@ Select (Widget w, XEvent *ev, String *params, Cardinal *num_params) < XtGetMultiClickTime (XtDisplay (w)))) return; - /* pop down everything. */ + /* Pop down everything. */ mw->menu.new_depth = 1; remap_menubar (mw); @@ -2582,7 +2582,7 @@ Select (Widget w, XEvent *ev, String *params, Cardinal *num_params) } - /* Special code to pop-up a menu */ + /* Special code to pop-up a menu. */ static void pop_up_menu (XlwMenuWidget mw, XButtonPressedEvent *event) { @@ -2619,14 +2619,14 @@ pop_up_menu (XlwMenuWidget mw, XButtonPressedEvent *event) mw->menu.popped_up = True; if (XtIsShell (XtParent ((Widget)mw))) { - fprintf(stderr, "Config %d %d\n", x, y); + /* fprintf (stderr, "Config %d %d\n", x, y); */ XtConfigureWidget (XtParent ((Widget)mw), x, y, w, h, XtParent ((Widget)mw)->core.border_width); XtPopup (XtParent ((Widget)mw), XtGrabExclusive); display_menu (mw, 0, False, NULL, NULL, NULL); mw->menu.windows [0].x = x + borderwidth; mw->menu.windows [0].y = y + borderwidth; - mw->menu.top_depth = 1; /* Popup menus don't have a bar so top is 1 */ + mw->menu.top_depth = 1; /* Popup menus don't have a bar so top is 1. */ } else { @@ -2634,7 +2634,7 @@ pop_up_menu (XlwMenuWidget mw, XButtonPressedEvent *event) XtAddGrab ((Widget) mw, True, True); - /* notes the absolute position of the menubar window */ + /* Notes the absolute position of the menubar window. */ mw->menu.windows [0].x = ev->xmotion.x_root - ev->xmotion.x; mw->menu.windows [0].y = ev->xmotion.y_root - ev->xmotion.y; mw->menu.top_depth = 2; diff --git a/src/ChangeLog b/src/ChangeLog index fbf8fb452fc..1b1a9c59033 100644 --- a/src/ChangeLog +++ b/src/ChangeLog @@ -1,3 +1,7 @@ +2015-03-18 Stefan Monnier + + * alloc.c (purecopy): Handle hash-tables. + 2015-03-16 Stefan Monnier * minibuf.c (Fread_buffer): Add `predicate' argument. @@ -6,13 +10,11 @@ 2015-03-15 Eli Zaretskii * xdisp.c (handle_invisible_prop): Fix up it->position even when - we are going to load overlays at the beginning of the invisible - text. + we are going to load overlays at the beginning of the invisible text. (setup_for_ellipsis): Reset the ignore_overlay_strings_at_pos_p flag also here. (next_overlay_string): Set the overlay_strings_at_end_processed_p - flag only if the overlays just processed were actually loaded at - EOB. + flag only if the overlays just processed were actually loaded at EOB. 2015-03-14 Daniel Colascione @@ -183,8 +185,8 @@ 2015-02-28 Martin Rudalics - * frame.c (make_initial_frame, Fmake_terminal_frame): Set - can_x_set_window_size and after_make_frame (Bug#19962). + * frame.c (make_initial_frame, Fmake_terminal_frame): + Set can_x_set_window_size and after_make_frame (Bug#19962). 2015-02-28 Eli Zaretskii @@ -454,8 +456,8 @@ * indent.c (Fvertical_motion): Accept an additional argument CUR-COL and use it as the starting screen coordinate. - * window.c (window_scroll_line_based, Fmove_to_window_line): All - callers of vertical-motion changed. + * window.c (window_scroll_line_based, Fmove_to_window_line): + All callers of vertical-motion changed. 2015-02-09 Dima Kogan diff --git a/src/alloc.c b/src/alloc.c index 022782504f1..1f4b1a4694e 100644 --- a/src/alloc.c +++ b/src/alloc.c @@ -3423,7 +3423,7 @@ union aligned_Lisp_Misc }; /* Allocation of markers and other objects that share that structure. - Works like allocation of conses. */ + Works like allocation of conses. */ #define MARKER_BLOCK_SIZE \ ((1020 - sizeof (struct marker_block *)) / sizeof (union aligned_Lisp_Misc)) @@ -4744,7 +4744,7 @@ mark_maybe_pointer (void *p) #endif /* Mark Lisp objects referenced from the address range START+OFFSET..END - or END+OFFSET..START. */ + or END+OFFSET..START. */ static void ATTRIBUTE_NO_SANITIZE_ADDRESS mark_memory (void *start, void *end) @@ -5356,7 +5356,6 @@ make_pure_vector (ptrdiff_t len) return new; } - DEFUN ("purecopy", Fpurecopy, Spurecopy, 1, 1, 0, doc: /* Make a copy of object OBJ in pure storage. Recursively copies contents of vectors and cons cells. @@ -5391,28 +5390,26 @@ purecopy (Lisp_Object obj) else if (FLOATP (obj)) obj = make_pure_float (XFLOAT_DATA (obj)); else if (STRINGP (obj)) - obj = make_pure_string (SSDATA (obj), SCHARS (obj), - SBYTES (obj), - STRING_MULTIBYTE (obj)); - else if (COMPILEDP (obj) || VECTORP (obj)) { - register struct Lisp_Vector *vec; + if (XSTRING (obj)->intervals) + message ("Dropping text-properties when making string pure"); + obj = make_pure_string (SSDATA (obj), SCHARS (obj), + SBYTES (obj), + STRING_MULTIBYTE (obj)); + } + else if (COMPILEDP (obj) || VECTORP (obj) || HASH_TABLE_P (obj)) + { + struct Lisp_Vector *objp = XVECTOR (obj); + ptrdiff_t nbytes = vector_nbytes (objp); + struct Lisp_Vector *vec = pure_alloc (nbytes, Lisp_Vectorlike); register ptrdiff_t i; - ptrdiff_t size; - - size = ASIZE (obj); + ptrdiff_t size = ASIZE (obj); if (size & PSEUDOVECTOR_FLAG) size &= PSEUDOVECTOR_SIZE_MASK; - vec = XVECTOR (make_pure_vector (size)); + memcpy (vec, objp, nbytes); for (i = 0; i < size; i++) - vec->contents[i] = purecopy (AREF (obj, i)); - if (COMPILEDP (obj)) - { - XSETPVECTYPE (vec, PVEC_COMPILED); - XSETCOMPILED (obj, vec); - } - else - XSETVECTOR (obj, vec); + vec->contents[i] = purecopy (vec->contents[i]); + XSETVECTOR (obj, vec); } else if (SYMBOLP (obj)) { @@ -5422,6 +5419,7 @@ purecopy (Lisp_Object obj) XSYMBOL (obj)->pinned = true; symbol_block_pinned = symbol_block; } + /* Don't hash-cons it. */ return obj; } else @@ -6229,13 +6227,14 @@ mark_discard_killed_buffers (Lisp_Object list) void mark_object (Lisp_Object arg) { - register Lisp_Object obj = arg; + register Lisp_Object obj; void *po; #ifdef GC_CHECK_MARKED_OBJECTS struct mem_node *m; #endif ptrdiff_t cdr_count = 0; + obj = arg; loop: po = XPNTR (obj); @@ -6870,7 +6869,7 @@ sweep_symbols (void) total_free_symbols = num_free; } -NO_INLINE /* For better stack traces */ +NO_INLINE /* For better stack traces. */ static void sweep_misc (void) { -- cgit v1.2.3 From 8dfff871bdf0e420c6f5570e72afc80471d40d51 Mon Sep 17 00:00:00 2001 From: Stefan Monnier Date: Wed, 18 Mar 2015 10:49:55 -0400 Subject: * cl-generic.el (cl-generic-generalizers): Clean up after braindamage --- lisp/emacs-lisp/cl-generic.el | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'lisp/emacs-lisp/cl-generic.el') diff --git a/lisp/emacs-lisp/cl-generic.el b/lisp/emacs-lisp/cl-generic.el index c9ca92d7c09..fb11a3e25a1 100644 --- a/lisp/emacs-lisp/cl-generic.el +++ b/lisp/emacs-lisp/cl-generic.el @@ -903,8 +903,8 @@ Can only be used from within the lexical body of a primary or around method." ;; take place without requiring cl-lib. (let ((class (cl--find-class type))) (and (cl-typep class 'cl-structure-class) - (when (cl--struct-class-type class) - (error "Can't dispatch on cl-struct %S: type is %S" + (or (null (cl--struct-class-type class)) + (error "Can't dispatch on cl-struct %S: type is %S" type (cl--struct-class-type class))) (progn (cl-assert (null (cl--struct-class-named class))) t) (list cl--generic-struct-generalizer)))) -- cgit v1.2.3