]> code.delx.au - gnu-emacs/blobdiff - lisp/emacs-lisp/cconv.el
Merge from emacs-24; up to 2012-12-21T07:35:02Z!ueno@gnu.org
[gnu-emacs] / lisp / emacs-lisp / cconv.el
index 38584c437b8713b0d24e83a0055bd1153fdb6461..ee84a9f69ba7551a32198060212aa66892a9e541 100644 (file)
@@ -1,6 +1,6 @@
 ;;; 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-2013 Free Software Foundation, Inc.
 
 ;; 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
-;; 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.
-;; 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.
 ;;
-;; 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
-;; 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 :
 
 ;; 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 danse.
+;; - make eval-region do the eval-sexp-add-defvars dance.
 ;; - byte-optimize-form should be applied before cconv.
 ;;   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).
-;; - turn defun and defmacro into macros (and remove special handling of
-;;   `declare' afterwards).
 ;; - 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
 ;;     ,@(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
@@ -152,7 +140,7 @@ Returns a form where all lambdas don't have any free variables."
   (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-convert form nil nil))) ; Env initially empty.
@@ -185,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)))
-    (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)
@@ -197,7 +185,7 @@ Returns a form where all lambdas don't have any free variables."
     (nreverse res)))
 
 (defun cconv--convert-function (args body env parentform)
-  (assert (equal body (caar cconv-freevars-alist)))
+  (cl-assert (equal body (caar cconv-freevars-alist)))
   (let* ((fvs (cdr (pop cconv-freevars-alist)))
          (body-new '())
          (letbind '())
@@ -263,11 +251,11 @@ ENV is a list where each entry takes the shape either:
 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."
-  (assert (not (delq nil (mapcar (lambda (mapping)
-                                   (if (eq (cadr mapping) 'apply-partially)
-                                       (cconv--set-diff (cdr (cddr mapping))
-                                                        extend)))
-                                 env))))
+  (cl-assert (not (delq nil (mapcar (lambda (mapping)
+                                      (if (eq (cadr mapping) 'apply-partially)
+                                          (cconv--set-diff (cdr (cddr mapping))
+                                                           extend)))
+                                    env))))
 
   ;; What's the difference between fvrs and envs?
   ;; Suppose that we have the code
@@ -299,10 +287,10 @@ places where they originally did not directly appear."
                   ;; Check if var is a candidate for lambda lifting.
                   ((and (member (cons binder form) cconv-lambda-candidates)
                         (progn
-                          (assert (and (eq (car value) 'function)
-                                       (eq (car (cadr value)) 'lambda)))
-                          (assert (equal (cddr (cadr value))
-                                         (caar cconv-freevars-alist)))
+                          (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))
@@ -319,7 +307,7 @@ places where they originally did not directly appear."
                           (funcbody-env ()))
                      (push `(,var . (apply-partially ,var . ,fvs)) new-env)
                      (dolist (fv fvs)
-                       (pushnew fv new-extend)
+                       (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)))
@@ -357,14 +345,14 @@ places where they originally did not directly appear."
                      (mapcar (lambda (mapping)
                                (if (not (eq (cadr mapping) 'apply-partially))
                                    mapping
-                                 (assert (eq (car mapping) (nth 2 mapping)))
-                                 (list* (car mapping)
-                                        'apply-partially
-                                        (car mapping)
-                                        (mapcar (lambda (arg)
-                                                  (if (eq var arg)
-                                                      closedsym arg))
-                                                (nthcdr 3 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)
@@ -420,20 +408,6 @@ places where they originally did not directly appear."
             . ,(mapcar (lambda (form) (cconv-convert form env extend))
                        forms)))
 
-                                       ;defun, defmacro
-    (`(,(and sym (or `defun `defmacro))
-       ,func ,args . ,body)
-     (assert (equal body (caar cconv-freevars-alist)))
-     (assert (null (cdar cconv-freevars-alist)))
-
-     (let ((new (cconv--convert-function args body env form)))
-       (pcase new
-         (`(function (lambda ,newargs . ,new-body))
-          (assert (equal args newargs))
-          `(,sym ,func ,args . ,new-body))
-         (t (byte-compile-report-error
-             (format "Internal error in cconv of (%s %s ...)" sym func))))))
-
                                        ;condition-case
     (`(condition-case ,var ,protected-form . ,handlers)
      (let ((newform (cconv--convert-function
@@ -481,7 +455,7 @@ places where they originally did not directly appear."
      (let ((mapping (cdr (assq fun env))))
        (pcase mapping
          (`(apply-partially ,_ . ,(and fvs `(,_ . ,_)))
-          (assert (eq (cadr mapping) fun))
+          (cl-assert (eq (cadr mapping) fun))
           `(,callsym ,fun
                      ,@(mapcar (lambda (fv)
                                  (let ((exp (or (cdr (assq fv env)) fv)))
@@ -517,7 +491,7 @@ places where they originally did not directly appear."
   (defalias 'byte-compile-not-lexical-var-p 'boundp))
 
 (defun cconv--analyse-use (vardata form varkind)
-  "Analyse the use of a variable.
+  "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."
@@ -569,7 +543,7 @@ FORM is the parent form that binds this var."
        (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)
@@ -577,7 +551,7 @@ FORM is the parent form that binds this var."
     ;; 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))))
@@ -591,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.
-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)).
@@ -622,21 +596,12 @@ and updates the data stored in 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-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))
-
     (`(function (lambda ,vrs . ,body-forms))
      (cconv--analyse-function vrs body-forms env form))
 
@@ -649,7 +614,9 @@ and updates the data stored in ENV."
        (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)))