Subject: Re: bug#11799: 24.1.50; M-x ibuffer: Symbol's function definition is void: cl-minusp

From: Stefan Monnier <[email-masked]>
Date: Wed, 27 Jun 2012 23:31:48 -0400

> start emacs -Q, and do M-x ibuffer.  I get these messages:

> Updating buffer list...
> Formats have changed, recompiling...

> and then this error:

> ibuffer-compile-format: Symbol's function definition is void: cl-minusp

> There is no such error if I eval (require 'cl-lib) before calling
> ibuffer.

I installed the patch below which should hopefully fix it.

        Stefan

=== modified file 'lisp/ChangeLog'
--- lisp/ChangeLog	2012-06-27 21:16:32 +0000
+++ lisp/ChangeLog	2012-06-28 03:29:10 +0000
@@ -1,3 +1,11 @@
+2012-06-28  Stefan Monnier  <[email-masked]>
+
+	Make inlining of other-mode interpreted functions work.
+	* emacs-lisp/bytecomp.el (byte-compile--refiy-function): New fun.
+	(byte-compile): Use it to fix compilation of lexical-binding closures.
+	* emacs-lisp/byte-opt.el (byte-compile-inline-expand): Compile the
+	function, if needed.
+
 2012-06-27  Stefan Monnier  <[email-masked]>
 
 	* help-mode.el (help-make-xrefs): Don't just withstand

=== modified file 'lisp/emacs-lisp/byte-opt.el'
--- lisp/emacs-lisp/byte-opt.el	2012-06-13 13:16:34 +0000
+++ lisp/emacs-lisp/byte-opt.el	2012-06-28 03:25:59 +0000
@@ -266,42 +266,30 @@
        ;; (message "Inlining byte-code for %S!" name)
        ;; The byte-code will be really inlined in byte-compile-unfold-bcf.
        `(,fn ,@(cdr form)))
-      ((or (and `(lambda ,args . ,body) (let env nil))
-           `(closure ,env ,args . ,body))
+      ((or `(lambda . ,_) `(closure . ,_))
        (if (not (or (eq fn localfn)     ;From the same file => same mode.
-                    (eq (not lexical-binding) (not env)))) ;Same mode.
+                    (eq (car fn)        ;Same mode.
+                        (if lexical-binding 'closure 'lambda))))
            ;; While byte-compile-unfold-bcf can inline dynbind byte-code into
            ;; letbind byte-code (or any other combination for that matter), we
            ;; can only inline dynbind source into dynbind source or letbind
            ;; source into letbind source.
-           ;; FIXME: we could of course byte-compile the inlined function
+           (progn
+             ;; We can of course byte-compile the inlined function
            ;; first, and then inline its byte-code.
-           form
-         (let ((renv ()))
-           ;; Turn the function's closed vars (if any) into local let bindings.
-           (dolist (binding env)
-             (cond
-              ((consp binding)
-               ;; We check shadowing by the args, so that the `let' can be
-               ;; moved within the lambda, which can then be unfolded.
-               ;; FIXME: Some of those bindings might be unused in `body'.
-               (unless (memq (car binding) args) ;Shadowed.
-                 (push `(,(car binding) ',(cdr binding)) renv)))
-              ((eq binding t))
-              (t (push `(defvar ,binding) body))))
+             (byte-compile name)
+             `(,(symbol-function name) ,@(cdr form)))
            (let ((newfn (if (eq fn localfn)
                             ;; If `fn' is from the same file, it has already
                             ;; been preprocessed!
                             `(function ,fn)
                           (byte-compile-preprocess
-                           (if (null renv)
-                               `(lambda ,args ,@body)
-                             `(lambda ,args (let ,(nreverse renv) ,@body)))))))
+                         (byte-compile--refiy-function fn)))))
              (if (eq (car-safe newfn) 'function)
                  (byte-compile-unfold-lambda `(,(cadr newfn) ,@(cdr form)))
                (byte-compile-log-warning
                 (format "Inlining closure %S failed" name))
-               form)))))
+             form))))
 
       (t ;; Give up on inlining.
        form))))

=== modified file 'lisp/emacs-lisp/bytecomp.el'
--- lisp/emacs-lisp/bytecomp.el	2012-06-22 13:42:38 +0000
+++ lisp/emacs-lisp/bytecomp.el	2012-06-28 03:23:27 +0000
@@ -2451,7 +2451,26 @@
           (- (position-bytes (point)) (point-min) -1)
         (goto-char (point-max))))))
 
-
+(defun byte-compile--refiy-function (fun)
+  "Return an expression which will evaluate to a function value FUN.
+FUN should be either a `lambda' value or a `closure' value."
+  (pcase-let* (((or (and `(lambda ,args . ,body) (let env nil))
+                    `(closure ,env ,args . ,body)) fun)
+               (renv ()))
+    ;; Turn the function's closed vars (if any) into local let bindings.
+    (dolist (binding env)
+      (cond
+       ((consp binding)
+        ;; We check shadowing by the args, so that the `let' can be moved
+        ;; within the lambda, which can then be unfolded.  FIXME: Some of those
+        ;; bindings might be unused in `body'.
+        (unless (memq (car binding) args) ;Shadowed.
+          (push `(,(car binding) ',(cdr binding)) renv)))
+       ((eq binding t))
+       (t (push `(defvar ,binding) body))))
+    (if (null renv)
+        `(lambda ,args ,@body)
+      `(lambda ,args (let ,(nreverse renv) ,@body)))))
 
 ;;;###autoload
 (defun byte-compile (form)
@@ -2459,23 +2478,29 @@
 If FORM is a lambda or a macro, byte-compile it as a function."
   (displaying-byte-compile-warnings
    (byte-compile-close-variables
-    (let* ((fun (if (symbolp form)
+    (let* ((lexical-binding lexical-binding)
+           (fun (if (symbolp form)
 		    (and (fboundp form) (symbol-function form))
 		  form))
 	   (macro (eq (car-safe fun) 'macro)))
       (if macro
 	  (setq fun (cdr fun)))
-      (cond ((eq (car-safe fun) 'lambda)
+      (when (symbolp form)
+        (unless (memq (car-safe fun) '(closure lambda))
+          (error "Don't know how to compile %S" fun))
+        (setq fun (byte-compile--refiy-function fun))
+        (setq lexical-binding (eq (car fun) 'closure)))
+      (unless (eq (car-safe fun) 'lambda)
+        (error "Don't know how to compile %S" fun))
 	     ;; Expand macros.
              (setq fun (byte-compile-preprocess fun))
 	     ;; Get rid of the `function' quote added by the `lambda' macro.
 	     (if (eq (car-safe fun) 'function) (setq fun (cadr fun)))
-	     (setq fun (if macro
-			   (cons 'macro (byte-compile-lambda fun))
-			 (byte-compile-lambda fun)))
+      (setq fun (byte-compile-lambda fun))
+      (if macro (push 'macro fun))
 	     (if (symbolp form)
-		 (defalias form fun)
-	       fun)))))))
+          (fset form fun)
+        fun)))))
 
 (defun byte-compile-sexp (sexp)
   "Compile and return SEXP."