]> git.eshelyaron.com Git - emacs.git/commitdiff
* lisp/emacs-lisp/macroexp.el: Break cycle with bytecomp/byte-opt
authorStefan Monnier <monnier@iro.umontreal.ca>
Tue, 9 Feb 2021 17:02:25 +0000 (12:02 -0500)
committerStefan Monnier <monnier@iro.umontreal.ca>
Tue, 9 Feb 2021 17:02:25 +0000 (12:02 -0500)
The recent change in macroexp triggered a cyclic dependency error
during eager macroexpansion when neither `bytecomp` nor `byte-opt` had
been byte-compiled yet.  This fixes it by moving the offending
function to macroexp.el.

* lisp/emacs-lisp/macroexp.el (macroexp--unfold-lambda): Move from
byte-opt.el and rename.
(macroexp--expand-all): Use it.

* lisp/emacs-lisp/byte-opt.el (byte-compile-unfold-lambda): Move to
macroexp.el.
(byte-compile-inline-expand, byte-optimize-form-code-walker):
* lisp/emacs-lisp/bytecomp.el (byte-compile-form):
Use `macroexp--unfold-lambda` instead.

lisp/emacs-lisp/byte-opt.el
lisp/emacs-lisp/bytecomp.el
lisp/emacs-lisp/macroexp.el

index abbe2a2e63f0b91a0b0f0db0a89850e4c1804d02..e67077639c28e6f84b014ef09fa85df297165dc2 100644 (file)
                           (byte-compile-preprocess
                            (byte-compile--reify-function fn))))))
            (if (eq (car-safe newfn) 'function)
-               (byte-compile-unfold-lambda `(,(cadr newfn) ,@(cdr form)))
+               (macroexp--unfold-lambda `(,(cadr newfn) ,@(cdr form)))
              ;; This can happen because of macroexp-warn-and-return &co.
              (byte-compile-warn
               "Inlining closure %S failed" name)
 
       (_ ;; Give up on inlining.
        form))))
