* doc/lispref/variables.texi (Scope): Mention the availability of lexbind.
(Lexical Binding): New node. * doc/lispref/eval.texi (Eval): Add `eval's new `lexical' arg. * lisp/emacs-lisp/cconv.el (cconv-liftwhen): Increase threshold. (cconv-closure-convert-rec): Convert interactive spec in empty lexenv. (cconv-analyse-use): Improve unused vars warnings. (cconv-analyse-form): Analyze interactive spec in empty lexenv. * lisp/emacs-lisp/bytecomp.el (byte-compile-lambda): Always byte-compile the interactive spec in lexical-binding mode. (byte-compile-refresh-preloaded): Don't reload byte-compiler files. * lisp/custom.el (custom-initialize-default): Use defvar. (custom-declare-variable): Set the special-variable-p flag. * lisp/help-fns.el (help-make-usage): Drop leading underscores. * lisp/dired.el (dired-revert, dired-make-relative): Mark unused args. (dired-unmark-all-files): Remove unused var `query'. (dired-overwrite-confirmed): Declare. (dired-restore-desktop-buffer): Don't use dynamically scoped arg names. * lisp/mpc.el: Mark unused args. (mpc--faster-toggle): Remove unused var `songnb'. * lisp/server.el (server-kill-buffer-running): Move before first use. * lisp/minibuffer.el: Mark unused args. * src/callint.c (quotify_arg): Simplify the logic. (Fcall_interactively): Use lexical binding when evaluating the interactive spec of a lexically bound function.
This commit is contained in:
parent
39605a343b
commit
d032d5e7df
21 changed files with 750 additions and 531 deletions
|
@ -308,6 +308,10 @@
|
|||
|
||||
;; ((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). 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)))
|
||||
|
|
|
@ -2563,6 +2563,7 @@ If FORM is a lambda or a macro, byte-compile it as a function."
|
|||
;; b-c-lambda didn't produce a compiled-function, so it's either a trivial
|
||||
;; function, or this is Emacs 18, or generate-emacs19-bytecodes is off.
|
||||
((let (tmp)
|
||||
;; FIXME: can this happen?
|
||||
(if (and (setq tmp (assq 'byte-code (cdr-safe (cdr fun))))
|
||||
(null (cdr (memq tmp fun))))
|
||||
;; Generate a make-byte-code call.
|
||||
|
@ -2587,7 +2588,7 @@ If FORM is a lambda or a macro, byte-compile it as a function."
|
|||
(list 'quote fun))))))
|
||||
|
||||
;; Turn a function into an ordinary lambda. Needed for v18 files.
|
||||
(defun byte-compile-byte-code-unmake (function)
|
||||
(defun byte-compile-byte-code-unmake (function) ;FIXME: what is it?
|
||||
(if (consp function)
|
||||
function;;It already is a lambda.
|
||||
(setq function (append function nil)) ; turn it into a list
|
||||
|
@ -2685,16 +2686,19 @@ If FORM is a lambda or a macro, byte-compile it as a function."
|
|||
;; compile it, because `call-interactively' looks at the
|
||||
;; args of `list'. Actually, compile it to get warnings,
|
||||
;; but don't use the result.
|
||||
(let ((form (nth 1 bytecomp-int)))
|
||||
(let* ((form (nth 1 bytecomp-int))
|
||||
(newform (byte-compile-top-level form)))
|
||||
(while (memq (car-safe form) '(let let* progn save-excursion))
|
||||
(while (consp (cdr form))
|
||||
(setq form (cdr form)))
|
||||
(setq form (car form)))
|
||||
(if (eq (car-safe form) 'list)
|
||||
(byte-compile-top-level (nth 1 bytecomp-int))
|
||||
(setq bytecomp-int (list 'interactive
|
||||
(byte-compile-top-level
|
||||
(nth 1 bytecomp-int)))))))
|
||||
(if (and (eq (car-safe form) 'list)
|
||||
;; The spec is evaled in callint.c in dynamic-scoping
|
||||
;; mode, so just leaving the form unchanged would mean
|
||||
;; it won't be eval'd in the right mode.
|
||||
(not lexical-binding))
|
||||
nil
|
||||
(setq bytecomp-int `(interactive ,newform)))))
|
||||
((cdr bytecomp-int)
|
||||
(byte-compile-warn "malformed interactive spec: %s"
|
||||
(prin1-to-string bytecomp-int)))))
|
||||
|
@ -3826,7 +3830,6 @@ Return the offset in the form (VAR . OFFSET)."
|
|||
(byte-compile-push-constant nil)))))
|
||||
|
||||
(defun byte-compile-not-lexical-var-p (var)
|
||||
;; FIXME: this doesn't catch defcustoms!
|
||||
(or (not (symbolp var))
|
||||
(special-variable-p var)
|
||||
(memq var byte-compile-bound-variables)
|
||||
|
@ -4560,7 +4563,14 @@ Use with caution."
|
|||
(setq f (car f))
|
||||
(if (string-match "elc\\'" f) (setq f (substring f 0 -1)))
|
||||
(when (and (file-readable-p f)
|
||||
(file-newer-than-file-p f emacs-file))
|
||||
(file-newer-than-file-p f emacs-file)
|
||||
;; Don't reload the source version of the files below
|
||||
;; because that causes subsequent byte-compilation to
|
||||
;; be a lot slower and need a higher max-lisp-eval-depth,
|
||||
;; so it can cause recompilation to fail.
|
||||
(not (member (file-name-nondirectory f)
|
||||
'("pcase.el" "bytecomp.el" "macroexp.el"
|
||||
"cconv.el" "byte-opt.el"))))
|
||||
(message "Reloading stale %s" (file-name-nondirectory f))
|
||||
(condition-case nil
|
||||
(load f 'noerror nil 'nosuffix)
|
||||
|
|
|
@ -65,21 +65,54 @@
|
|||
;;
|
||||
;;; Code:
|
||||
|
||||
;;; TODO:
|
||||
;; - pay attention to `interactive': its arg is run in an empty env.
|
||||
;; TODO:
|
||||
;; - canonize code in macro-expand so we don't have to handle (let (var) body)
|
||||
;; and other oddities.
|
||||
;; - Change new byte-code representation, so it directly gives the
|
||||
;; number of mandatory and optional arguments as well as whether or
|
||||
;; not there's a &rest arg.
|
||||
;; - warn about unused lexical vars.
|
||||
;; - clean up cconv-closure-convert-rec, especially the `let' binding part.
|
||||
;; - new byte codes for unwind-protect, catch, and condition-case so that
|
||||
;; closures aren't needed at all.
|
||||
;; - a reference to a var that is known statically to always hold a constant
|
||||
;; should be turned into a byte-constant rather than a byte-stack-ref.
|
||||
;; Hmm... right, that's called constant propagation and could be done here
|
||||
;; But when that constant is a function, we have to be careful to make sure
|
||||
;; the bytecomp only compiles it once.
|
||||
;; - Since we know here when a variable is not mutated, we could pass that
|
||||
;; info to the byte-compiler, e.g. by using a new `immutable-let'.
|
||||
;; - add tail-calls to bytecode.c and the bytecompiler.
|
||||
|
||||
;; (defmacro dlet (binders &rest body)
|
||||
;; ;; Works in both lexical and non-lexical mode.
|
||||
;; `(progn
|
||||
;; ,@(mapcar (lambda (binder)
|
||||
;; `(defvar ,(if (consp binder) (car binder) binder)))
|
||||
;; binders)
|
||||
;; (let ,binders ,@body)))
|
||||
|
||||
;; (defmacro llet (binders &rest body)
|
||||
;; ;; Only works in lexical-binding mode.
|
||||
;; `(funcall
|
||||
;; (lambda ,(mapcar (lambda (binder) (if (consp binder) (car binder) binder))
|
||||
;; binders)
|
||||
;; ,@body)
|
||||
;; ,@(mapcar (lambda (binder) (if (consp binder) (cadr binder)))
|
||||
;; binders)))
|
||||
|
||||
;; (defmacro letrec (binders &rest body)
|
||||
;; ;; Only useful in lexical-binding mode.
|
||||
;; ;; As a special-form, we could implement it more efficiently (and cleanly,
|
||||
;; ;; making the vars actually unbound during evaluation of the binders).
|
||||
;; `(let ,(mapcar (lambda (binder) (if (consp binder) (car binder) binder))
|
||||
;; binders)
|
||||
;; ,@(delq nil (mapcar (lambda (binder) (if (consp binder) `(setq ,@binder)))
|
||||
;; binders))
|
||||
;; ,@body))
|
||||
|
||||
(eval-when-compile (require 'cl))
|
||||
|
||||
(defconst cconv-liftwhen 3
|
||||
(defconst cconv-liftwhen 6
|
||||
"Try to do lambda lifting if the number of arguments + free variables
|
||||
is less than this number.")
|
||||
;; List of all the variables that are both captured by a closure
|
||||
|
@ -212,13 +245,13 @@ Returns a form where all lambdas don't have any free variables."
|
|||
;; This function actually rewrites the tree.
|
||||
"Eliminates all free variables of all lambdas in given forms.
|
||||
Arguments:
|
||||
-- FORM is a piece of Elisp code after macroexpansion.
|
||||
-- LMENVS is a list of environments used for lambda-lifting. Initially empty.
|
||||
-- EMVRS is a list that contains mutated variables that are visible
|
||||
- FORM is a piece of Elisp code after macroexpansion.
|
||||
- LMENVS is a list of environments used for lambda-lifting. Initially empty.
|
||||
- EMVRS is a list that contains mutated variables that are visible
|
||||
within current environment.
|
||||
-- ENVS is an environment(list of free variables) of current closure.
|
||||
- ENVS is an environment(list of free variables) of current closure.
|
||||
Initially empty.
|
||||
-- FVRS is a list of variables to substitute in each context.
|
||||
- FVRS is a list of variables to substitute in each context.
|
||||
Initially empty.
|
||||
|
||||
Returns a form where all lambdas don't have any free variables."
|
||||
|
@ -270,10 +303,17 @@ Returns a form where all lambdas don't have any free variables."
|
|||
; lambda lifting condition
|
||||
(if (or (not fv) (< cconv-liftwhen (length funcvars)))
|
||||
; do not lift
|
||||
(cconv-closure-convert-rec
|
||||
value emvrs fvrs envs lmenvs)
|
||||
(progn
|
||||
;; (byte-compile-log-warning
|
||||
;; (format "Not λ-lifting `%S': %d > %d"
|
||||
;; var (length funcvars) cconv-liftwhen))
|
||||
|
||||
(cconv-closure-convert-rec
|
||||
value emvrs fvrs envs lmenvs))
|
||||
; lift
|
||||
(progn
|
||||
;; (byte-compile-log-warning
|
||||
;; (format "λ-lifting `%S'" var))
|
||||
(setq cconv-freevars-alist
|
||||
;; Now that we know we'll λ-lift, consume the
|
||||
;; freevar data.
|
||||
|
@ -579,6 +619,12 @@ Returns a form where all lambdas don't have any free variables."
|
|||
cdr-new))
|
||||
`(,callsym . ,(reverse cdr-new))))))
|
||||
|
||||
(`(interactive . ,forms)
|
||||
`(interactive
|
||||
,@(mapcar (lambda (form)
|
||||
(cconv-closure-convert-rec form nil nil nil nil))
|
||||
forms)))
|
||||
|
||||
(`(,func . ,body-forms) ; first element is function or whatever
|
||||
; function-like forms are:
|
||||
; or, and, if, progn, prog1, prog2,
|
||||
|
@ -608,23 +654,34 @@ Returns a form where all lambdas don't have any free variables."
|
|||
;; Only used to test the code in non-lexbind Emacs.
|
||||
(defalias 'byte-compile-not-lexical-var-p 'boundp))
|
||||
|
||||
(defun cconv-analyse-use (vardata form)
|
||||
(defun cconv-analyse-use (vardata form varkind)
|
||||
"Analyse the use of a variable.
|
||||
VARDATA should be (BINDER READ MUTATED CAPTURED CALLED).
|
||||
VARKIND is the name of the kind of variable.
|
||||
FORM is the parent form that binds this var."
|
||||
;; use = `(,binder ,read ,mutated ,captured ,called)
|
||||
(pcase vardata
|
||||
(`(,binder nil ,_ ,_ nil)
|
||||
;; FIXME: Don't warn about unused fun-args.
|
||||
;; FIXME: Don't warn about uninterned vars or _ vars.
|
||||
;; FIXME: This gives warnings in the wrong order and with wrong line
|
||||
;; number and without function name info.
|
||||
(byte-compile-log-warning (format "Unused variable %S" (car binder))))
|
||||
(`(,_ nil nil nil nil) nil)
|
||||
(`((,(and (pred (lambda (var) (eq ?_ (aref (symbol-name var) 0)))) var) . ,_)
|
||||
,_ ,_ ,_ ,_)
|
||||
(byte-compile-log-warning (format "%s `%S' not left unused" varkind var)))
|
||||
((or `(,_ ,_ ,_ ,_ ,_) dontcare) nil))
|
||||
(pcase vardata
|
||||
(`((,var . ,_) nil ,_ ,_ nil)
|
||||
;; FIXME: This gives warnings in the wrong order, with imprecise line
|
||||
;; numbers and without function name info.
|
||||
(unless (or ;; Uninterned symbols typically come from macro-expansion, so
|
||||
;; it is often non-trivial for the programmer to avoid such
|
||||
;; unused vars.
|
||||
(not (intern-soft var))
|
||||
(eq ?_ (aref (symbol-name var) 0)))
|
||||
(byte-compile-log-warning (format "Unused lexical %s `%S'"
|
||||
varkind var))))
|
||||
;; If it's unused, there's no point converting it into a cons-cell, even if
|
||||
;; it's captures and mutated.
|
||||
;; it's captured and mutated.
|
||||
(`(,binder ,_ t t ,_)
|
||||
(push (cons binder form) cconv-captured+mutated))
|
||||
(`(,(and binder `(,_ (function (lambda . ,_)))) nil nil nil t)
|
||||
;; This is very rare in typical Elisp code. It's probably not really
|
||||
;; worth the trouble to try and use lambda-lifting in Elisp, but
|
||||
;; since we coded it up, we might as well use it.
|
||||
(push (cons binder form) cconv-lambda-candidates))
|
||||
(`(,_ ,_ ,_ ,_ ,_) nil)
|
||||
(dontcare)))
|
||||
|
@ -654,7 +711,7 @@ Returns a form where all lambdas don't have any free variables."
|
|||
(cconv-analyse-form form newenv))
|
||||
;; Summarize resulting data about arguments.
|
||||
(dolist (vardata newvars)
|
||||
(cconv-analyse-use vardata parentform))
|
||||
(cconv-analyse-use vardata parentform "argument"))
|
||||
;; Transfer uses collected in `envcopy' (via `newenv') back to `env';
|
||||
;; and compute free variables.
|
||||
(while env
|
||||
|
@ -673,8 +730,8 @@ Returns a form where all lambdas don't have any free variables."
|
|||
(defun cconv-analyse-form (form env)
|
||||
"Find mutated variables and variables captured by closure.
|
||||
Analyse lambdas if they are suitable for lambda lifting.
|
||||
-- FORM is a piece of Elisp code after macroexpansion.
|
||||
-- ENV is an alist mapping each enclosing lexical variable to its info.
|
||||
- FORM is a piece of Elisp code after macroexpansion.
|
||||
- ENV is an alist mapping each enclosing lexical variable to its info.
|
||||
I.e. each element has the form (VAR . (READ MUTATED CAPTURED CALLED)).
|
||||
This function does not return anything but instead fills the
|
||||
`cconv-captured+mutated' and `cconv-lambda-candidates' variables
|
||||
|
@ -707,7 +764,7 @@ and updates the data stored in ENV."
|
|||
(cconv-analyse-form form env))
|
||||
|
||||
(dolist (vardata newvars)
|
||||
(cconv-analyse-use vardata form))))
|
||||
(cconv-analyse-use vardata form "variable"))))
|
||||
|
||||
; defun special form
|
||||
(`(,(or `defun `defmacro) ,func ,vrs . ,body-forms)
|
||||
|
@ -736,8 +793,7 @@ and updates the data stored in ENV."
|
|||
|
||||
(`(cond . ,cond-forms) ; cond special form
|
||||
(dolist (forms cond-forms)
|
||||
(dolist (form forms)
|
||||
(cconv-analyse-form form env))))
|
||||
(dolist (form forms) (cconv-analyse-form form env))))
|
||||
|
||||
(`(quote . ,_) nil) ; quote form
|
||||
(`(function . ,_) nil) ; same as quote
|
||||
|
@ -773,12 +829,18 @@ and updates the data stored in ENV."
|
|||
(if fdata
|
||||
(setf (nth 4 fdata) t)
|
||||
(cconv-analyse-form fun env)))
|
||||
(dolist (form args)
|
||||
(cconv-analyse-form form env)))
|
||||
(dolist (form args) (cconv-analyse-form form env)))
|
||||
|
||||
(`(interactive . ,forms)
|
||||
;; These appear within the function body but they don't have access
|
||||
;; to the function's arguments.
|
||||
;; We could extend this to allow interactive specs to refer to
|
||||
;; variables in the function's enclosing environment, but it doesn't
|
||||
;; seem worth the trouble.
|
||||
(dolist (form forms) (cconv-analyse-form form nil)))
|
||||
|
||||
(`(,_ . ,body-forms) ; First element is a function or whatever.
|
||||
(dolist (form body-forms)
|
||||
(cconv-analyse-form form env)))
|
||||
(dolist (form body-forms) (cconv-analyse-form form env)))
|
||||
|
||||
((pred symbolp)
|
||||
(let ((dv (assq form env))) ; dv = declared and visible
|
||||
|
|
|
@ -431,7 +431,7 @@ and otherwise defers to REST which is a list of branches of the form
|
|||
rest)))))))
|
||||
((eq 'match (caar matches))
|
||||
(let* ((popmatches (pop matches))
|
||||
(op (car popmatches)) (cdrpopmatches (cdr popmatches))
|
||||
(_op (car popmatches)) (cdrpopmatches (cdr popmatches))
|
||||
(sym (car cdrpopmatches))
|
||||
(upat (cdr cdrpopmatches)))
|
||||
(cond
|
||||
|
@ -520,7 +520,7 @@ and otherwise defers to REST which is a list of branches of the form
|
|||
(pcase--u1 `((match ,sym . ,(cadr upat)))
|
||||
;; FIXME: This codegen is not careful to share its
|
||||
;; code if used several times: code blow up is likely.
|
||||
(lambda (vars)
|
||||
(lambda (_vars)
|
||||
;; `vars' will likely contain bindings which are
|
||||
;; not always available in other paths to
|
||||
;; `rest', so there' no point trying to pass
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue