]> code.delx.au - gnu-emacs/blobdiff - lisp/emacs-lisp/cconv.el
Fix previous rmail-output-read-file-name change
[gnu-emacs] / lisp / emacs-lisp / cconv.el
index 5501c13ee4fd009cbdc63c459a97c5612bbdeb15..5a1d62658488ddd96c14b515892116eeac05f778 100644 (file)
@@ -1,6 +1,6 @@
 ;;; cconv.el --- Closure conversion for statically scoped Emacs lisp. -*- lexical-binding: t; coding: utf-8 -*-
 
 ;;; cconv.el --- Closure conversion for statically scoped Emacs lisp. -*- lexical-binding: t; coding: utf-8 -*-
 
-;; Copyright (C) 2011  Free Software Foundation, Inc.
+;; Copyright (C) 2011-2012  Free Software Foundation, Inc.
 
 ;; Author: Igor Kuzmin <kzuminig@iro.umontreal.ca>
 ;; Maintainer: FSF
 
 ;; Author: Igor Kuzmin <kzuminig@iro.umontreal.ca>
 ;; Maintainer: FSF
 
 ;; This takes a piece of Elisp code, and eliminates all free variables from
 ;; lambda expressions.  The user entry points are cconv-closure-convert and
 
 ;; This takes a piece of Elisp code, and eliminates all free variables from
 ;; lambda expressions.  The user entry points are cconv-closure-convert and
-;; cconv-closure-convert-toplevel(for toplevel forms).
+;; cconv-closure-convert-toplevel (for toplevel forms).
 ;; All macros should be expanded beforehand.
 ;;
 ;; Here is a brief explanation how this code works.
 ;; All macros should be expanded beforehand.
 ;;
 ;; Here is a brief explanation how this code works.
-;; Firstly, we analyse the tree by calling cconv-analyse-form.
+;; Firstly, we analyze the tree by calling cconv-analyse-form.
 ;; This function finds all mutated variables, all functions that are suitable
 ;; for lambda lifting and all variables captured by closure. It passes the tree
 ;; once, returning a list of three lists.
 ;;
 ;; This function finds all mutated variables, all functions that are suitable
 ;; for lambda lifting and all variables captured by closure. It passes the tree
 ;; once, returning a list of three lists.
 ;;
-;; Then we calculate the intersection of first and third lists returned by
+;; Then we calculate the intersection of the first and third lists returned by
 ;; cconv-analyse form to find all mutated variables that are captured by
 ;; closure.
 
 ;; Armed with this data, we call cconv-closure-convert-rec, that rewrites the
 ;; cconv-analyse form to find all mutated variables that are captured by
 ;; closure.
 
 ;; Armed with this data, we call cconv-closure-convert-rec, that rewrites the
-;; tree recursivly, lifting lambdas where possible, building closures where it
+;; tree recursively, lifting lambdas where possible, building closures where it
 ;; is needed and eliminating mutable variables used in closure.
 ;;
 ;; We do following replacements :
 ;; is needed and eliminating mutable variables used in closure.
 ;;
 ;; We do following replacements :
 ;;
 ;;; Code:
 
 ;;
 ;;; Code:
 
-;; TODO:
+;; TODO: (not just for cconv but also for the lexbind changes in general)
+;; - let (e)debug find the value of lexical variables from the stack.
+;; - make eval-region do the eval-sexp-add-defvars dance.
 ;; - byte-optimize-form should be applied before cconv.
 ;; - byte-optimize-form should be applied before cconv.
-;; - maybe unify byte-optimize and compiler-macros.
+;;   OTOH, the warnings emitted by cconv-analyze need to come before optimize
+;;   since afterwards they can because obnoxious (warnings about an "unused
+;;   variable" should not be emitted when the variable use has simply been
+;;   optimized away).
+;; - let macros specify that some let-bindings come from the same source,
+;;   so the unused warning takes all uses into account.
+;; - let interactive specs return a function to build the args (to stash into
+;;   command-history).
 ;; - canonize code in macro-expand so we don't have to handle (let (var) body)
 ;;   and other oddities.
 ;; - canonize code in macro-expand so we don't have to handle (let (var) body)
 ;;   and other oddities.
-;; - 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.
 ;; - new byte codes for unwind-protect, catch, and condition-case so that
 ;;   closures aren't needed at all.
+;; - inline source code of different binding mode by first compiling it.
 ;; - 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,
 ;; - 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,
@@ -81,6 +90,8 @@
 ;; - 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 byte compiler.
 ;; - 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 byte compiler.
+;; - call known non-escaping functions with `goto' rather than `call'.
+;; - optimize mapcar to a while loop.
 
 ;; (defmacro dlet (binders &rest body)
 ;;   ;; Works in both lexical and non-lexical mode.
 
 ;; (defmacro dlet (binders &rest body)
 ;;   ;; Works in both lexical and non-lexical mode.
 ;;     ,@(mapcar (lambda (binder) (if (consp binder) (cadr binder)))
 ;;               binders)))
 
 ;;     ,@(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))
