Function: cconv-analyze-form

cconv-analyze-form is a byte-compiled function defined in cconv.el.gz.

Signature

(cconv-analyze-form FORM ENV)

Documentation

Find mutated variables and variables captured by closure.

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)).
This function does not return anything but instead fills the cconv-var-classification variable and updates the data stored in ENV.

Aliases

cconv-analyse-form (obsolete since 25.1)

Source Code

;; Defined in /usr/src/emacs/lisp/emacs-lisp/cconv.el.gz
(defun cconv-analyze-form (form env)
  "Find mutated variables and variables captured by closure.
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)).
This function does not return anything but instead fills the
`cconv-var-classification' variable and updates the data stored in ENV."
  (pcase form
					; let special form
    (`(,(and (or 'let* 'let) letsym) ,binders . ,body-forms)

     (let ((orig-env env)
           (newvars nil)
           (var nil)
           (cconv--dynbound-variables cconv--dynbound-variables)
           (value nil))
       (dolist (binder binders)
         (if (not (consp binder))
             (progn
               (setq var binder)      ; treat the form (let (x) ...) well
               (setq binder (list binder))
               (setq value nil))
           (setq var (car binder))
           (setq value (cadr binder))

           (cconv-analyze-form value (if (eq letsym 'let*) env orig-env)))

         (if (cconv--not-lexical-var-p var cconv--dynbound-variables)
             (when (boundp 'cconv--dynbindings)
               (push var cconv--dynbindings))
           (cl-pushnew var byte-compile-lexical-variables)
           (let ((varstruct (list var nil nil nil nil)))
             (push (cons binder (cdr varstruct)) newvars)
             (push varstruct env))))

       (dolist (form body-forms)          ; Analyze body forms.
         (cconv-analyze-form form env))

       (dolist (vardata newvars)
         (cconv--analyze-use vardata form "variable"))))

    (`(function (lambda ,vrs . ,body-forms))
     (when (eq :documentation (car-safe (car body-forms)))
       (cconv-analyze-form (cadr (pop body-forms)) env))
     (let ((bf (if (stringp (car body-forms)) (cdr body-forms) body-forms)))
       (when (eq 'interactive (car-safe (car bf)))
         (let ((if (cadr (car bf))))
           (unless (macroexp-const-p if) ;Optimize this common case.
             (let ((f `#'(lambda () ,if)))
               (setf (gethash form cconv--interactive-form-funs) f)
               (cconv-analyze-form f env))))))
     (cconv--analyze-function vrs body-forms env form))

    (`(setq ,var ,expr)
     ;; If a local variable (member of env) is modified by setq then
     ;; it is a mutated variable.
     (let ((v (assq var env))) ; v = non nil if visible
       (when v
         (setf (nth 2 v) t)))
     (cconv-analyze-form expr env))

    (`((lambda . ,_) . ,_)             ; First element is lambda expression.
     (byte-compile-warn-x
      (nth 1 (car form))
      "Use of deprecated ((lambda %s ...) ...) form" (nth 1 (car form)))
     (dolist (exp `((function ,(car form)) . ,(cdr form)))
       (cconv-analyze-form exp env)))

    (`(cond . ,cond-forms)              ; cond special form
     (dolist (forms cond-forms)
       (dolist (form forms) (cconv-analyze-form form env))))

    ;; ((and `(quote ,v . ,_) (guard (assq v env)))
    ;;  (byte-compile-warn
    ;;   "Possible confusion variable/symbol for `%S'" v))

    (`(quote . ,_) nil)                 ; quote form
    (`(function . ,_) nil)              ; same as quote

    (`(condition-case ,var ,protected-form . ,handlers)
     (cconv-analyze-form protected-form env)
     (unless lexical-binding
       (setq var nil))
     (when (and var (symbolp var)
                (cconv--not-lexical-var-p var cconv--dynbound-variables))
       (byte-compile-warn-x
        var "Lexical variable shadows the dynamic variable %S" var))
     (let* ((varstruct (list var nil nil nil nil)))
       (if var (push varstruct env))
       (dolist (handler handlers)
         (dolist (form (cdr handler))
           (cconv-analyze-form form env)))
       (if var (cconv--analyze-use (cons (list var) (cdr varstruct))
                                   form "variable"))))

    ;; FIXME: The bytecode for unwind-protect forces us to wrap the unwind.
    (`(unwind-protect ,form . ,body)
     (cconv-analyze-form form env)
     (cconv--analyze-function () body env form))

    (`(defvar ,var) (push var cconv--dynbound-variables))
    (`(,(or 'defconst 'defvar) ,var ,value . ,_)
     (push var cconv--dynbound-variables)
     (cconv-analyze-form value env))

    (`(,(or 'funcall 'apply) ,fun . ,args)
     ;; Here we ignore fun because funcall and apply are the only two
     ;; functions where we can pass a candidate for lambda lifting as
     ;; argument.  So, if we see fun elsewhere, we'll delete it from
     ;; lambda candidate list.
     (let ((fdata (and (symbolp fun) (assq fun env))))
       (if fdata
           (setf (nth 4 fdata) t)
         (cconv-analyze-form fun env)))
     (dolist (form args) (cconv-analyze-form form env)))

    ;; The form (if any) is converted beforehand as part of the `lambda' case.
    (`(interactive . ,_) nil)

    ;; `declare' should now be macro-expanded away (and if they're not, we're
    ;; in trouble because they *can* contain code nowadays).
    ;; (`(declare . ,_) nil)               ;The args don't contain code.

    (`(,_ . ,body-forms)    ; First element is a function or whatever.
     (unless (listp body-forms)
       (signal 'wrong-type-argument (list 'proper-list-p form)))
     (dolist (form body-forms) (cconv-analyze-form form env)))

    ((pred symbolp)
     (let ((dv (assq form env)))        ; dv = declared and visible
       (when dv
         (setf (nth 1 dv) t))))))