-
-;; ((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).  But luckily, 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))
-         (arglist (nth 1 lambda))
-         (body (cdr (cdr lambda)))
-         optionalp restp
-         bindings)
-    (if (and (stringp (car body)) (cdr body))
-        (setq body (cdr body)))
-    (if (and (consp (car body)) (eq 'interactive (car (car body))))
-        (setq body (cdr body)))
-    ;; FIXME: The checks below do not belong in an optimization phase.
-    (while arglist
-      (cond ((eq (car arglist) '&optional)
-             ;; ok, I'll let this slide because funcall_lambda() does...
-             ;; (if optionalp (error "multiple &optional keywords in %s" name))
-             (if restp (error "&optional found after &rest in %s" name))
-             (if (null (cdr arglist))
-                 (error "nothing after &optional in %s" name))
-             (setq optionalp t))
-            ((eq (car arglist) '&rest)
-             ;; ...but it is by no stretch of the imagination a reasonable
-             ;; thing that funcall_lambda() allows (&rest x y) and
-             ;; (&rest x &optional y) in arglists.
-             (if (null (cdr arglist))
-                 (error "nothing after &rest in %s" name))
-             (if (cdr (cdr arglist))
-                 (error "multiple vars after &rest in %s" name))
-             (setq restp t))
-            (restp
-             (setq bindings (cons (list (car arglist)
-                                        (and values (cons 'list values)))
-                                  bindings)
-                   values nil))
-            ((and (not optionalp) (null values))
-             (byte-compile-warn "attempt to open-code `%s' with too few arguments" name)
-             (setq arglist nil values 'too-few))
-            (t
-             (setq bindings (cons (list (car arglist) (car values))
-                                  bindings)
-                   values (cdr values))))
-      (setq arglist (cdr arglist)))
-    (if values
-        (progn
-          (or (eq values 'too-few)
-              (byte-compile-warn
-               "attempt to open-code `%s' with too many arguments" name))
-          form)
-
-                                       ;; The following leads to infinite recursion when loading a
-                                       ;; file containing `(defsubst f () (f))', and then trying to
-                                       ;; byte-compile that file.
-                       ;(setq body (mapcar 'byte-optimize-form body)))
-
-      (let ((newform
-             (if bindings
-                 (cons 'let (cons (nreverse bindings) body))
-               (cons 'progn body))))
-        (byte-compile-log "  %s\t==>\t%s" form newform)
-        newform))))
-
 \f
 ;;; implementing source-level optimizers
 
@@ -604,7 +536,7 @@ Same format as `byte-optimize--lexvars', with shared structure and contents.")
        form)
 
       (`((lambda . ,_) . ,_)
-       (let ((newform (byte-compile-unfold-lambda form)))
+       (let ((newform (macroexp--unfold-lambda form)))
         (if (eq newform form)
             ;; Some error occurred, avoid infinite recursion.
             form
index 9429d6a0d5d78a57954ff2ff13017163d65867f7..89068a14f023edcbe4b2e21810c9ca74e150791d 100644 (file)
@@ -195,7 +195,6 @@ otherwise adds \".elc\"."
 (autoload 'byte-optimize-form "byte-opt")
 ;; This is the entry point to the lapcode optimizer pass2.
 (autoload 'byte-optimize-lapcode "byte-opt")
-(autoload 'byte-compile-unfold-lambda "byte-opt")
 
 ;; This is the entry point to the decompiler, which is used by the
 ;; disassembler.  The disassembler just requires 'byte-compile, but
@@ -3277,7 +3276,7 @@ for symbols generated by the byte compiler itself."
      ((and (eq (car-safe (car form)) 'lambda)
            ;; if the form comes out the same way it went in, that's
            ;; because it was malformed, and we couldn't unfold it.
-           (not (eq form (setq form (byte-compile-unfold-lambda form)))))
+           (not (eq form (setq form (macroexp--unfold-lambda form)))))
       (byte-compile-form form byte-compile--for-effect)
       (setq byte-compile--for-effect nil))
      ((byte-compile-normal-call form)))
index e842222b7c314581b0f2ad1f5a9172e3aa0d5d89..042061c44fcf703ccd107a06740dceb83c662005 100644 (file)
@@ -200,6 +200,69 @@ and also to avoid outputting the warning during normal execution."
            new-form))
       new-form)))
 
+(defun macroexp--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).  But luckily, 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))
+         (arglist (nth 1 lambda))
+         (body (cdr (cdr lambda)))
+         optionalp restp
+         bindings)
+    (if (and (stringp (car body)) (cdr body))
+        (setq body (cdr body)))
+    (if (and (consp (car body)) (eq 'interactive (car (car body))))
+        (setq body (cdr body)))
+    ;; FIXME: The checks below do not belong in an optimization phase.
+    (while arglist
+      (cond ((eq (car arglist) '&optional)
+             ;; ok, I'll let this slide because funcall_lambda() does...
+             ;; (if optionalp (error "multiple &optional keywords in %s" name))
+             (if restp (error "&optional found after &rest in %s" name))
+             (if (null (cdr arglist))
+                 (error "nothing after &optional in %s" name))
+             (setq optionalp t))
+            ((eq (car arglist) '&rest)
+             ;; ...but it is by no stretch of the imagination a reasonable
+             ;; thing that funcall_lambda() allows (&rest x y) and
+             ;; (&rest x &optional y) in arglists.
+             (if (null (cdr arglist))
+                 (error "nothing after &rest in %s" name))
+             (if (cdr (cdr arglist))
+                 (error "multiple vars after &rest in %s" name))
+             (setq restp t))
+            (restp
+             (setq bindings (cons (list (car arglist)
+                                        (and values (cons 'list values)))
+                                  bindings)
+                   values nil))
+            ((and (not optionalp) (null values))
+             (setq arglist nil values 'too-few))
+            (t
+             (setq bindings (cons (list (car arglist) (car values))
+                                  bindings)
+                   values (cdr values))))
+      (setq arglist (cdr arglist)))
+    (if values
+        (macroexp--warn-and-return
+         (format (if (eq values 'too-few)
+                     "attempt to open-code `%s' with too few arguments"
+                   "attempt to open-code `%s' with too many arguments")
+                 name)
+         form)
+
+      ;; The following leads to infinite recursion when loading a
+      ;; file containing `(defsubst f () (f))', and then trying to
+      ;; byte-compile that file.
+      ;;(setq body (mapcar 'byte-optimize-form body)))
+
+      (if bindings
+          `(let ,(nreverse bindings) . ,body)
+        (macroexp-progn body)))))
+
 (defun macroexp--expand-all (form)
   "Expand all macros in FORM.
 This is an internal version of `macroexpand-all'.
@@ -245,12 +308,8 @@ Assumes the caller has bound `macroexpand-all-environment'."
        ;; i.e. rewrite it to (let (<args>) <body>).  We'd do it in the optimizer
        ;; anyway, but doing it here (i.e. earlier) can sometimes avoid the
        ;; creation of a closure, thus resulting in much better code.
-       (let ((newform (if (not (fboundp 'byte-compile-unfold-lambda))
-                         'macroexp--not-unfolded
-                       ;; Don't unfold if byte-opt is not yet loaded.
-                       (byte-compile-unfold-lambda form))))
-        (if (or (eq newform 'macroexp--not-unfolded)
-                (eq newform form))
+       (let ((newform (macroexp--unfold-lambda form)))
+        (if (eq newform form)
             ;; Unfolding failed for some reason, avoid infinite recursion.
             (macroexp--cons (macroexp--all-forms fun 2)
                              (macroexp--all-forms args)