+(eval-when-compile (require 'cl-lib))
 
 (defconst cconv-liftwhen 6
   "Try to do lambda lifting if the number of arguments + free variables
 
 (defconst cconv-liftwhen 6
   "Try to do lambda lifting if the number of arguments + free variables
@@ -139,16 +140,10 @@ Returns a form where all lambdas don't have any free variables."
   (let ((cconv-freevars-alist '())
        (cconv-lambda-candidates '())
        (cconv-captured+mutated '()))
   (let ((cconv-freevars-alist '())
        (cconv-lambda-candidates '())
        (cconv-captured+mutated '()))
-    ;; Analyse form - fill these variables with new information.
+    ;; Analyze form - fill these variables with new information.
     (cconv-analyse-form form '())
     (setq cconv-freevars-alist (nreverse cconv-freevars-alist))
     (cconv-analyse-form form '())
     (setq cconv-freevars-alist (nreverse cconv-freevars-alist))
-    (cconv-closure-convert-rec
-     form                               ; the tree
-     '()                                ;
-     '()                                ; fvrs initially empty
-     '()                                ; envs initially empty
-     '()
-     )))
+    (cconv-convert form nil nil))) ; Env initially empty.
 
 (defconst cconv--dummy-var (make-symbol "ignored"))
 
 
 (defconst cconv--dummy-var (make-symbol "ignored"))
 
@@ -178,7 +173,7 @@ Returns a form where all lambdas don't have any free variables."
   ;; Here we assume that X appears at most once in M.
   (let* ((b (assq x m))
          (res (if b (remq b m) m)))
   ;; Here we assume that X appears at most once in M.
   (let* ((b (assq x m))
          (res (if b (remq b m) m)))
-    (assert (null (assq x res))) ;; Check the assumption was warranted.
+    (cl-assert (null (assq x res))) ;; Check the assumption was warranted.
     res))
 
 (defun cconv--map-diff-set (m s)
     res))
 
 (defun cconv--map-diff-set (m s)
@@ -189,71 +184,79 @@ Returns a form where all lambdas don't have any free variables."
       (unless (memq (car b) s) (push b res)))
     (nreverse res)))
 
       (unless (memq (car b) s) (push b res)))
     (nreverse res)))
 
-(defun cconv-closure-convert-function (fvrs vars emvrs envs lmenvs body-forms
-                                            parentform)
-  (assert (equal body-forms (caar cconv-freevars-alist)))
-  (let* ((fvrs-new (cconv--set-diff fvrs vars)) ; Remove vars from fvrs.
-         (fv (cdr (pop cconv-freevars-alist)))
-         (body-forms-new '())
+(defun cconv--convert-function (args body env parentform)
+  (cl-assert (equal body (caar cconv-freevars-alist)))
+  (let* ((fvs (cdr (pop cconv-freevars-alist)))
+         (body-new '())
          (letbind '())
          (letbind '())
-         (envector nil))
-    (when fv
-      ;; Here we form our environment vector.
-
-      (dolist (elm fv)
-        (push
-         (cconv-closure-convert-rec
-          ;; Remove `elm' from `emvrs' for this call because in case
-          ;; `elm' is a variable that's wrapped in a cons-cell, we
-          ;; want to put the cons-cell itself in the closure, rather
-          ;; than just a copy of its current content.
-          elm (remq elm emvrs) fvrs envs lmenvs)
-         envector))                     ; Process vars for closure vector.
-      (setq envector (reverse envector))
-      (setq envs fv)
-      (setq fvrs-new fv))               ; Update substitution list.
-
-    (setq emvrs (cconv--set-diff emvrs vars))
-    (setq lmenvs (cconv--map-diff-set lmenvs vars))
-       
-    ;; The difference between envs and fvrs is explained
-    ;; in comment in the beginning of the function.
-    (dolist (var vars)
-      (when (member (cons (list var) parentform) cconv-captured+mutated)
-        (push var emvrs)
-        (push `(,var (list ,var)) letbind)))
-    (dolist (elm body-forms)            ; convert function body
-      (push (cconv-closure-convert-rec
-             elm emvrs fvrs-new envs lmenvs)
-            body-forms-new))
-
-    (setq body-forms-new
-          (if letbind `((let ,letbind . ,(reverse body-forms-new)))
-            (reverse body-forms-new)))
+         (envector ())
+         (i 0)
+         (new-env ()))
+    ;; Build the "formal and actual envs" for the closure-converted function.
+    (dolist (fv fvs)
+      (let ((exp (or (cdr (assq fv env)) fv)))
+        (pcase exp
+          ;; If `fv' is a variable that's wrapped in a cons-cell,
+          ;; we want to put the cons-cell itself in the closure,
+          ;; rather than just a copy of its current content.
+          (`(car ,iexp . ,_)
+           (push iexp envector)
+           (push `(,fv . (car (internal-get-closed-var ,i))) new-env))
+          (_
+           (push exp envector)
+           (push `(,fv . (internal-get-closed-var ,i)) new-env))))
+      (setq i (1+ i)))
+    (setq envector (nreverse envector))
+    (setq new-env (nreverse new-env))
+
+    (dolist (arg args)
+      (if (not (member (cons (list arg) parentform) cconv-captured+mutated))
+          (if (assq arg new-env) (push `(,arg) new-env))
+        (push `(,arg . (car ,arg)) new-env)
+        (push `(,arg (list ,arg)) letbind)))
+
+    (setq body-new (mapcar (lambda (form)
+                             (cconv-convert form new-env nil))
+                           body))
+
+    (when letbind
+      (let ((special-forms '()))
+        ;; Keep special forms at the beginning of the body.
+        (while (or (stringp (car body-new)) ;docstring.
+                   (memq (car-safe (car body-new)) '(interactive declare)))
+          (push (pop body-new) special-forms))
+        (setq body-new
+              `(,@(nreverse special-forms) (let ,letbind . ,body-new)))))
 
     (cond
 
     (cond
-                                       ;if no freevars - do nothing
-     ((null envector)
-      `(function (lambda ,vars . ,body-forms-new)))
-                                        ; 1 free variable - do not build vector
+     ((null envector)                   ;if no freevars - do nothing
+      `(function (lambda ,args . ,body-new)))
      (t
       `(internal-make-closure
      (t
       `(internal-make-closure
-        ,vars ,envector . ,body-forms-new)))))
+        ,args ,envector . ,body-new)))))
 
 
-(defun cconv-closure-convert-rec (form emvrs fvrs envs lmenvs)
+(defun cconv-convert (form env extend)
   ;; This function actually rewrites the tree.
   ;; 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
-within current environment.
-- ENVS is an environment(list of free variables) of current closure.
-Initially empty.
-- FVRS is a list of variables to substitute in each context.
-Initially empty.
+  "Return FORM with all its lambdas changed so they are closed.
+ENV is a lexical environment mapping variables to the expression
+used to get its value.  This is used for variables that are copied into
+closures, moved into cons cells, ...
+ENV is a list where each entry takes the shape either:
+ (VAR . (car EXP)): VAR has been moved into the car of a cons-cell, and EXP
+    is an expression that evaluates to this cons-cell.
+ (VAR . (internal-get-closed-var N)): VAR has been copied into the closure
+    environment's Nth slot.
+ (VAR . (apply-partially F ARG1 ARG2 ..)): VAR has been λ-lifted and takes
+    additional arguments ARGs.
+EXTEND is a list of variables which might need to be accessed even from places
+where they are shadowed, because some part of ENV causes them to be used at
+places where they originally did not directly appear."
+  (cl-assert (not (delq nil (mapcar (lambda (mapping)
+                                      (if (eq (cadr mapping) 'apply-partially)
+                                          (cconv--set-diff (cdr (cddr mapping))
+                                                           extend)))
+                                    env))))
 
 
-Returns a form where all lambdas don't have any free variables."
   ;; What's the difference between fvrs and envs?
   ;; Suppose that we have the code
   ;; (lambda (..) fvr (let ((fvr 1)) (+ fvr 1)))
   ;; What's the difference between fvrs and envs?
   ;; Suppose that we have the code
   ;; (lambda (..) fvr (let ((fvr 1)) (+ fvr 1)))
@@ -266,18 +269,12 @@ Returns a form where all lambdas don't have any free variables."
   ;; so we never touch it(unless we enter to the other closure).
   ;;(if (listp form) (print (car form)) form)
   (pcase form
   ;; so we never touch it(unless we enter to the other closure).
   ;;(if (listp form) (print (car form)) form)
   (pcase form
-    (`(,(and letsym (or `let* `let)) ,binders . ,body-forms)
+    (`(,(and letsym (or `let* `let)) ,binders . ,body)
 
                                        ; let and let* special forms
 
                                        ; let and let* special forms
-     (let ((body-forms-new '())
-           (binders-new '())
-           ;; next for variables needed for delayed push
-           ;; because we should process <value(s)>
-           ;; before we change any arguments
-           (lmenvs-new '())             ;needed only in case of let
-           (emvrs-new '())              ;needed only in case of let
-           (emvr-push)                  ;needed only in case of let*
-           (lmenv-push))                ;needed only in case of let*
+     (let ((binders-new '())
+           (new-env env)
+           (new-extend extend))
 
        (dolist (binder binders)
          (let* ((value nil)
 
        (dolist (binder binders)
          (let* ((value nil)
@@ -288,373 +285,213 @@ Returns a form where all lambdas don't have any free variables."
                 (new-val
                  (cond
                   ;; Check if var is a candidate for lambda lifting.
                 (new-val
                  (cond
                   ;; Check if var is a candidate for lambda lifting.
-                  ((member (cons binder form) cconv-lambda-candidates)
-                   (assert (and (eq (car value) 'function)
-                                (eq (car (cadr value)) 'lambda)))
-                   (assert (equal (cddr (cadr value))
-                                  (caar cconv-freevars-alist)))
-                  ;; Peek at the freevars to decide whether to λ-lift.
-                   (let* ((fv (cdr (car cconv-freevars-alist)))
-                          (funargs (cadr (cadr value)))
-                          (funcvars (append fv funargs))
-                          (funcbodies (cddadr value)) ; function bodies
-                          (funcbodies-new '()))
+                  ((and (member (cons binder form) cconv-lambda-candidates)
+                        (progn
+                          (cl-assert (and (eq (car value) 'function)
+                                          (eq (car (cadr value)) 'lambda)))
+                          (cl-assert (equal (cddr (cadr value))
+                                            (caar cconv-freevars-alist)))
+                          ;; Peek at the freevars to decide whether to λ-lift.
+                          (let* ((fvs (cdr (car cconv-freevars-alist)))
+                                 (fun (cadr value))
+                                 (funargs (cadr fun))
+                                 (funcvars (append fvs funargs)))
                                        ; lambda lifting condition
                                        ; lambda lifting condition
-                     (if (or (not fv) (< cconv-liftwhen (length funcvars)))
-                                       ; do not lift
-                         (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.
-                              (cdr cconv-freevars-alist))
-                         (dolist (elm2 funcbodies)
-                           (push        ; convert function bodies
-                            (cconv-closure-convert-rec
-                             elm2 emvrs nil envs lmenvs)
-                            funcbodies-new))
-                         (if (eq letsym 'let*)
-                             (setq lmenv-push (cons var fv))
-                           (push (cons var fv) lmenvs-new))
-                                       ; push lifted function
-
-                         `(function .
-                                    ((lambda ,funcvars .
-                                       ,(reverse funcbodies-new))))))))
+                            (and fvs (>= cconv-liftwhen (length funcvars))))))
+                                       ; Lift.
+                   (let* ((fvs (cdr (pop cconv-freevars-alist)))
+                          (fun (cadr value))
+                          (funargs (cadr fun))
+                          (funcvars (append fvs funargs))
+                          (funcbody (cddr fun))
+                          (funcbody-env ()))
+                     (push `(,var . (apply-partially ,var . ,fvs)) new-env)
+                     (dolist (fv fvs)
+                       (cl-pushnew fv new-extend)
+                       (if (and (eq 'car (car-safe (cdr (assq fv env))))
+                                (not (memq fv funargs)))
+                           (push `(,fv . (car ,fv)) funcbody-env)))
+                     `(function (lambda ,funcvars .
+                                  ,(mapcar (lambda (form)
+                                             (cconv-convert
+                                              form funcbody-env nil))
+                                           funcbody)))))
 
                   ;; Check if it needs to be turned into a "ref-cell".
                   ((member (cons binder form) cconv-captured+mutated)
                    ;; Declared variable is mutated and captured.
 
                   ;; Check if it needs to be turned into a "ref-cell".
                   ((member (cons binder form) cconv-captured+mutated)
                    ;; Declared variable is mutated and captured.
-                   (prog1
-                       `(list ,(cconv-closure-convert-rec
-                                value emvrs
-                                fvrs envs lmenvs))
-                     (if (eq letsym 'let*)
-                         (setq emvr-push var)
-                       (push var emvrs-new))))
+                   (push `(,var . (car ,var)) new-env)
+                   `(list ,(cconv-convert value env extend)))
 
                   ;; Normal default case.
                   (t
 
                   ;; Normal default case.
                   (t
-                   (cconv-closure-convert-rec
-                    value emvrs fvrs envs lmenvs)))))
-
-           ;; this piece of code below letbinds free
-           ;; variables  of a lambda lifted function
-           ;; if they are redefined in this let
-           ;; example:
-           ;; (let* ((fun (lambda (x) (+ x y))) (y 1)) (funcall fun 1))
-           ;; Here we can not pass y as parameter because it is
-           ;; redefined. We add a (closed-y y) declaration.
-           ;; We do that even if the function is not used inside
-           ;; this let(*). The reason why we ignore this case is
-           ;; that we can't "look forward" to see if the function
-           ;; is called there or not. To treat well this case we
-           ;; need to traverse the tree one more time to collect this
-           ;; data, and I think that it's not worth it.
+                   (if (assq var new-env) (push `(,var) new-env))
+                   (cconv-convert value env extend)))))
+
+           ;; The piece of code below letbinds free variables of a λ-lifted
+           ;; function if they are redefined in this let, example:
+           ;;   (let* ((fun (lambda (x) (+ x y))) (y 1)) (funcall fun 1))
+           ;; Here we can not pass y as parameter because it is redefined.
+           ;; So we add a (closed-y y) declaration.  We do that even if the
+           ;; function is not used inside this let(*).  The reason why we
+           ;; ignore this case is that we can't "look forward" to see if the
+           ;; function is called there or not.  To treat this case better we'd
+           ;; need to traverse the tree one more time to collect this data, and
+           ;; I think that it's not worth it.
+           (when (memq var new-extend)
+             (let ((closedsym
+                    (make-symbol (concat "closed-" (symbol-name var)))))
+               (setq new-env
+                     (mapcar (lambda (mapping)
+                               (if (not (eq (cadr mapping) 'apply-partially))
+                                   mapping
+                                 (cl-assert (eq (car mapping) (nth 2 mapping)))
+                                 `(,(car mapping)
+                                   apply-partially
+                                   ,(car mapping)
+                                   ,@(mapcar (lambda (arg)
+                                               (if (eq var arg)
+                                                   closedsym arg))
+                                             (nthcdr 3 mapping)))))
+                             new-env))
+               (setq new-extend (remq var new-extend))
+               (push closedsym new-extend)
+               (push `(,closedsym ,var) binders-new)))
 
 
-           (when (eq letsym 'let*)
-             (let ((closedsym '())
-                   (new-lmenv '())
-                   (old-lmenv '()))
-               (dolist (lmenv lmenvs)
-                 (when (memq var (cdr lmenv))
-                   (setq closedsym
-                         (make-symbol
-                          (concat "closed-" (symbol-name var))))
-                   (setq new-lmenv (list (car lmenv)))
-                   (dolist (frv (cdr lmenv)) (if (eq frv var)
-                                                 (push closedsym new-lmenv)
-                                               (push frv new-lmenv)))
-                   (setq new-lmenv (reverse new-lmenv))
-                   (setq old-lmenv lmenv)))
-               (when new-lmenv
-                 (setq lmenvs (remq old-lmenv lmenvs))
-                 (push new-lmenv lmenvs)
-                 (push `(,closedsym ,var) binders-new))))
            ;; We push the element after redefined free variables are
            ;; processed.  This is important to avoid the bug when free
            ;; variable and the function have the same name.
            (push (list var new-val) binders-new)
 
            ;; We push the element after redefined free variables are
            ;; processed.  This is important to avoid the bug when free
            ;; variable and the function have the same name.
            (push (list var new-val) binders-new)
 
-           (when (eq letsym 'let*)      ; update fvrs
-             (setq fvrs (remq var fvrs))
-             (setq emvrs (remq var emvrs)) ; remove if redefined
-             (when emvr-push
-               (push emvr-push emvrs)
-               (setq emvr-push nil))
-             (setq lmenvs (cconv--map-diff-elem lmenvs var))
-             (when lmenv-push
-               (push lmenv-push lmenvs)
-               (setq lmenv-push nil)))
-           ))                          ; end of dolist over binders
-       (when (eq letsym 'let)
-
-         ;; Here we update emvrs, fvrs and lmenvs lists
-         (setq fvrs (cconv--set-diff-map fvrs binders-new))
-         (setq emvrs (cconv--set-diff-map emvrs binders-new))
-         (setq emvrs (append emvrs emvrs-new))
-         (setq lmenvs (cconv--set-diff-map lmenvs binders-new))
-         (setq lmenvs (append lmenvs lmenvs-new))
-
-         ;; Here we do the same letbinding as for let* above
-         ;; to avoid situation when a free variable of a lambda lifted
-         ;; function got redefined.
-
-         (let ((new-lmenv)
-               (var nil)
-               (closedsym nil)
-               (letbinds '()))
-           (dolist (binder binders)
-             (setq var (if (consp binder) (car binder) binder))
-
-             (let ((lmenvs-1 lmenvs))   ; just to avoid manipulating
-               (dolist (lmenv lmenvs-1) ; the counter inside the loop
-                 (when (memq var (cdr lmenv))
-                   (setq closedsym (make-symbol
-                                    (concat "closed-"
-                                            (symbol-name var))))
-
-                   (setq new-lmenv (list (car lmenv)))
-                   (dolist (frv (cdr lmenv))
-                     (push (if (eq frv var) closedsym frv)
-                           new-lmenv))
-                   (setq new-lmenv (reverse new-lmenv))
-                   (setq lmenvs (remq lmenv lmenvs))
-                   (push new-lmenv lmenvs)
-                   (push `(,closedsym ,var) letbinds)
-                   ))))
-           (setq binders-new (append binders-new letbinds))))
-
-       (dolist (elm body-forms)         ; convert body forms
-         (push (cconv-closure-convert-rec
-                elm emvrs fvrs envs lmenvs)
-               body-forms-new))
-       `(,letsym ,(reverse binders-new) . ,(reverse body-forms-new))))
+           (when (eq letsym 'let*)
+             (setq env new-env)
+             (setq extend new-extend))
+           ))                           ; end of dolist over binders
+
+       `(,letsym ,(nreverse binders-new)
+                 . ,(mapcar (lambda (form)
+                              (cconv-convert
+                               form new-env new-extend))
+                            body))))
                                        ;end of let let* forms
 
                                   ; first element is lambda expression
                                        ;end of let let* forms
 
                                   ; first element is lambda expression
-    (`(,(and `(lambda . ,_) fun) . ,other-body-forms)
-
-     (let ((other-body-forms-new '()))
-       (dolist (elm other-body-forms)
-         (push (cconv-closure-convert-rec
-                elm emvrs fvrs envs lmenvs)
-               other-body-forms-new))
-       `(funcall
-         ,(cconv-closure-convert-rec
-           (list 'function fun) emvrs fvrs envs lmenvs)
-         ,@(nreverse other-body-forms-new))))
+    (`(,(and `(lambda . ,_) fun) . ,args)
+     ;; FIXME: it's silly to create a closure just to call it.
+     ;; Running byte-optimize-form earlier will resolve this.
+     `(funcall
+       ,(cconv-convert `(function ,fun) env extend)
+       ,@(mapcar (lambda (form)
+                   (cconv-convert form env extend))
+                 args)))
 
     (`(cond . ,cond-forms)              ; cond special form
 
     (`(cond . ,cond-forms)              ; cond special form
-     (let ((cond-forms-new '()))
-       (dolist (elm cond-forms)
-         (push (let ((elm-new '()))
-                 (dolist (elm-2 elm)
-                   (push
-                    (cconv-closure-convert-rec
-                     elm-2 emvrs fvrs envs lmenvs)
-                    elm-new))
-                 (reverse elm-new))
-               cond-forms-new))
-       (cons 'cond
-             (reverse cond-forms-new))))
-
-    (`(quote . ,_) form)
+     `(cond . ,(mapcar (lambda (branch)
+                         (mapcar (lambda (form)
+                                   (cconv-convert form env extend))
+                                 branch))
+                       cond-forms)))
 
 
-    (`(function (lambda ,vars . ,body-forms)) ; function form
-     (cconv-closure-convert-function
-      fvrs vars emvrs envs lmenvs body-forms form))
+    (`(function (lambda ,args . ,body) . ,_)
+     (cconv--convert-function args body env form))
 
     (`(internal-make-closure . ,_)
 
     (`(internal-make-closure . ,_)
-     (error "Internal byte-compiler error: cconv called twice"))
+     (byte-compile-report-error
+      "Internal error in compiler: cconv called twice?"))
 
 
-    (`(function . ,_) form)             ; Same as quote.
+    (`(quote . ,_) form)
+    (`(function . ,_) form)
 
                                        ;defconst, defvar
 
                                        ;defconst, defvar
-    (`(,(and sym (or `defconst `defvar)) ,definedsymbol . ,body-forms)
-
-     (let ((body-forms-new '()))
-       (dolist (elm body-forms)
-         (push (cconv-closure-convert-rec
-                elm emvrs fvrs envs lmenvs)
-               body-forms-new))
-       (setq body-forms-new (reverse body-forms-new))
-       `(,sym ,definedsymbol . ,body-forms-new)))
-
-                                       ;defun, defmacro
-    (`(,(and sym (or `defun `defmacro))
-       ,func ,vars . ,body-forms)
-
-     ;; The freevar data was pushed onto cconv-freevars-alist
-     ;; but we don't need it.
-     (assert (equal body-forms (caar cconv-freevars-alist)))
-     (assert (null (cdar cconv-freevars-alist)))
-     (setq cconv-freevars-alist (cdr cconv-freevars-alist))
-
-     (let ((body-new '())        ; The whole body.
-           (body-forms-new '())   ; Body w\o docstring and interactive.
-           (letbind '()))
-                                       ; Find mutable arguments.
-       (dolist (elm vars)
-         (when (member (cons (list elm) form) cconv-captured+mutated)
-           (push elm letbind)
-           (push elm emvrs)))
-                                            ;Transform body-forms.
-       (when (stringp (car body-forms))     ; Treat docstring well.
-         (push (car body-forms) body-new)
-         (setq body-forms (cdr body-forms)))
-       (when (eq (car-safe (car body-forms)) 'interactive)
-         (push (cconv-closure-convert-rec
-                (car body-forms)
-                emvrs fvrs envs lmenvs)
-               body-new)
-         (setq body-forms (cdr body-forms)))
-
-       (dolist (elm body-forms)
-         (push (cconv-closure-convert-rec
-                elm emvrs fvrs envs lmenvs)
-               body-forms-new))
-       (setq body-forms-new (reverse body-forms-new))
-
-       (if letbind
-                                       ; Letbind mutable arguments.
-           (let ((binders-new '()))
-             (dolist (elm letbind) (push `(,elm (list ,elm))
-                                         binders-new))
-             (push `(let ,(reverse binders-new) .
-                         ,body-forms-new) body-new)
-             (setq body-new (reverse body-new)))
-         (setq body-new (append (reverse body-new) body-forms-new)))
-
-       `(,sym ,func ,vars . ,body-new)))
+    (`(,(and sym (or `defconst `defvar)) ,definedsymbol . ,forms)
+     `(,sym ,definedsymbol
+            . ,(mapcar (lambda (form) (cconv-convert form env extend))
+                       forms)))
 
                                        ;condition-case
     (`(condition-case ,var ,protected-form . ,handlers)
 
                                        ;condition-case
     (`(condition-case ,var ,protected-form . ,handlers)
-     (let ((newform (cconv-closure-convert-rec
-                     `(function (lambda () ,protected-form))
-                     emvrs fvrs envs lmenvs)))
-       (setq fvrs (remq var fvrs))
+     (let ((newform (cconv--convert-function
+                     () (list protected-form) env form)))
        `(condition-case :fun-body ,newform
          ,@(mapcar (lambda (handler)
                       (list (car handler)
        `(condition-case :fun-body ,newform
          ,@(mapcar (lambda (handler)
                       (list (car handler)
-                            (cconv-closure-convert-rec
-                             (let ((arg (or var cconv--dummy-var)))
-                               `(function (lambda (,arg) ,@(cdr handler))))
-                             emvrs fvrs envs lmenvs)))
+                            (cconv--convert-function
+                             (list (or var cconv--dummy-var))
+                             (cdr handler) env form)))
                     handlers))))
 
     (`(,(and head (or `catch `unwind-protect)) ,form . ,body)
                     handlers))))
 
     (`(,(and head (or `catch `unwind-protect)) ,form . ,body)
-     `(,head ,(cconv-closure-convert-rec form emvrs fvrs envs lmenvs)
-        :fun-body
-        ,(cconv-closure-convert-rec `(function (lambda () ,@body))
-                                    emvrs fvrs envs lmenvs)))
+     `(,head ,(cconv-convert form env extend)
+        :fun-body ,(cconv--convert-function () body env form)))
 
     (`(track-mouse . ,body)
      `(track-mouse
 
     (`(track-mouse . ,body)
      `(track-mouse
-        :fun-body
-        ,(cconv-closure-convert-rec `(function (lambda () ,@body))
-                                    emvrs fvrs envs lmenvs)))
+        :fun-body ,(cconv--convert-function () body env form)))
 
     (`(setq . ,forms)                   ; setq special form
 
     (`(setq . ,forms)                   ; setq special form
-     (let (prognlist sym sym-new value)
+     (let ((prognlist ()))
        (while forms
        (while forms
-         (setq sym (car forms))
-         (setq sym-new (cconv-closure-convert-rec
-                        sym
-                        (remq sym emvrs) fvrs envs lmenvs))
-         (setq value
-               (cconv-closure-convert-rec
-                (cadr forms) emvrs fvrs envs lmenvs))
-         (cond
-          ((memq sym emvrs) (push `(setcar ,sym-new ,value) prognlist))
-          ((symbolp sym-new) (push `(setq ,sym-new ,value) prognlist))
-          ;; This should never happen, but for variables which are
-          ;; mutated+captured+unused, we may end up trying to `setq'
-          ;; on a closed-over variable, so just drop the setq.
-          (t (push value prognlist)))
-         (setq forms (cddr forms)))
+         (let* ((sym (pop forms))
+                (sym-new (or (cdr (assq sym env)) sym))
+                (value (cconv-convert (pop forms) env extend)))
+           (push (pcase sym-new
+                   ((pred symbolp) `(setq ,sym-new ,value))
+                   (`(car ,iexp) `(setcar ,iexp ,value))
+                   ;; This "should never happen", but for variables which are
+                   ;; mutated+captured+unused, we may end up trying to `setq'
+                   ;; on a closed-over variable, so just drop the setq.
+                   (_ ;; (byte-compile-report-error
+                    ;;  (format "Internal error in cconv of (setq %s ..)"
+                    ;;          sym-new))
+                    value))
+                 prognlist)))
        (if (cdr prognlist)
        (if (cdr prognlist)
-           `(progn . ,(reverse prognlist))
+           `(progn . ,(nreverse prognlist))
          (car prognlist))))
 
     (`(,(and (or `funcall `apply) callsym) ,fun . ,args)
          (car prognlist))))
 
     (`(,(and (or `funcall `apply) callsym) ,fun . ,args)
-                                     ; funcall is not a special form
-                                     ; but we treat it separately
-                                     ; for the needs of lambda lifting
-     (let ((fv (cdr (assq fun lmenvs))))
-       (if fv
-           (let ((args-new '())
-                 (processed-fv '()))
-             ;; All args (free variables and actual arguments)
-             ;; should be processed, because they can be fvrs
-             ;; (free variables of another closure)
-             (dolist (fvr fv)
-               (push (cconv-closure-convert-rec
-                      fvr (remq fvr emvrs)
-                      fvrs envs lmenvs)
-                     processed-fv))
-             (setq processed-fv (reverse processed-fv))
-             (dolist (elm args)
-               (push (cconv-closure-convert-rec
-                      elm emvrs fvrs envs lmenvs)
-                     args-new))
-             (setq args-new (append processed-fv (reverse args-new)))
-             (setq fun (cconv-closure-convert-rec
-                        fun emvrs fvrs envs lmenvs))
-             `(,callsym ,fun . ,args-new))
-         (let ((cdr-new '()))
-           (dolist (elm (cdr form))
-             (push (cconv-closure-convert-rec
-                    elm emvrs fvrs envs lmenvs)
-                   cdr-new))
-           `(,callsym . ,(reverse cdr-new))))))
+     ;; These are not special forms but we treat them separately for the needs
+     ;; of lambda lifting.
+     (let ((mapping (cdr (assq fun env))))
+       (pcase mapping
+         (`(apply-partially ,_ . ,(and fvs `(,_ . ,_)))
+          (cl-assert (eq (cadr mapping) fun))
+          `(,callsym ,fun
+                     ,@(mapcar (lambda (fv)
+                                 (let ((exp (or (cdr (assq fv env)) fv)))
+                                   (pcase exp
+                                     (`(car ,iexp . ,_) iexp)
+                                     (_ exp))))
+                               fvs)
+                     ,@(mapcar (lambda (arg)
+                                 (cconv-convert arg env extend))
+                               args)))
+         (_ `(,callsym ,@(mapcar (lambda (arg)
+                                   (cconv-convert arg env extend))
+                                 (cons fun args)))))))
 
     (`(interactive . ,forms)
 
     (`(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,
-                               ; while, until
-     (let ((body-forms-new '()))
-       (dolist (elm body-forms)
-         (push (cconv-closure-convert-rec
-                elm emvrs fvrs envs lmenvs)
-               body-forms-new))
-       (setq body-forms-new (reverse body-forms-new))
-       `(,func . ,body-forms-new)))
-
-    (_
-     (let ((free (memq form fvrs)))
-       (if free                         ;form is a free variable
-           (let* ((numero (- (length fvrs) (length free)))
-                  ;; Replace form => (aref env #)
-                  (var `(internal-get-closed-var ,numero)))
-             (if (memq form emvrs) ; form => (car (aref env #)) if mutable
-                 `(car ,var)
-               var))
-         (if (memq form emvrs)         ; if form is a mutable variable
-             `(car ,form)              ; replace form => (car form)
-           form))))))
+     `(interactive . ,(mapcar (lambda (form)
+                                (cconv-convert form nil nil))
+                              forms)))
+
+    (`(declare . ,_) form)              ;The args don't contain code.
+
+    (`(,func . ,forms)
+     ;; First element is function or whatever function-like forms are: or, and,
+     ;; if, progn, prog1, prog2, while, until
+     `(,func . ,(mapcar (lambda (form)
+                          (cconv-convert form env extend))
+                        forms)))
+
+    (_ (or (cdr (assq form env)) form))))
 
 (unless (fboundp 'byte-compile-not-lexical-var-p)
   ;; Only used to test the code in non-lexbind Emacs.
   (defalias 'byte-compile-not-lexical-var-p 'boundp))
 
 
 (unless (fboundp 'byte-compile-not-lexical-var-p)
   ;; Only used to test the code in non-lexbind Emacs.
   (defalias 'byte-compile-not-lexical-var-p 'boundp))
 
-(defun cconv-analyse-use (vardata form varkind)
-  "Analyse the use of a variable.
+(defun cconv--analyse-use (vardata form varkind)
+  "Analyze 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."
 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."
@@ -663,8 +500,8 @@ FORM is the parent form that binds this var."
     (`(,_ nil nil nil nil) nil)
     (`((,(and (pred (lambda (var) (eq ?_ (aref (symbol-name var) 0)))) var) . ,_)
        ,_ ,_ ,_ ,_)
     (`(,_ 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))
+     (byte-compile-log-warning
+      (format "%s `%S' not left unused" varkind var))))
   (pcase vardata
     (`((,var . ,_) nil ,_ ,_ nil)
      ;; FIXME: This gives warnings in the wrong order, with imprecise line
   (pcase vardata
     (`((,var . ,_) nil ,_ ,_ nil)
      ;; FIXME: This gives warnings in the wrong order, with imprecise line
@@ -673,7 +510,9 @@ FORM is the parent form that binds this var."
               ;; it is often non-trivial for the programmer to avoid such
               ;; unused vars.
               (not (intern-soft var))
               ;; it is often non-trivial for the programmer to avoid such
               ;; unused vars.
               (not (intern-soft var))
-              (eq ?_ (aref (symbol-name var) 0)))
+              (eq ?_ (aref (symbol-name var) 0))
+             ;; As a special exception, ignore "ignore".
+             (eq var 'ignored))
        (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
        (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
@@ -681,11 +520,9 @@ FORM is the parent form that binds this var."
     (`(,binder ,_ t t ,_)
      (push (cons binder form) cconv-captured+mutated))
     (`(,(and binder `(,_ (function (lambda . ,_)))) nil nil nil t)
     (`(,binder ,_ t t ,_)
      (push (cons binder form) cconv-captured+mutated))
     (`(,(and binder `(,_ (function (lambda . ,_)))) nil nil nil t)
-     (push (cons binder form) cconv-lambda-candidates))
-    (`(,_ ,_ ,_ ,_ ,_) nil)
-    (dontcare)))
+     (push (cons binder form) cconv-lambda-candidates))))
 
 
-(defun cconv-analyse-function (args body env parentform)
+(defun cconv--analyse-function (args body env parentform)
   (let* ((newvars nil)
          (freevars (list body))
          ;; We analyze the body within a new environment where all uses are
   (let* ((newvars nil)
          (freevars (list body))
          ;; We analyze the body within a new environment where all uses are
@@ -700,21 +537,21 @@ FORM is the parent form that binds this var."
     (dolist (arg args)
       (cond
        ((byte-compile-not-lexical-var-p arg)
     (dolist (arg args)
       (cond
        ((byte-compile-not-lexical-var-p arg)
-        (byte-compile-report-error
+        (byte-compile-log-warning
          (format "Argument %S is not a lexical variable" arg)))
        ((eq ?& (aref (symbol-name arg) 0)) nil) ;Ignore &rest, &optional, ...
        (t (let ((varstruct (list arg nil nil nil nil)))
             (push (cons (list arg) (cdr varstruct)) newvars)
             (push varstruct newenv)))))
          (format "Argument %S is not a lexical variable" arg)))
        ((eq ?& (aref (symbol-name arg) 0)) nil) ;Ignore &rest, &optional, ...
        (t (let ((varstruct (list arg nil nil nil nil)))
             (push (cons (list arg) (cdr varstruct)) newvars)
             (push varstruct newenv)))))
-    (dolist (form body)                   ;Analyse body forms.
+    (dolist (form body)                   ;Analyze body forms.
       (cconv-analyse-form form newenv))
     ;; Summarize resulting data about arguments.
     (dolist (vardata newvars)
       (cconv-analyse-form form newenv))
     ;; Summarize resulting data about arguments.
     (dolist (vardata newvars)
-      (cconv-analyse-use vardata parentform "argument"))
+      (cconv--analyse-use vardata parentform "argument"))
     ;; Transfer uses collected in `envcopy' (via `newenv') back to `env';
     ;; and compute free variables.
     (while env
     ;; Transfer uses collected in `envcopy' (via `newenv') back to `env';
     ;; and compute free variables.
     (while env
-      (assert (and envcopy (eq (caar env) (caar envcopy))))
+      (cl-assert (and envcopy (eq (caar env) (caar envcopy))))
       (let ((free nil)
             (x (cdr (car env)))
             (y (cdr (car envcopy))))
       (let ((free nil)
             (x (cdr (car env)))
             (y (cdr (car envcopy))))
@@ -728,7 +565,7 @@ FORM is the parent form that binds this var."
 
 (defun cconv-analyse-form (form env)
   "Find mutated variables and variables captured by closure.
 
 (defun cconv-analyse-form (form env)
   "Find mutated variables and variables captured by closure.
-Analyse lambdas if they are suitable for lambda lifting.
+Analyze 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.
    I.e. each element has the form (VAR . (READ MUTATED CAPTURED CALLED)).
 - 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)).
@@ -759,24 +596,15 @@ and updates the data stored in ENV."
              (push (cons binder (cdr varstruct)) newvars)
              (push varstruct env))))
 
              (push (cons binder (cdr varstruct)) newvars)
              (push varstruct env))))
 
-       (dolist (form body-forms)          ; Analyse body forms.
+       (dolist (form body-forms)          ; Analyze body forms.
          (cconv-analyse-form form env))
 
        (dolist (vardata newvars)
          (cconv-analyse-form form env))
 
        (dolist (vardata newvars)
-         (cconv-analyse-use vardata form "variable"))))
-
-                                       ; defun special form
-    (`(,(or `defun `defmacro) ,func ,vrs . ,body-forms)
-     (when env
-       (byte-compile-log-warning
-        (format "Function %S will ignore its context %S"
-                func (mapcar #'car env))
-        t :warning))
-     (cconv-analyse-function vrs body-forms nil form))
+         (cconv--analyse-use vardata form "variable"))))
 
     (`(function (lambda ,vrs . ,body-forms))
 
     (`(function (lambda ,vrs . ,body-forms))
-     (cconv-analyse-function vrs body-forms env form))
-     
+     (cconv--analyse-function vrs body-forms env form))
+
     (`(setq . ,forms)
      ;; If a local variable (member of env) is modified by setq then
      ;; it is a mutated variable.
     (`(setq . ,forms)
      ;; If a local variable (member of env) is modified by setq then
      ;; it is a mutated variable.
@@ -786,7 +614,9 @@ and updates the data stored in ENV."
        (cconv-analyse-form (cadr forms) env)
        (setq forms (cddr forms))))
 
        (cconv-analyse-form (cadr forms) env)
        (setq forms (cddr forms))))
 
-    (`((lambda . ,_) . ,_)             ; first element is lambda expression
+    (`((lambda . ,_) . ,_)             ; First element is lambda expression.
+     (byte-compile-log-warning
+      "Use of deprecated ((lambda ...) ...) form" t :warning)
      (dolist (exp `((function ,(car form)) . ,(cdr form)))
        (cconv-analyse-form exp env)))
 
      (dolist (exp `((function ,(car form)) . ,(cdr form)))
        (cconv-analyse-form exp env)))
 
@@ -799,21 +629,21 @@ and updates the data stored in ENV."
 
     (`(condition-case ,var ,protected-form . ,handlers)
      ;; FIXME: The bytecode for condition-case forces us to wrap the
 
     (`(condition-case ,var ,protected-form . ,handlers)
      ;; FIXME: The bytecode for condition-case forces us to wrap the
-     ;; form and handlers in closures (for handlers, it's probably
-     ;; unavoidable, but not for the protected form).
-     (cconv-analyse-function () (list protected-form) env form)
+     ;; form and handlers in closures (for handlers, it's understandable
+     ;; but not for the protected form).
+     (cconv--analyse-function () (list protected-form) env form)
      (dolist (handler handlers)
      (dolist (handler handlers)
-       (cconv-analyse-function (if var (list var)) (cdr handler) env form)))
+       (cconv--analyse-function (if var (list var)) (cdr handler) env form)))
 
     ;; FIXME: The bytecode for catch forces us to wrap the body.
     (`(,(or `catch `unwind-protect) ,form . ,body)
      (cconv-analyse-form form env)
 
     ;; FIXME: The bytecode for catch forces us to wrap the body.
     (`(,(or `catch `unwind-protect) ,form . ,body)
      (cconv-analyse-form form env)
-     (cconv-analyse-function () body env form))
+     (cconv--analyse-function () body env form))
 
 
-    ;; FIXME: The bytecode for save-window-excursion and the lack of
-    ;; bytecode for track-mouse forces us to wrap the body.
+    ;; FIXME: The lack of bytecode for track-mouse forces us to wrap the body.
+    ;; `track-mouse' really should be made into a macro.
     (`(track-mouse . ,body)
     (`(track-mouse . ,body)
-     (cconv-analyse-function () body env form))
+     (cconv--analyse-function () body env form))
 
     (`(,(or `defconst `defvar) ,var ,value . ,_)
      (push var byte-compile-bound-variables)
 
     (`(,(or `defconst `defvar) ,var ,value . ,_)
      (push var byte-compile-bound-variables)
@@ -837,7 +667,9 @@ and updates the data stored in ENV."
      ;; variables in the function's enclosing environment, but it doesn't
      ;; seem worth the trouble.
      (dolist (form forms) (cconv-analyse-form form nil)))
      ;; variables in the function's enclosing environment, but it doesn't
      ;; seem worth the trouble.
      (dolist (form forms) (cconv-analyse-form form nil)))
-    
+
+    (`(declare . ,_) nil)               ;The args don't contain code.
+
     (`(,_ . ,body-forms)    ; First element is a function or whatever.
      (dolist (form body-forms) (cconv-analyse-form form env)))
 
     (`(,_ . ,body-forms)    ; First element is a function or whatever.
      (dolist (form body-forms) (cconv-analyse-form form env)))