summaryrefslogtreecommitdiff
path: root/lisp/emacs-lisp
diff options
context:
space:
mode:
authorAndrea Corallo <akrl@sdf.org>2021-02-10 21:56:55 +0100
committerAndrea Corallo <akrl@sdf.org>2021-02-10 21:56:55 +0100
commit2fcb85c3e780f1f2871ce0f300cfaffce9836eb0 (patch)
treea8857ccad8bff12080062a3edaad1a55a3eb8171 /lisp/emacs-lisp
parent1f626e9662d8120acd5a937f847123cc2b8c6e31 (diff)
parent6bfdfeed36fab4680c8db90c22da8f6611694186 (diff)
downloademacs-2fcb85c3e780f1f2871ce0f300cfaffce9836eb0.tar.gz
emacs-2fcb85c3e780f1f2871ce0f300cfaffce9836eb0.tar.bz2
emacs-2fcb85c3e780f1f2871ce0f300cfaffce9836eb0.zip
Merge remote-tracking branch 'savannah/master' into HEAD
Diffstat (limited to 'lisp/emacs-lisp')
-rw-r--r--lisp/emacs-lisp/byte-opt.el439
-rw-r--r--lisp/emacs-lisp/bytecomp.el3
-rw-r--r--lisp/emacs-lisp/chart.el3
-rw-r--r--lisp/emacs-lisp/checkdoc.el7
-rw-r--r--lisp/emacs-lisp/easy-mmode.el12
-rw-r--r--lisp/emacs-lisp/edebug.el81
-rw-r--r--lisp/emacs-lisp/elp.el3
-rw-r--r--lisp/emacs-lisp/generator.el21
-rw-r--r--lisp/emacs-lisp/generic.el3
-rw-r--r--lisp/emacs-lisp/lisp-mnt.el2
-rw-r--r--lisp/emacs-lisp/lisp-mode.el25
-rw-r--r--lisp/emacs-lisp/macroexp.el71
-rw-r--r--lisp/emacs-lisp/package-x.el3
-rw-r--r--lisp/emacs-lisp/pcase.el19
-rw-r--r--lisp/emacs-lisp/pp.el5
-rw-r--r--lisp/emacs-lisp/re-builder.el10
-rw-r--r--lisp/emacs-lisp/syntax.el3
-rw-r--r--lisp/emacs-lisp/tcover-ses.el2
-rw-r--r--lisp/emacs-lisp/testcover.el52
19 files changed, 482 insertions, 282 deletions
diff --git a/lisp/emacs-lisp/byte-opt.el b/lisp/emacs-lisp/byte-opt.el
index 66a117fccc8..8851f0ef32d 100644
--- a/lisp/emacs-lisp/byte-opt.el
+++ b/lisp/emacs-lisp/byte-opt.el
@@ -289,7 +289,7 @@
(byte-compile-preprocess
(byte-compile--reify-function fn))))))
(if (eq (car-safe newfn) 'function)
- (byte-compile-unfold-lambda `(,(cadr newfn) ,@(cdr form)))
+ (macroexp--unfold-lambda `(,(cadr newfn) ,@(cdr form)))
;; This can happen because of macroexp-warn-and-return &co.
(byte-compile-warn
"Inlining closure %S failed" name)
@@ -297,77 +297,91 @@
(_ ;; Give up on inlining.
form))))
-
-;; ((lambda ...) ...)
-(defun byte-compile-unfold-lambda (form &optional name)
- ;; In lexical-binding mode, let and functions don't bind vars in the same way
- ;; (let obey special-variable-p, but functions don't). But luckily, this
- ;; doesn't matter here, because function's behavior is underspecified so it
- ;; can safely be turned into a `let', even though the reverse is not true.
- (or name (setq name "anonymous lambda"))
- (let* ((lambda (car form))
- (values (cdr form))
- (arglist (nth 1 lambda))
- (body (cdr (cdr lambda)))
- optionalp restp
- bindings)
- (if (and (stringp (car body)) (cdr body))
- (setq body (cdr body)))
- (if (and (consp (car body)) (eq 'interactive (car (car body))))
- (setq body (cdr body)))
- ;; FIXME: The checks below do not belong in an optimization phase.
- (while arglist
- (cond ((eq (car arglist) '&optional)
- ;; ok, I'll let this slide because funcall_lambda() does...
- ;; (if optionalp (error "multiple &optional keywords in %s" name))
- (if restp (error "&optional found after &rest in %s" name))
- (if (null (cdr arglist))
- (error "nothing after &optional in %s" name))
- (setq optionalp t))
- ((eq (car arglist) '&rest)
- ;; ...but it is by no stretch of the imagination a reasonable
- ;; thing that funcall_lambda() allows (&rest x y) and
- ;; (&rest x &optional y) in arglists.
- (if (null (cdr arglist))
- (error "nothing after &rest in %s" name))
- (if (cdr (cdr arglist))
- (error "multiple vars after &rest in %s" name))
- (setq restp t))
- (restp
- (setq bindings (cons (list (car arglist)
- (and values (cons 'list values)))
- bindings)
- values nil))
- ((and (not optionalp) (null values))
- (byte-compile-warn "attempt to open-code `%s' with too few arguments" name)
- (setq arglist nil values 'too-few))
- (t
- (setq bindings (cons (list (car arglist) (car values))
- bindings)
- values (cdr values))))
- (setq arglist (cdr arglist)))
- (if values
- (progn
- (or (eq values 'too-few)
- (byte-compile-warn
- "attempt to open-code `%s' with too many arguments" name))
- form)
-
- ;; The following leads to infinite recursion when loading a
- ;; file containing `(defsubst f () (f))', and then trying to
- ;; byte-compile that file.
- ;(setq body (mapcar 'byte-optimize-form body)))
-
- (let ((newform
- (if bindings
- (cons 'let (cons (nreverse bindings) body))
- (cons 'progn body))))
- (byte-compile-log " %s\t==>\t%s" form newform)
- newform))))
-
;;; implementing source-level optimizers
+(defconst byte-optimize-enable-variable-constprop t
+ "If non-nil, enable constant propagation through local variables.")
+
+(defconst byte-optimize-warn-eliminated-variable nil
+ "Whether to warn when a variable is optimised away entirely.
+This does usually not indicate a problem and makes the compiler
+very chatty, but can be useful for debugging.")
+
+(defvar byte-optimize--lexvars nil
+ "Lexical variables in scope, in reverse order of declaration.
+Each element is on the form (NAME KEEP [VALUE]), where:
+ NAME is the variable name,
+ KEEP is a boolean indicating whether the binding must be retained,
+ VALUE, if present, is a substitutable expression.
+Earlier variables shadow later ones with the same name.")
+
+(defvar byte-optimize--vars-outside-condition nil
+ "Alist of variables lexically bound outside conditionally executed code.
+Variables here are sensitive to mutation inside the conditional code,
+since their contents in sequentially later code depends on the path taken
+and may no longer be statically known.
+Same format as `byte-optimize--lexvars', with shared structure and contents.")
+
+(defvar byte-optimize--vars-outside-loop nil
+ "Alist of variables lexically bound outside the innermost `while' loop.
+Variables here are sensitive to mutation inside the loop, since this can
+occur an indeterminate number of times and thus have effect on code
+sequentially preceding the mutation itself.
+Same format as `byte-optimize--lexvars', with shared structure and contents.")
+
+(defvar byte-optimize--dynamic-vars nil
+ "List of variables declared as dynamic during optimisation.")
+
+(defun byte-optimize--substitutable-p (expr)
+ "Whether EXPR is a constant that can be propagated."
+ ;; Only consider numbers, symbols and strings to be values for substitution
+ ;; purposes. Numbers and symbols are immutable, and mutating string
+ ;; literals (or results from constant-evaluated string-returning functions)
+ ;; can be considered undefined.
+ ;; (What about other quoted values, like conses?)
+ (or (booleanp expr)
+ (numberp expr)
+ (stringp expr)
+ (and (consp expr)
+ (eq (car expr) 'quote)
+ (symbolp (cadr expr)))
+ (keywordp expr)))
+
+(defmacro byte-optimize--pcase (exp &rest cases)
+ ;; When we do
+ ;;
+ ;; (pcase EXP
+ ;; (`(if ,exp ,then ,else) (DO-TEST))
+ ;; (`(plus ,e2 ,e2) (DO-ADD))
+ ;; (`(times ,e2 ,e2) (DO-MULT))
+ ;; ...)
+ ;;
+ ;; we usually don't want to fall back to the default case if
+ ;; the value of EXP is of a form like `(if E1 E2)' or `(plus E1)'
+ ;; or `(times E1 E2 E3)', instead we either want to signal an error
+ ;; that EXP has an unexpected shape, or we want to carry on as if
+ ;; it had the right shape (ignore the extra data and pretend the missing
+ ;; data is nil) because it should simply never happen.
+ ;;
+ ;; The macro below implements the second option by rewriting patterns
+ ;; like `(if ,exp ,then ,else)'
+ ;; to `(if . (or `(,exp ,then ,else) pcase--dontcare))'.
+ ;;
+ ;; The resulting macroexpansion is also significantly cleaner/smaller/faster.
+ (declare (indent 1) (debug (form &rest (pcase-PAT body))))
+ `(pcase ,exp
+ . ,(mapcar (lambda (case)
+ `(,(pcase (car case)
+ ((and `(,'\` (,_ . (,'\, ,_))) pat) pat)
+ (`(,'\` (,head . ,tail))
+ (list '\`
+ (cons head
+ (list '\, `(or ,(list '\` tail) pcase--dontcare)))))
+ (pat pat))
+ . ,(cdr case)))
+ cases)))
+
(defun byte-optimize-form-code-walker (form for-effect)
;;
;; For normal function calls, We can just mapcar the optimizer the cdr. But
@@ -380,13 +394,26 @@
;; have no place in an optimizer: the corresponding tests should be
;; performed in `macroexpand-all', or in `cconv', or in `bytecomp'.
(let ((fn (car-safe form)))
- (pcase form
+ (byte-optimize--pcase form
((pred (not consp))
- (if (not (and for-effect
- (or byte-compile-delete-errors
- (not (symbolp form))
- (eq form t))))
- form))
+ (cond
+ ((and for-effect
+ (or byte-compile-delete-errors
+ (not (symbolp form))
+ (eq form t)))
+ nil)
+ ((symbolp form)
+ (let ((lexvar (assq form byte-optimize--lexvars)))
+ (if (cddr lexvar) ; Value available?
+ (if (assq form byte-optimize--vars-outside-loop)
+ ;; Cannot substitute; mark for retention to avoid the
+ ;; variable being eliminated.
+ (progn
+ (setcar (cdr lexvar) t)
+ form)
+ (caddr lexvar)) ; variable value to use
+ form)))
+ (t form)))
(`(quote . ,v)
(if (cdr v)
(byte-compile-warn "malformed quote form: `%s'"
@@ -396,39 +423,28 @@
(and (car v)
(not for-effect)
form))
- (`(,(or 'let 'let*) . ,(or `(,bindings . ,exps) pcase--dontcare))
- ;; Recursively enter the optimizer for the bindings and body
- ;; of a let or let*. This for depth-firstness: forms that
- ;; are more deeply nested are optimized first.
- (cons fn
- (cons
- (mapcar (lambda (binding)
- (if (symbolp binding)
- binding
- (if (cdr (cdr binding))
- (byte-compile-warn "malformed let binding: `%s'"
- (prin1-to-string binding)))
- (list (car binding)
- (byte-optimize-form (nth 1 binding) nil))))
- bindings)
- (byte-optimize-body exps for-effect))))
+ (`(,(or 'let 'let*) . ,rest)
+ (cons fn (byte-optimize-let-form fn rest for-effect)))
(`(cond . ,clauses)
- (cons fn
- (mapcar (lambda (clause)
- (if (consp clause)
- (cons
- (byte-optimize-form (car clause) nil)
- (byte-optimize-body (cdr clause) for-effect))
- (byte-compile-warn "malformed cond form: `%s'"
- (prin1-to-string clause))
- clause))
- clauses)))
+ ;; The condition in the first clause is always executed, but
+ ;; right now we treat all of them as conditional for simplicity.
+ (let ((byte-optimize--vars-outside-condition byte-optimize--lexvars))
+ (cons fn
+ (mapcar (lambda (clause)
+ (if (consp clause)
+ (cons
+ (byte-optimize-form (car clause) nil)
+ (byte-optimize-body (cdr clause) for-effect))
+ (byte-compile-warn "malformed cond form: `%s'"
+ (prin1-to-string clause))
+ clause))
+ clauses))))
(`(progn . ,exps)
;; As an extra added bonus, this simplifies (progn <x>) --> <x>.
(if (cdr exps)
(macroexp-progn (byte-optimize-body exps for-effect))
(byte-optimize-form (car exps) for-effect)))
- (`(prog1 . ,(or `(,exp . ,exps) pcase--dontcare))
+ (`(prog1 ,exp . ,exps)
(if exps
`(prog1 ,(byte-optimize-form exp for-effect)
. ,(byte-optimize-body exps t))
@@ -442,37 +458,54 @@
(cons fn (byte-optimize-body exps for-effect)))
(`(if ,test ,then . ,else)
- `(if ,(byte-optimize-form test nil)
- ,(byte-optimize-form then for-effect)
- . ,(byte-optimize-body else for-effect)))
- (`(if . ,_)
- (byte-compile-warn "too few arguments for `if'"))
+ ;; The test is always executed.
+ (let* ((test-opt (byte-optimize-form test nil))
+ ;; The THEN and ELSE branches are executed conditionally.
+ ;;
+ ;; FIXME: We are conservative here: any variable changed in the
+ ;; THEN branch will be barred from substitution in the ELSE
+ ;; branch, despite the branches being mutually exclusive.
+ (byte-optimize--vars-outside-condition byte-optimize--lexvars)
+ (then-opt (byte-optimize-form then for-effect))
+ (else-opt (byte-optimize-body else for-effect)))
+ `(if ,test-opt ,then-opt . ,else-opt)))
(`(,(or 'and 'or) . ,exps) ; Remember, and/or are control structures.
- ;; Take forms off the back until we can't any more.
- ;; In the future it could conceivably be a problem that the
- ;; subexpressions of these forms are optimized in the reverse
- ;; order, but it's ok for now.
- (if for-effect
- (let ((backwards (reverse exps)))
- (while (and backwards
- (null (setcar backwards
- (byte-optimize-form (car backwards)
- for-effect))))
- (setq backwards (cdr backwards)))
- (if (and exps (null backwards))
- (byte-compile-log
- " all subforms of %s called for effect; deleted" form))
- (and backwards
- (cons fn (nreverse (mapcar #'byte-optimize-form
- backwards)))))
- (cons fn (mapcar #'byte-optimize-form exps))))
+ ;; FIXME: We have to traverse the expressions in left-to-right
+ ;; order (because that is the order of evaluation and variable
+ ;; mutations must be found prior to their use), but doing so we miss
+ ;; some optimisation opportunities:
+ ;; consider (and A B) in a for-effect context, where B => nil.
+ ;; Then A could be optimised in a for-effect context too.
+ (let ((tail exps)
+ (args nil))
+ (when tail
+ ;; The first argument is always unconditional.
+ (push (byte-optimize-form
+ (car tail) (and for-effect (null (cdr tail))))
+ args)
+ (setq tail (cdr tail))
+ ;; Remaining arguments are conditional.
+ (let ((byte-optimize--vars-outside-condition byte-optimize--lexvars))
+ (while tail
+ (push (byte-optimize-form
+ (car tail) (and for-effect (null (cdr tail))))
+ args)
+ (setq tail (cdr tail)))))
+ (cons fn (nreverse args))))
(`(while ,exp . ,exps)
- `(while ,(byte-optimize-form exp nil)
- . ,(byte-optimize-body exps t)))
- (`(while . ,_)
- (byte-compile-warn "too few arguments for `while'"))
+ ;; FIXME: We conservatively prevent the substitution of any variable
+ ;; bound outside the loop in case it is mutated later in the loop,
+ ;; but this misses many opportunities: variables not mutated in the
+ ;; loop at all, and variables affecting the initial condition (which
+ ;; is always executed unconditionally).
+ (let* ((byte-optimize--vars-outside-condition byte-optimize--lexvars)
+ (byte-optimize--vars-outside-loop byte-optimize--lexvars)
+ (condition (byte-optimize-form exp nil))
+ (body (byte-optimize-body exps t)))
+ `(while ,condition . ,body)))
+
(`(interactive . ,_)
(byte-compile-warn "misplaced interactive spec: `%s'"
@@ -484,25 +517,36 @@
;; all the subexpressions and compiling them separately.
form)
- (`(condition-case . ,(or `(,var ,exp . ,clauses) pcase--dontcare))
- `(condition-case ,var ;Not evaluated.
- ,(byte-optimize-form exp for-effect)
- ,@(mapcar (lambda (clause)
- `(,(car clause)
- ,@(byte-optimize-body (cdr clause) for-effect)))
- clauses)))
-
- (`(unwind-protect . ,(or `(,exp . ,exps) pcase--dontcare))
- ;; The "protected" part of an unwind-protect is compiled (and thus
- ;; optimized) as a top-level form, so don't do it here. But the
- ;; non-protected part has the same for-effect status as the
- ;; unwind-protect itself. (The protected part is always for effect,
+ (`(condition-case ,var ,exp . ,clauses)
+ (let ((byte-optimize--vars-outside-condition byte-optimize--lexvars))
+ `(condition-case ,var ;Not evaluated.
+ ,(byte-optimize-form exp for-effect)
+ ,@(mapcar (lambda (clause)
+ `(,(car clause)
+ ,@(byte-optimize-body (cdr clause) for-effect)))
+ clauses))))
+
+ (`(unwind-protect ,exp . ,exps)
+ ;; The unwinding part of an unwind-protect is compiled (and thus
+ ;; optimized) as a top-level form, but run the optimizer for it here
+ ;; anyway for lexical variable usage and substitution. But the
+ ;; protected part has the same for-effect status as the
+ ;; unwind-protect itself. (The unwinding part is always for effect,
;; but that isn't handled properly yet.)
- `(unwind-protect ,(byte-optimize-form exp for-effect) . ,exps))
-
- (`(catch . ,(or `(,tag . ,exps) pcase--dontcare))
- `(catch ,(byte-optimize-form tag nil)
- . ,(byte-optimize-body exps for-effect)))
+ (let* ((byte-optimize--vars-outside-condition byte-optimize--lexvars)
+ (bodyform (byte-optimize-form exp for-effect)))
+ (pcase exps
+ (`(:fun-body ,f)
+ `(unwind-protect ,bodyform
+ :fun-body ,(byte-optimize-form f nil)))
+ (_
+ `(unwind-protect ,bodyform
+ . ,(byte-optimize-body exps t))))))
+
+ (`(catch ,tag . ,exps)
+ (let ((byte-optimize--vars-outside-condition byte-optimize--lexvars))
+ `(catch ,(byte-optimize-form tag nil)
+ . ,(byte-optimize-body exps for-effect))))
(`(ignore . ,exps)
;; Don't treat the args to `ignore' as being
@@ -512,10 +556,17 @@
`(prog1 nil . ,(mapcar #'byte-optimize-form exps)))
;; Needed as long as we run byte-optimize-form after cconv.
- (`(internal-make-closure . ,_) form)
+ (`(internal-make-closure . ,_)
+ ;; Look up free vars and mark them to be kept, so that they
+ ;; won't be optimised away.
+ (dolist (var (caddr form))
+ (let ((lexvar (assq var byte-optimize--lexvars)))
+ (when lexvar
+ (setcar (cdr lexvar) t))))
+ form)
(`((lambda . ,_) . ,_)
- (let ((newform (byte-compile-unfold-lambda form)))
+ (let ((newform (macroexp--unfold-lambda form)))
(if (eq newform form)
;; Some error occurred, avoid infinite recursion.
form
@@ -525,6 +576,36 @@
;; is a *value* and shouldn't appear in the car.
(`((closure . ,_) . ,_) form)
+ (`(setq . ,args)
+ (let ((var-expr-list nil))
+ (while args
+ (unless (and (consp args)
+ (symbolp (car args)) (consp (cdr args)))
+ (byte-compile-warn "malformed setq form: %S" form))
+ (let* ((var (car args))
+ (expr (cadr args))
+ (lexvar (assq var byte-optimize--lexvars))
+ (value (byte-optimize-form expr nil)))
+ (when lexvar
+ ;; If it's bound outside conditional, invalidate.
+ (if (assq var byte-optimize--vars-outside-condition)
+ ;; We are in conditional code and the variable was
+ ;; bound outside: cancel substitutions.
+ (setcdr (cdr lexvar) nil)
+ ;; Set a new value (if substitutable).
+ (setcdr (cdr lexvar)
+ (and (byte-optimize--substitutable-p value)
+ (list value))))
+ (setcar (cdr lexvar) t)) ; Mark variable to be kept.
+ (push var var-expr-list)
+ (push value var-expr-list))
+ (setq args (cddr args)))
+ (cons fn (nreverse var-expr-list))))
+
+ (`(defvar ,(and (pred symbolp) name) . ,_)
+ (push name byte-optimize--dynamic-vars)
+ form)
+
(`(,(pred byte-code-function-p) . ,exps)
(cons fn (mapcar #'byte-optimize-form exps)))
@@ -582,6 +663,66 @@
new)
form)))
+(defun byte-optimize-let-form (head form for-effect)
+ ;; Recursively enter the optimizer for the bindings and body
+ ;; of a let or let*. This for depth-firstness: forms that
+ ;; are more deeply nested are optimized first.
+ (if (and lexical-binding byte-optimize-enable-variable-constprop)
+ (let* ((byte-optimize--lexvars byte-optimize--lexvars)
+ (new-lexvars nil)
+ (let-vars nil))
+ (dolist (binding (car form))
+ (let (name expr)
+ (cond ((consp binding)
+ (setq name (car binding))
+ (unless (symbolp name)
+ (byte-compile-warn "let-bind nonvariable: `%S'" name))
+ (setq expr (byte-optimize-form (cadr binding) nil)))
+ ((symbolp binding)
+ (setq name binding))
+ (t (byte-compile-warn "malformed let binding: `%S'" binding)))
+ (let* (
+ (value (and (byte-optimize--substitutable-p expr)
+ (list expr)))
+ (lexical (not (or (and (symbolp name)
+ (special-variable-p name))
+ (memq name byte-compile-bound-variables)
+ (memq name byte-optimize--dynamic-vars))))
+ (lexinfo (and lexical (cons name (cons nil value)))))
+ (push (cons name (cons expr (cdr lexinfo))) let-vars)
+ (when lexinfo
+ (push lexinfo (if (eq head 'let*)
+ byte-optimize--lexvars
+ new-lexvars))))))
+ (setq byte-optimize--lexvars
+ (append new-lexvars byte-optimize--lexvars))
+ ;; Walk the body expressions, which may mutate some of the records,
+ ;; and generate new bindings that exclude unused variables.
+ (let* ((byte-optimize--dynamic-vars byte-optimize--dynamic-vars)
+ (opt-body (byte-optimize-body (cdr form) for-effect))
+ (bindings nil))
+ (dolist (var let-vars)
+ ;; VAR is (NAME EXPR [KEEP [VALUE]])
+ (if (and (nthcdr 3 var) (not (nth 2 var)))
+ ;; Value present and not marked to be kept: eliminate.
+ (when byte-optimize-warn-eliminated-variable
+ (byte-compile-warn "eliminating local variable %S" (car var)))
+ (push (list (nth 0 var) (nth 1 var)) bindings)))
+ (cons bindings opt-body)))
+
+ ;; With dynamic binding, no substitutions are in effect.
+ (let ((byte-optimize--lexvars nil))
+ (cons
+ (mapcar (lambda (binding)
+ (if (symbolp binding)
+ binding
+ (when (or (atom binding) (cddr binding))
+ (byte-compile-warn "malformed let binding: `%S'" binding))
+ (list (car binding)
+ (byte-optimize-form (nth 1 binding) nil))))
+ (car form))
+ (byte-optimize-body (cdr form) for-effect)))))
+
(defun byte-optimize-body (forms all-for-effect)
;; Optimize the cdr of a progn or implicit progn; all forms is a list of
diff --git a/lisp/emacs-lisp/bytecomp.el b/lisp/emacs-lisp/bytecomp.el
index c0f8db69e51..709a310eb6c 100644
--- a/lisp/emacs-lisp/bytecomp.el
+++ b/lisp/emacs-lisp/bytecomp.el
@@ -195,7 +195,6 @@ otherwise adds \".elc\"."
(autoload 'byte-optimize-form "byte-opt")
;; This is the entry point to the lapcode optimizer pass2.
(autoload 'byte-optimize-lapcode "byte-opt")
-(autoload 'byte-compile-unfold-lambda "byte-opt")
;; This is the entry point to the decompiler, which is used by the
;; disassembler. The disassembler just requires 'byte-compile, but
@@ -3365,7 +3364,7 @@ for symbols generated by the byte compiler itself."
((and (eq (car-safe (car form)) 'lambda)
;; if the form comes out the same way it went in, that's
;; because it was malformed, and we couldn't unfold it.
- (not (eq form (setq form (byte-compile-unfold-lambda form)))))
+ (not (eq form (setq form (macroexp--unfold-lambda form)))))
(byte-compile-form form byte-compile--for-effect)
(setq byte-compile--for-effect nil))
((byte-compile-normal-call form)))
diff --git a/lisp/emacs-lisp/chart.el b/lisp/emacs-lisp/chart.el
index 2cd73225ff3..7d760ffc57f 100644
--- a/lisp/emacs-lisp/chart.el
+++ b/lisp/emacs-lisp/chart.el
@@ -67,9 +67,8 @@
(define-obsolete-variable-alias 'chart-map 'chart-mode-map "24.1")
(defvar chart-mode-map (make-sparse-keymap) "Keymap used in chart mode.")
-(defvar chart-local-object nil
+(defvar-local chart-local-object nil
"Local variable containing the locally displayed chart object.")
-(make-variable-buffer-local 'chart-local-object)
(defvar chart-face-color-list '("red" "green" "blue"
"cyan" "yellow" "purple")
diff --git a/lisp/emacs-lisp/checkdoc.el b/lisp/emacs-lisp/checkdoc.el
index 9722792a5a5..75aefdc7ba0 100644
--- a/lisp/emacs-lisp/checkdoc.el
+++ b/lisp/emacs-lisp/checkdoc.el
@@ -147,13 +147,6 @@
;;
;; See the above section "Checking Parameters" for details about
;; parameter checking.
-;;
-;; Dependencies:
-;;
-;; This file requires lisp-mnt (Lisp maintenance routines) for the
-;; comment checkers.
-;;
-;; Requires custom for Emacs v20.
;;; TO DO:
;; Hook into the byte compiler on a defun/defvar level to generate
diff --git a/lisp/emacs-lisp/easy-mmode.el b/lisp/emacs-lisp/easy-mmode.el
index f4dbcee4d69..2916ae4adea 100644
--- a/lisp/emacs-lisp/easy-mmode.el
+++ b/lisp/emacs-lisp/easy-mmode.el
@@ -278,11 +278,10 @@ For example, you could write
((not globalp)
`(progn
:autoload-end
- (defvar ,mode ,init-value
+ (defvar-local ,mode ,init-value
,(concat (format "Non-nil if %s is enabled.\n" pretty-name)
(internal--format-docstring-line
- "Use the command `%s' to change this variable." mode)))
- (make-variable-buffer-local ',mode)))
+ "Use the command `%s' to change this variable." mode)))))
(t
(let ((base-doc-string
(concat "Non-nil if %s is enabled.
@@ -419,6 +418,7 @@ on if the hook has explicitly disabled it.
(pretty-global-name (easy-mmode-pretty-mode-name global-mode))
(group nil)
(extra-keywords nil)
+ (MODE-variable mode)
(MODE-buffers (intern (concat global-mode-name "-buffers")))
(MODE-enable-in-buffers
(intern (concat global-mode-name "-enable-in-buffers")))
@@ -440,6 +440,7 @@ on if the hook has explicitly disabled it.
(pcase keyw
(:group (setq group (nconc group (list :group (pop body)))))
(:global (pop body))
+ (:variable (setq MODE-variable (pop body)))
(:predicate
(setq predicate (list (pop body)))
(setq turn-on-function
@@ -453,8 +454,7 @@ on if the hook has explicitly disabled it.
(progn
(put ',global-mode 'globalized-minor-mode t)
:autoload-end
- (defvar ,MODE-major-mode nil)
- (make-variable-buffer-local ',MODE-major-mode))
+ (defvar-local ,MODE-major-mode nil))
;; The actual global minor-mode
(define-minor-mode ,global-mode
,(concat (format "Toggle %s in all buffers.\n" pretty-name)
@@ -543,7 +543,7 @@ list."
(with-current-buffer buf
(unless ,MODE-set-explicitly
(unless (eq ,MODE-major-mode major-mode)
- (if ,mode
+ (if ,MODE-variable
(progn
(,mode -1)
(funcall ,turn-on-function))
diff --git a/lisp/emacs-lisp/edebug.el b/lisp/emacs-lisp/edebug.el
index 1ded0e7b097..0733dcec27b 100644
--- a/lisp/emacs-lisp/edebug.el
+++ b/lisp/emacs-lisp/edebug.el
@@ -341,7 +341,7 @@ Return the result of the last expression in BODY."
;; FIXME: We should probably just be using `pop-to-buffer'.
(setq window
(cond
- ((and (edebug-window-live-p window)
+ ((and (window-live-p window)
(eq (window-buffer window) buffer))
window)
((eq (window-buffer) buffer)
@@ -392,7 +392,7 @@ Return the result of the last expression in BODY."
;; Get either a full window configuration or some window information.
(if (listp which-windows)
(mapcar (lambda (window)
- (if (edebug-window-live-p window)
+ (if (window-live-p window)
(list window
(window-buffer window)
(window-point window)
@@ -407,7 +407,7 @@ Return the result of the last expression in BODY."
(mapcar (lambda (one-window-info)
(if one-window-info
(apply (lambda (window buffer point start hscroll)
- (if (edebug-window-live-p window)
+ (if (window-live-p window)
(progn
(set-window-buffer window buffer)
(set-window-point window point)
@@ -1687,10 +1687,10 @@ contains a circular object."
(first-char (and (symbolp spec) (aref (symbol-name spec) 0)))
(match (cond
((eq ?& first-char);; "&" symbols take all following specs.
- (funcall (get-edebug-spec spec) cursor (cdr specs)))
+ (edebug--handle-&-spec-op spec cursor (cdr specs)))
((eq ?: first-char);; ":" symbols take one following spec.
(setq rest (cdr (cdr specs)))
- (funcall (get-edebug-spec spec) cursor (car (cdr specs))))
+ (edebug--handle-:-spec-op spec cursor (car (cdr specs))))
(t;; Any other normal spec.
(setq rest (cdr specs))
(edebug-match-one-spec cursor spec)))))
@@ -1721,16 +1721,10 @@ contains a circular object."
;; user may want to define macros or functions with the same names.
;; We could use an internal obarray for these primitive specs.
-(dolist (pair '((&optional . edebug-match-&optional)
- (&rest . edebug-match-&rest)
- (&or . edebug-match-&or)
- (form . edebug-match-form)
+(dolist (pair '((form . edebug-match-form)
(sexp . edebug-match-sexp)
(body . edebug-match-body)
- (&define . edebug-match-&define)
(name . edebug-match-name)
- (:name . edebug-match-colon-name)
- (:unique . edebug-match-:unique)
(arg . edebug-match-arg)
(def-body . edebug-match-def-body)
(def-form . edebug-match-def-form)
@@ -1743,9 +1737,6 @@ contains a circular object."
(cl-macrolet-expr . edebug-match-cl-macrolet-expr)
(cl-macrolet-name . edebug-match-cl-macrolet-name)
(cl-macrolet-body . edebug-match-cl-macrolet-body)
- (&not . edebug-match-&not)
- (&key . edebug-match-&key)
- (&error . edebug-match-&error)
(place . edebug-match-place)
(gate . edebug-match-gate)
;; (nil . edebug-match-nil) not this one - special case it.
@@ -1793,7 +1784,7 @@ contains a circular object."
(defsubst edebug-match-body (cursor) (edebug-forms cursor))
-(defun edebug-match-&optional (cursor specs)
+(cl-defmethod edebug--handle-&-spec-op ((_ (eql &optional)) cursor specs)
;; Keep matching until one spec fails.
(edebug-&optional-wrapper cursor specs 'edebug-&optional-wrapper))
@@ -1819,7 +1810,11 @@ contains a circular object."
;; Reuse the &optional handler with this as the remainder handler.
(edebug-&optional-wrapper cursor specs remainder-handler))
-(defun edebug-match-&rest (cursor specs)
+(cl-defgeneric edebug--handle-&-spec-op (op cursor specs)
+ "Handle &foo spec operators.
+&foo spec operators operate on all the subsequent SPECS.")
+
+(cl-defmethod edebug--handle-&-spec-op ((_ (eql &rest)) cursor specs)
;; Repeatedly use specs until failure.
(let ((edebug-&rest specs) ;; remember these
edebug-best-error
@@ -1827,7 +1822,7 @@ contains a circular object."
(edebug-&rest-wrapper cursor specs 'edebug-&rest-wrapper)))
-(defun edebug-match-&or (cursor specs)
+(cl-defmethod edebug--handle-&-spec-op ((_ (eql &or)) cursor specs)
;; Keep matching until one spec succeeds, and return its results.
;; If none match, fail.
;; This needs to be optimized since most specs spend time here.
@@ -1852,23 +1847,24 @@ contains a circular object."
))
-(defun edebug-match-&not (cursor specs)
+(cl-defmethod edebug--handle-&-spec-op ((_ (eql &not)) cursor specs)
;; If any specs match, then fail
(if (null (catch 'no-match
(let ((edebug-gate nil))
(save-excursion
- (edebug-match-&or cursor specs)))
+ (edebug--handle-&-spec-op '&or cursor specs)))
nil))
;; This means something matched, so it is a no match.
(edebug-no-match cursor "Unexpected"))
;; This means nothing matched, so it is OK.
nil) ;; So, return nothing
-(defun edebug-match-&key (cursor specs)
+(cl-defmethod edebug--handle-&-spec-op ((_ (eql &key)) cursor specs)
;; Following specs must look like (<name> <spec>) ...
;; where <name> is the name of a keyword, and spec is its spec.
;; This really doesn't save much over the expanded form and takes time.
- (edebug-match-&rest
+ (edebug--handle-&-spec-op
+ '&rest
cursor
(cons '&or
(mapcar (lambda (pair)
@@ -1876,7 +1872,7 @@ contains a circular object."
(car (cdr pair))))
specs))))
-(defun edebug-match-&error (cursor specs)
+(cl-defmethod edebug--handle-&-spec-op ((_ (eql &error)) cursor specs)
;; Signal an error, using the following string in the spec as argument.
(let ((error-string (car specs))
(edebug-error-point (edebug-before-offset cursor)))
@@ -1980,7 +1976,7 @@ contains a circular object."
(defun edebug-match-function (_cursor)
(error "Use function-form instead of function in edebug spec"))
-(defun edebug-match-&define (cursor specs)
+(cl-defmethod edebug--handle-&-spec-op ((_ (eql &define)) cursor specs)
;; Match a defining form.
;; Normally, &define is interpreted specially other places.
;; This should only be called inside of a spec list to match the remainder
@@ -2034,7 +2030,11 @@ contains a circular object."
(edebug-move-cursor cursor)
(list name)))
-(defun edebug-match-colon-name (_cursor spec)
+(cl-defgeneric edebug--handle-:-spec-op (op cursor spec)
+ "Handle :foo spec operators.
+:foo spec operators operate on just the one subsequent SPEC element.")
+
+(cl-defmethod edebug--handle-:-spec-op ((_ (eql :name)) _cursor spec)
;; Set the edebug-def-name to the spec.
(setq edebug-def-name
(if edebug-def-name
@@ -2043,7 +2043,7 @@ contains a circular object."
spec))
nil)
-(defun edebug-match-:unique (_cursor spec)
+(cl-defmethod edebug--handle-:-spec-op ((_ (eql :unique)) _cursor spec)
"Match a `:unique PREFIX' specifier.
SPEC is the symbol name prefix for `gensym'."
(let ((suffix (gensym spec)))
@@ -2641,12 +2641,11 @@ See `edebug-behavior-alist' for implementations.")
;; window-start now stored with each function.
-;;(defvar edebug-window-start nil)
+;;(defvar-local edebug-window-start nil)
;; Remember where each buffers' window starts between edebug calls.
;; This is to avoid spurious recentering.
;; Does this still need to be buffer-local??
;;(setq-default edebug-window-start nil)
-;;(make-variable-buffer-local 'edebug-window-start)
;; Dynamically declared unbound vars
@@ -2689,7 +2688,7 @@ See `edebug-behavior-alist' for implementations.")
(edebug-outside-window (selected-window))
(edebug-outside-buffer (current-buffer))
(edebug-outside-point (point))
- (edebug-outside-mark (edebug-mark))
+ (edebug-outside-mark (mark t))
edebug-outside-windows ; Window or screen configuration.
edebug-buffer-points
@@ -2858,7 +2857,7 @@ See `edebug-behavior-alist' for implementations.")
;; Unrestore edebug-buffer's window-start, if displayed.
(let ((window (car edebug-window-data)))
- (if (and (edebug-window-live-p window)
+ (if (and (window-live-p window)
(eq (window-buffer) edebug-buffer))
(progn
(set-window-start window (cdr edebug-window-data)
@@ -2877,7 +2876,7 @@ See `edebug-behavior-alist' for implementations.")
;; Since we may be in a save-excursion, in case of quit,
;; reselect the outside window only.
;; Only needed if we are not recovering windows??
- (if (edebug-window-live-p edebug-outside-window)
+ (if (window-live-p edebug-outside-window)
(select-window edebug-outside-window))
) ; if edebug-save-windows
@@ -3802,9 +3801,10 @@ Print result in minibuffer."
(interactive (list (read--expression "Eval: ")))
(princ
(edebug-outside-excursion
- (setq values (cons (edebug-eval expr) values))
- (concat (edebug-safe-prin1-to-string (car values))
- (eval-expression-print-format (car values))))))
+ (let ((result (edebug-eval expr)))
+ (values--store-value result)
+ (concat (edebug-safe-prin1-to-string result)
+ (eval-expression-print-format result))))))
(defun edebug-eval-last-sexp (&optional no-truncate)
"Evaluate sexp before point in the outside environment.
@@ -4541,11 +4541,6 @@ It is removed when you hit any char."
;;; Emacs version specific code
-(defalias 'edebug-window-live-p 'window-live-p)
-
-(defun edebug-mark ()
- (mark t))
-
(defun edebug-set-conditional-breakpoint (arg condition)
"Set a conditional breakpoint at nearest sexp.
The condition is evaluated in the outside context.
@@ -4661,7 +4656,15 @@ instrumentation for, defaulting to all functions."
(message "Removed edebug instrumentation from %s"
(mapconcat #'symbol-name functions ", ")))
+
+;;; Obsolete.
+
+(defun edebug-mark ()
+ (declare (obsolete mark "28.1"))
+ (mark t))
+
(define-obsolete-function-alias 'edebug-mark-marker #'mark-marker "28.1")
+(define-obsolete-function-alias 'edebug-window-live-p #'window-live-p "28.1")
(provide 'edebug)
;;; edebug.el ends here
diff --git a/lisp/emacs-lisp/elp.el b/lisp/emacs-lisp/elp.el
index f551c0c36c3..cc2927caf40 100644
--- a/lisp/emacs-lisp/elp.el
+++ b/lisp/emacs-lisp/elp.el
@@ -110,8 +110,7 @@
;; Boy Jim's profiler.el. Both were written for Emacs 18 and both were
;; pretty good first shots at profiling, but I found that they didn't
;; provide the functionality or interface that I wanted, so I wrote
-;; this. I've tested elp in XEmacs 19 and Emacs 19. There's no point
-;; in even trying to make this work with Emacs 18.
+;; this.
;; Unlike previous profilers, elp uses Emacs 19's built-in function
;; current-time to return interval times. This obviates the need for
diff --git a/lisp/emacs-lisp/generator.el b/lisp/emacs-lisp/generator.el
index 9eb6d959645..e45260c32ac 100644
--- a/lisp/emacs-lisp/generator.el
+++ b/lisp/emacs-lisp/generator.el
@@ -725,17 +725,20 @@ Return the value with which ITERATOR finished iteration."
(condition-symbol (cps--gensym "iter-do-condition"))
(it-symbol (cps--gensym "iter-do-iterator"))
(result-symbol (cps--gensym "iter-do-result")))
- `(let (,var
- ,result-symbol
+ `(let (,result-symbol
(,done-symbol nil)
(,it-symbol ,iterator))
- (while (not ,done-symbol)
- (condition-case ,condition-symbol
- (setf ,var (iter-next ,it-symbol))
- (iter-end-of-sequence
- (setf ,result-symbol (cdr ,condition-symbol))
- (setf ,done-symbol t)))
- (unless ,done-symbol ,@body))
+ (while
+ (let ((,var
+ (condition-case ,condition-symbol
+ (iter-next ,it-symbol)
+ (iter-end-of-sequence
+ (setf ,result-symbol (cdr ,condition-symbol))
+ (setf ,done-symbol t)))))
+ (unless ,done-symbol
+ ,@body
+ ;; Loop until done-symbol is set.
+ t)))
,result-symbol)))
(defvar cl--loop-args)
diff --git a/lisp/emacs-lisp/generic.el b/lisp/emacs-lisp/generic.el
index 6db1bbbb224..294aba66c3a 100644
--- a/lisp/emacs-lisp/generic.el
+++ b/lisp/emacs-lisp/generic.el
@@ -96,9 +96,8 @@
;; Internal Variables
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
-(defvar generic-font-lock-keywords nil
+(defvar-local generic-font-lock-keywords nil
"Keywords for `font-lock-defaults' in a generic mode.")
-(make-variable-buffer-local 'generic-font-lock-keywords)
;;;###autoload
(defvar generic-mode-list nil
diff --git a/lisp/emacs-lisp/lisp-mnt.el b/lisp/emacs-lisp/lisp-mnt.el
index adb9cb2372c..6d9c8c32794 100644
--- a/lisp/emacs-lisp/lisp-mnt.el
+++ b/lisp/emacs-lisp/lisp-mnt.el
@@ -495,7 +495,7 @@ absent, return nil."
(concat "^;;;[[:blank:]]*\\("
lm-commentary-header
"\\):[[:blank:]\n]*")
- "^;;[[:blank:]]*" ; double semicolon prefix
+ "^;;[[:blank:]]?" ; double semicolon prefix
"[[:blank:]\n]*\\'") ; trailing new-lines
"" (buffer-substring-no-properties
start (lm-commentary-end))))))))
diff --git a/lisp/emacs-lisp/lisp-mode.el b/lisp/emacs-lisp/lisp-mode.el
index 3918fa01b2a..54089c4bc69 100644
--- a/lisp/emacs-lisp/lisp-mode.el
+++ b/lisp/emacs-lisp/lisp-mode.el
@@ -62,9 +62,6 @@
(modify-syntax-entry ?\t " " table)
(modify-syntax-entry ?\f " " table)
(modify-syntax-entry ?\n "> " table)
- ;; This is probably obsolete since nowadays such features use overlays.
- ;; ;; Give CR the same syntax as newline, for selective-display.
- ;; (modify-syntax-entry ?\^m "> " table)
(modify-syntax-entry ?\; "< " table)
(modify-syntax-entry ?` "' " table)
(modify-syntax-entry ?' "' " table)
@@ -775,7 +772,8 @@ or to switch back to an existing one."
(setq-local find-tag-default-function 'lisp-find-tag-default)
(setq-local comment-start-skip
"\\(\\(^\\|[^\\\n]\\)\\(\\\\\\\\\\)*\\)\\(;+\\|#|\\) *")
- (setq-local comment-end "|#")
+ (setq-local comment-end-skip "[ \t]*\\(\\s>\\||#\\)")
+ (setq-local font-lock-comment-end-skip "|#")
(setq imenu-case-fold-search t))
(defun lisp-find-tag-default ()
@@ -1372,7 +1370,24 @@ and initial semicolons."
(derived-mode-p 'emacs-lisp-mode))
emacs-lisp-docstring-fill-column
fill-column)))
- (fill-paragraph justify))
+ (save-restriction
+ (save-excursion
+ (let ((ppss (syntax-ppss)))
+ ;; If we're in a string, then narrow (roughly) to that
+ ;; string before filling. This avoids filling Lisp
+ ;; statements that follow the string.
+ (when (ppss-string-terminator ppss)
+ (goto-char (ppss-comment-or-string-start ppss))
+ (beginning-of-line)
+ ;; The string may be unterminated -- in that case, don't
+ ;; narrow.
+ (when (ignore-errors
+ (progn
+ (forward-sexp 1)
+ t))
+ (narrow-to-region (ppss-comment-or-string-start ppss)
+ (point))))
+ (fill-paragraph justify)))))
;; Never return nil.
t))
diff --git a/lisp/emacs-lisp/macroexp.el b/lisp/emacs-lisp/macroexp.el
index e842222b7c3..042061c44fc 100644
--- a/lisp/emacs-lisp/macroexp.el
+++ b/lisp/emacs-lisp/macroexp.el
@@ -200,6 +200,69 @@ and also to avoid outputting the warning during normal execution."
new-form))
new-form)))
+(defun macroexp--unfold-lambda (form &optional name)
+ ;; In lexical-binding mode, let and functions don't bind vars in the same way
+ ;; (let obey special-variable-p, but functions don't). But luckily, this
+ ;; doesn't matter here, because function's behavior is underspecified so it
+ ;; can safely be turned into a `let', even though the reverse is not true.
+ (or name (setq name "anonymous lambda"))
+ (let* ((lambda (car form))
+ (values (cdr form))
+ (arglist (nth 1 lambda))
+ (body (cdr (cdr lambda)))
+ optionalp restp
+ bindings)
+ (if (and (stringp (car body)) (cdr body))
+ (setq body (cdr body)))
+ (if (and (consp (car body)) (eq 'interactive (car (car body))))
+ (setq body (cdr body)))
+ ;; FIXME: The checks below do not belong in an optimization phase.
+ (while arglist
+ (cond ((eq (car arglist) '&optional)
+ ;; ok, I'll let this slide because funcall_lambda() does...
+ ;; (if optionalp (error "multiple &optional keywords in %s" name))
+ (if restp (error "&optional found after &rest in %s" name))
+ (if (null (cdr arglist))
+ (error "nothing after &optional in %s" name))
+ (setq optionalp t))
+ ((eq (car arglist) '&rest)
+ ;; ...but it is by no stretch of the imagination a reasonable
+ ;; thing that funcall_lambda() allows (&rest x y) and
+ ;; (&rest x &optional y) in arglists.
+ (if (null (cdr arglist))
+ (error "nothing after &rest in %s" name))
+ (if (cdr (cdr arglist))
+ (error "multiple vars after &rest in %s" name))
+ (setq restp t))
+ (restp
+ (setq bindings (cons (list (car arglist)
+ (and values (cons 'list values)))
+ bindings)
+ values nil))
+ ((and (not optionalp) (null values))
+ (setq arglist nil values 'too-few))
+ (t
+ (setq bindings (cons (list (car arglist) (car values))
+ bindings)
+ values (cdr values))))
+ (setq arglist (cdr arglist)))
+ (if values
+ (macroexp--warn-and-return
+ (format (if (eq values 'too-few)
+ "attempt to open-code `%s' with too few arguments"
+ "attempt to open-code `%s' with too many arguments")
+ name)
+ form)
+
+ ;; The following leads to infinite recursion when loading a
+ ;; file containing `(defsubst f () (f))', and then trying to
+ ;; byte-compile that file.
+ ;;(setq body (mapcar 'byte-optimize-form body)))
+
+ (if bindings
+ `(let ,(nreverse bindings) . ,body)
+ (macroexp-progn body)))))
+
(defun macroexp--expand-all (form)
"Expand all macros in FORM.
This is an internal version of `macroexpand-all'.
@@ -245,12 +308,8 @@ Assumes the caller has bound `macroexpand-all-environment'."
;; i.e. rewrite it to (let (<args>) <body>). We'd do it in the optimizer
;; anyway, but doing it here (i.e. earlier) can sometimes avoid the
;; creation of a closure, thus resulting in much better code.
- (let ((newform (if (not (fboundp 'byte-compile-unfold-lambda))
- 'macroexp--not-unfolded
- ;; Don't unfold if byte-opt is not yet loaded.
- (byte-compile-unfold-lambda form))))
- (if (or (eq newform 'macroexp--not-unfolded)
- (eq newform form))
+ (let ((newform (macroexp--unfold-lambda form)))
+ (if (eq newform form)
;; Unfolding failed for some reason, avoid infinite recursion.
(macroexp--cons (macroexp--all-forms fun 2)
(macroexp--all-forms args)
diff --git a/lisp/emacs-lisp/package-x.el b/lisp/emacs-lisp/package-x.el
index b723643ffb9..2e327d16de4 100644
--- a/lisp/emacs-lisp/package-x.el
+++ b/lisp/emacs-lisp/package-x.el
@@ -182,8 +182,7 @@ if it exists."
;; Check if `package-archive-upload-base' is valid.
(when (or (not (stringp package-archive-upload-base))
(equal package-archive-upload-base
- (car-safe
- (get 'package-archive-upload-base 'standard-value))))
+ (custom--standard-value 'package-archive-upload-base)))
(setq package-archive-upload-base
(read-directory-name
"Base directory for package archive: ")))
diff --git a/lisp/emacs-lisp/pcase.el b/lisp/emacs-lisp/pcase.el
index cf129c453ec..ec746fa4747 100644
--- a/lisp/emacs-lisp/pcase.el
+++ b/lisp/emacs-lisp/pcase.el
@@ -135,7 +135,6 @@ PATTERN matches. PATTERN can take one of the forms:
(pred (not FUN)) matches if FUN called on EXPVAL returns nil.
(app FUN PAT) matches if FUN called on EXPVAL matches PAT.
(guard BOOLEXP) matches if BOOLEXP evaluates to non-nil.
- (let PAT EXPR) matches if EXPR matches PAT.
(and PAT...) matches if all the patterns match.
(or PAT...) matches if any of the patterns matches.
@@ -145,7 +144,7 @@ FUN in `pred' and `app' can take one of the forms:
(F ARG1 .. ARGn)
call F with ARG1..ARGn and EXPVAL as n+1'th argument
-FUN, BOOLEXP, EXPR, and subsequent PAT can refer to variables
+FUN, BOOLEXP, and subsequent PAT can refer to variables
bound earlier in the pattern by a SYMBOL pattern.
Additional patterns can be defined using `pcase-defmacro'.
@@ -426,7 +425,6 @@ of the elements of LIST is performed as if by `pcase-let'.
(if (pcase--self-quoting-p pat) `',pat pat))
((memq head '(pred guard quote)) pat)
((memq head '(or and)) `(,head ,@(mapcar #'pcase--macroexpand (cdr pat))))
- ((eq head 'let) `(let ,(pcase--macroexpand (cadr pat)) ,@(cddr pat)))
((eq head 'app) `(app ,(nth 1 pat) ,(pcase--macroexpand (nth 2 pat))))
(t
(let* ((expander (pcase--get-macroexpander head))
@@ -888,18 +886,9 @@ Otherwise, it defers to REST which is a list of branches of the form
(if (not (assq upat vars))
(pcase--u1 matches code (cons (cons upat sym) vars) rest)
;; Non-linear pattern. Turn it into an `eq' test.
- (pcase--u1 (cons `(match ,sym . (pred (eq ,(cdr (assq upat vars)))))
+ (pcase--u1 (cons `(match ,sym . (pred (eql ,(cdr (assq upat vars)))))
matches)
code vars rest)))
- ((eq (car-safe upat) 'let)
- ;; A upat of the form (let VAR EXP).
- ;; (pcase--u1 matches code
- ;; (cons (cons (nth 1 upat) (nth 2 upat)) vars) rest)
- (macroexp-let2
- macroexp-copyable-p sym
- (pcase--eval (nth 2 upat) vars)
- (pcase--u1 (cons (pcase--match sym (nth 1 upat)) matches)
- code vars rest)))
((eq (car-safe upat) 'app)
;; A upat of the form (app FUN PAT)
(pcase--mark-used sym)
@@ -1011,5 +1000,9 @@ The predicate is the logical-AND of:
;; compounded values that are not `consp'
(t (error "Unknown QPAT: %S" qpat))))
+(pcase-defmacro let (pat expr)
+ "Matches if EXPR matches PAT."
+ `(app (lambda (_) ,expr) ,pat))
+
(provide 'pcase)
;;; pcase.el ends here
diff --git a/lisp/emacs-lisp/pp.el b/lisp/emacs-lisp/pp.el
index ef4c9603284..2fd4724aef1 100644
--- a/lisp/emacs-lisp/pp.el
+++ b/lisp/emacs-lisp/pp.el
@@ -127,8 +127,9 @@ Also add the value to the front of the list in the variable `values'."
(interactive
(list (read--expression "Eval: ")))
(message "Evaluating...")
- (push (eval expression lexical-binding) values)
- (pp-display-expression (car values) "*Pp Eval Output*"))
+ (let ((result (eval expression lexical-binding)))
+ (values--store-value result)
+ (pp-display-expression result "*Pp Eval Output*")))
;;;###autoload
(defun pp-macroexpand-expression (expression)
diff --git a/lisp/emacs-lisp/re-builder.el b/lisp/emacs-lisp/re-builder.el
index 23221a2a00d..ce8d98df807 100644
--- a/lisp/emacs-lisp/re-builder.el
+++ b/lisp/emacs-lisp/re-builder.el
@@ -187,14 +187,14 @@ Set it to nil if you don't want limits here."
(defvar reb-target-window nil
"Window to which the RE is applied to.")
-(defvar reb-regexp nil
+(defvar-local reb-regexp nil
"Last regexp used by RE Builder.")
-(defvar reb-regexp-src nil
+(defvar-local reb-regexp-src nil
"Last regexp used by RE Builder before processing it.
Except for Lisp syntax this is the same as `reb-regexp'.")
-(defvar reb-overlays nil
+(defvar-local reb-overlays nil
"List of overlays of the RE Builder.")
(defvar reb-window-config nil
@@ -212,10 +212,6 @@ Except for Lisp syntax this is the same as `reb-regexp'.")
(defvar reb-valid-string ""
"String in mode line showing validity of RE.")
-(make-variable-buffer-local 'reb-overlays)
-(make-variable-buffer-local 'reb-regexp)
-(make-variable-buffer-local 'reb-regexp-src)
-
(defconst reb-buffer "*RE-Builder*"
"Buffer to use for the RE Builder.")
diff --git a/lisp/emacs-lisp/syntax.el b/lisp/emacs-lisp/syntax.el
index 62f213c57f7..bee2f9639e7 100644
--- a/lisp/emacs-lisp/syntax.el
+++ b/lisp/emacs-lisp/syntax.el
@@ -75,7 +75,7 @@ properties won't work properly.")
(defvar syntax-propertize-chunk-size 500)
-(defvar syntax-propertize-extend-region-functions
+(defvar-local syntax-propertize-extend-region-functions
'(syntax-propertize-wholelines)
"Special hook run just before proceeding to propertize a region.
This is used to allow major modes to help `syntax-propertize' find safe buffer
@@ -89,7 +89,6 @@ These functions are run in turn repeatedly until they all return nil.
Put first the functions more likely to cause a change and cheaper to compute.")
;; Mark it as a special hook which doesn't use any global setting
;; (i.e. doesn't obey the element t in the buffer-local value).
-(make-variable-buffer-local 'syntax-propertize-extend-region-functions)
(cl-defstruct (ppss
(:constructor make-ppss)
diff --git a/lisp/emacs-lisp/tcover-ses.el b/lisp/emacs-lisp/tcover-ses.el
index fb9cd8f47df..12b0dcfff95 100644
--- a/lisp/emacs-lisp/tcover-ses.el
+++ b/lisp/emacs-lisp/tcover-ses.el
@@ -6,6 +6,8 @@
;; Keywords: spreadsheet lisp utility
;; Package: testcover
+;; This file is part of GNU Emacs.
+
;; GNU Emacs is free software: you can redistribute it and/or modify
;; it under the terms of the GNU General Public License as published by
;; the Free Software Foundation, either version 3 of the License, or
diff --git a/lisp/emacs-lisp/testcover.el b/lisp/emacs-lisp/testcover.el
index 312e38769c5..75b27d08e56 100644
--- a/lisp/emacs-lisp/testcover.el
+++ b/lisp/emacs-lisp/testcover.el
@@ -258,10 +258,10 @@ vector. Return VALUE."
(aset testcover-vector after-index (testcover--copy-object value)))
((eq 'maybe old-result)
(aset testcover-vector after-index 'edebug-ok-coverage))
- ((eq '1value old-result)
+ ((eq 'testcover-1value old-result)
(aset testcover-vector after-index
(cons old-result (testcover--copy-object value))))
- ((and (eq (car-safe old-result) '1value)
+ ((and (eq (car-safe old-result) 'testcover-1value)
(not (condition-case ()
(equal (cdr old-result) value)
(circular-list t))))
@@ -358,11 +358,11 @@ eliminated by adding more test cases."
data (aref coverage len))
(when (and (not (eq data 'edebug-ok-coverage))
(not (memq (car-safe data)
- '(1value maybe noreturn)))
+ '(testcover-1value maybe noreturn)))
(setq j (+ def-mark (aref points len))))
(setq ov (make-overlay (1- j) j))
(overlay-put ov 'face
- (if (memq data '(edebug-unknown maybe 1value))
+ (if (memq data '(edebug-unknown maybe testcover-1value))
'testcover-nohits
'testcover-1value))))
(set-buffer-modified-p changed))))
@@ -450,12 +450,12 @@ or return multiple values."
(`(defconst ,sym . ,args)
(push sym testcover-module-constants)
(testcover-analyze-coverage-progn args)
- '1value)
+ 'testcover-1value)
(`(defun ,name ,_ . ,doc-and-body)
(let ((val (testcover-analyze-coverage-progn doc-and-body)))
(cl-case val
- ((1value) (push name testcover-module-1value-functions))
+ ((testcover-1value) (push name testcover-module-1value-functions))
((maybe) (push name testcover-module-potentially-1value-functions)))
nil))
@@ -466,13 +466,13 @@ or return multiple values."
;; To avoid infinite recursion, don't examine quoted objects.
;; This will cause the coverage marks on an instrumented quoted
;; form to look odd. See bug#25316.
- '1value)
+ 'testcover-1value)
(`(\` ,bq-form)
(testcover-analyze-coverage-backquote-form bq-form))
((or 't 'nil (pred keywordp))
- '1value)
+ 'testcover-1value)
((pred vectorp)
(testcover-analyze-coverage-compose (append form nil)
@@ -482,7 +482,7 @@ or return multiple values."
nil)
((pred atom)
- '1value)
+ 'testcover-1value)
(_
;; Whatever we have here, it's not wrapped, so treat it as a list of forms.
@@ -494,7 +494,7 @@ Analyze all the forms in FORMS and return 1value, maybe or nil
depending on the analysis of the last one. Find the coverage
vectors referenced by `edebug-enter' forms nested within FORMS and
update them with the results of the analysis."
- (let ((result '1value))
+ (let ((result 'testcover-1value))
(while (consp forms)
(setq result (testcover-analyze-coverage (pop forms))))
result))
@@ -518,7 +518,7 @@ form to be treated accordingly."
(setq val (testcover-analyze-coverage-wrapped-form wrapped-form))
(when (or (eq wrapper '1value) val)
;; The form is 1-valued or potentially 1-valued.
- (aset testcover-vector after-id (or val '1value)))
+ (aset testcover-vector after-id (or val 'testcover-1value)))
(cond
((or (eq wrapper 'noreturn)
@@ -526,13 +526,13 @@ form to be treated accordingly."
;; This function won't return, so indicate to testcover-before that
;; it should record coverage.
(aset testcover-vector before-id (cons 'noreturn after-id))
- (aset testcover-vector after-id '1value)
- (setq val '1value))
+ (aset testcover-vector after-id 'testcover-1value)
+ (setq val 'testcover-1value))
((eq (car-safe wrapped-form) '1value)
;; This function is always supposed to return the same value.
- (setq val '1value)
- (aset testcover-vector after-id '1value)))
+ (setq val 'testcover-1value)
+ (aset testcover-vector after-id 'testcover-1value)))
val))
(defun testcover-analyze-coverage-wrapped-form (form)
@@ -540,26 +540,26 @@ form to be treated accordingly."
FORM is treated as if it will be evaluated."
(pcase form
((pred keywordp)
- '1value)
+ 'testcover-1value)
((pred symbolp)
(when (or (memq form testcover-constants)
(memq form testcover-module-constants))
- '1value))
+ 'testcover-1value))
((pred atom)
- '1value)
+ 'testcover-1value)
(`(\` ,bq-form)
(testcover-analyze-coverage-backquote-form bq-form))
(`(defconst ,sym ,val . ,_)
(push sym testcover-module-constants)
(testcover-analyze-coverage val)
- '1value)
+ 'testcover-1value)
(`(,(or 'dotimes 'dolist) (,_ ,expr . ,result) . ,body)
;; These always return RESULT if provided.
(testcover-analyze-coverage expr)
(testcover-analyze-coverage-progn body)
(let ((val (testcover-analyze-coverage-progn result)))
;; If the third value is not present, the loop always returns nil.
- (if result val '1value)))
+ (if result val 'testcover-1value)))
(`(,(or 'let 'let*) ,bindings . ,body)
(testcover-analyze-coverage-progn bindings)
(testcover-analyze-coverage-progn body))
@@ -604,12 +604,12 @@ FORM is treated as if it will be evaluated."
(defun testcover-analyze-coverage-wrapped-application (func args)
"Analyze the application of FUNC to ARGS for code coverage."
(cond
- ((eq func 'quote) '1value)
+ ((eq func 'quote) 'testcover-1value)
((or (memq func testcover-1value-functions)
(memq func testcover-module-1value-functions))
;; The function should always return the same value.
(testcover-analyze-coverage-progn args)
- '1value)
+ 'testcover-1value)
((or (memq func testcover-potentially-1value-functions)
(memq func testcover-module-potentially-1value-functions))
;; The function might always return the same value.
@@ -635,14 +635,14 @@ If either argument is nil, return nil, otherwise if either
argument is maybe, return maybe. Return 1value only if both arguments
are 1value."
(cl-case val
- (1value result)
+ (testcover-1value result)
(maybe (and result 'maybe))
(nil nil)))
(defun testcover-analyze-coverage-compose (forms func)
"Analyze a list of FORMS for code coverage using FUNC.
The list is 1valued if all of its constituent elements are also 1valued."
- (let ((result '1value))
+ (let ((result 'testcover-1value))
(while (consp forms)
(setq result (testcover-coverage-combine result (funcall func (car forms))))
(setq forms (cdr forms)))
@@ -652,7 +652,7 @@ The list is 1valued if all of its constituent elements are also 1valued."
(defun testcover-analyze-coverage-backquote (bq-list)
"Analyze BQ-LIST, the body of a backquoted list, for code coverage."
- (let ((result '1value))
+ (let ((result 'testcover-1value))
(while (consp bq-list)
(let ((form (car bq-list))
val)
@@ -670,7 +670,7 @@ The list is 1valued if all of its constituent elements are also 1valued."
"Analyze a single FORM from a backquoted list for code coverage."
(cond
((vectorp form) (testcover-analyze-coverage-backquote (append form nil)))
- ((atom form) '1value)
+ ((atom form) 'testcover-1value)
((memq (car form) (list '\, '\,@))
(testcover-analyze-coverage (cadr form)))
(t (testcover-analyze-coverage-backquote form))))