branch: elpa/loopy
commit ca810b722968b515df9482c3a665be7a5e0195cb
Author: okamsn <[email protected]>
Commit: GitHub <[email protected]>

    Remove Pcase workarounds for Emacs 27. (#253)
    
    - In `loopy-destructure.el`, don't use methods other than
      `pcase-compile-patterns` for getting Pcase variables.  Now that we depend 
on
      at least Emacs 28.1, we can stop using the older method.
    
    - Simplify `loopy--pcase-destructure-for-iteration`
      and `loopy--pcase-parse-for-destructuring-accumulation-command`.
      - Stop creating a capturing variable to use with the erroring case.
        Instead, use a generated symbol as the match condition.
        Create the erroring branch using the new function
        `loopy--pcase-make-erroring-branch`.
      - Make it clearer which generated code is always used.
    
    - Remove `loopy--pcase-let-workaround`.  This macro is no longer needed
      now that minimum Emacs version is 28.
    
    - Revert `loopy-let*` back to a wrapper of (possible multiple uses of) 
`pcase`.
      Do this instead of calling `loopy--pcase-destructure-for-iteration` 
directly.
    
    - Copy definition of `pcase--flip` instead of using it internally.
      In Emacs 30, we no longer need the functionality of `pcase--flip`.
---
 lisp/loopy-destructure.el | 465 ++++++++++++++++++----------------------------
 lisp/loopy-misc.el        |  14 --
 lisp/loopy.el             |  63 +++----
 3 files changed, 209 insertions(+), 333 deletions(-)

diff --git a/lisp/loopy-destructure.el b/lisp/loopy-destructure.el
index 10efeae2249..3624277dea9 100644
--- a/lisp/loopy-destructure.el
+++ b/lisp/loopy-destructure.el
@@ -266,78 +266,74 @@ Type is one of `list' or `array'."
         (default)
         (supplied)
         (len))
-    (loopy--pcase-let-workaround (var2 def2 sup2)
-      (pcase form
-        ;; Uses `nil' if not long enough.
-        ((and (seq var2 def2 sup2) form2) (setq var var2
-                                                default def2
-                                                supplied sup2
-                                                len (seq-length form2)))
-        (form2 (setq var form2
-                     len 0))))
+    (pcase form
+      ;; Uses `nil' if not long enough.
+      ((and (seq var2 def2 sup2) form2) (setq var var2
+                                              default def2
+                                              supplied sup2
+                                              len (seq-length form2)))
+      (form2 (setq var form2
+                   len 0)))
     (list var default supplied len)))
 
 (defun loopy--get-&key-spec (var-form)
   "Get the spec of `&key' VAR-FORM as (KEY VAR DEFAULT SUPPLIED)."
-  (loopy--pcase-let-workaround (key var default supplied)
-    (pcase-let (((or (or (seq (seq key var) default supplied)
-                         (seq (seq key var) default)
-                         (seq (seq key var)))
-                     (and (or (seq var default supplied)
-                              (seq var default)
-                              (seq var)
-                              (and (pred symbolp)
-                                   var))
-                          ;; Strip a leading underscore, since it
-                          ;; only means that this argument is
-                          ;; unused, but shouldn't affect the
-                          ;; key's name (bug#12367).
-                          (let key (if (seqp var)
-                                       (signal 'loopy-&key-key-from-sequence
-                                               (list var-form))
-                                     (intern
-                                      (format ":%s"
-                                              (let ((name (symbol-name var)))
-                                                (if (eq ?_ (aref name 0))
-                                                    (substring name 1)
-                                                  name))))))))
-                 var-form))
-      (unless var
-        (signal 'loopy-&key-var-malformed
-                (list var-form)))
-      (list key var default supplied))))
+  (pcase-let (((or (or (seq (seq key var) default supplied)
+                       (seq (seq key var) default)
+                       (seq (seq key var)))
+                   (and (or (seq var default supplied)
+                            (seq var default)
+                            (seq var)
+                            (and (pred symbolp)
+                                 var))
+                        ;; Strip a leading underscore, since it
+                        ;; only means that this argument is
+                        ;; unused, but shouldn't affect the
+                        ;; key's name (bug#12367).
+                        (let key (if (seqp var)
+                                     (signal 'loopy-&key-key-from-sequence
+                                             (list var-form))
+                                   (intern
+                                    (format ":%s"
+                                            (let ((name (symbol-name var)))
+                                              (if (eq ?_ (aref name 0))
+                                                  (substring name 1)
+                                                name))))))))
+               var-form))
+    (unless var
+      (signal 'loopy-&key-var-malformed
+              (list var-form)))
+    (list key var default supplied)))
 
 (defun loopy--get-&map-spec (var-form)
   "Get the spec of `&map' VAR-FORM as (KEY VAR DEFAULT SUPPLIED)."
-  (loopy--pcase-let-workaround (key var default supplied)
-    (pcase-let (((or (seq key var default supplied)
-                     (seq key var default)
-                     (seq key var)
-                     (and (or (seq var)
-                              (and (pred symbolp)
-                                   var))
-                          (let key
-                            (if (seqp var-form)
-                                (signal 'loopy-&map-key-from-sequence
-                                        (list var-form))
-                              `(quote ,var)))))
-                 var-form))
-      (unless var
-        (signal 'loopy-&map-var-malformed
-                (list var-form)))
-      (list key var default supplied))))
+  (pcase-let (((or (seq key var default supplied)
+                   (seq key var default)
+                   (seq key var)
+                   (and (or (seq var)
+                            (and (pred symbolp)
+                                 var))
+                        (let key
+                          (if (seqp var-form)
+                              (signal 'loopy-&map-key-from-sequence
+                                      (list var-form))
+                            `(quote ,var)))))
+               var-form))
+    (unless var
+      (signal 'loopy-&map-var-malformed
+              (list var-form)))
+    (list key var default supplied)))
 
 (defun loopy--get-&aux-spec (var-form)
   "Get the spec of `&aux' VAR-FORM as (VAR VAL)."
-  (loopy--pcase-let-workaround (var val)
-    (pcase-let (((or (seq var val)
-                     (seq var)
-                     (and (pred symbolp)
-                          var))
-                 var-form))
-      (unless var
-        (signal 'loopy-&aux-malformed-var (list var-form)))
-      (list var val))))
+  (pcase-let (((or (seq var val)
+                   (seq var)
+                   (and (pred symbolp)
+                        var))
+               var-form))
+    (unless var
+      (signal 'loopy-&aux-malformed-var (list var-form)))
+    (list var val)))
 
 (defun loopy--get-var-list (var-seq)
   "Get the variables in VAR-SEQ as a flat, unordered list."
@@ -347,28 +343,24 @@ Type is one of `list' or `array'."
                               (dolist (val2 (loopy--get-var-list val))
                                 (cl-pushnew val2 result :test #'eq))
                             (cl-pushnew val result)))
-                (opt-fn (val) (loopy--pcase-let-workaround (var supplied)
-                                (seq-let [var _ supplied _]
-                                    (loopy--get-&optional-spec val)
-                                  (fn var)
-                                  (when supplied
-                                    (fn supplied)))))
-                (key-fn (val) (loopy--pcase-let-workaround (var supplied)
-                                (seq-let [_ var _ supplied]
-                                    (loopy--get-&key-spec val)
-                                  (fn var)
-                                  (when supplied
-                                    (fn supplied)))))
-                (map-fn (val) (loopy--pcase-let-workaround (var supplied)
-                                (seq-let [_ var _ supplied]
-                                    (loopy--get-&map-spec val)
-                                  (fn var)
-                                  (when supplied
-                                    (fn supplied)))))
-                (aux-fn (val) (loopy--pcase-let-workaround (var)
-                                (seq-let [var _]
-                                    (loopy--get-&map-spec val)
-                                  (fn var)))))
+                (opt-fn (val) (seq-let [var _ supplied _]
+                                  (loopy--get-&optional-spec val)
+                                (fn var)
+                                (when supplied
+                                  (fn supplied))))
+                (key-fn (val) (seq-let [_ var _ supplied]
+                                  (loopy--get-&key-spec val)
+                                (fn var)
+                                (when supplied
+                                  (fn supplied))))
+                (map-fn (val) (seq-let [_ var _ supplied]
+                                  (loopy--get-&map-spec val)
+                                (fn var)
+                                (when supplied
+                                  (fn supplied))))
+                (aux-fn (val) (seq-let [var _]
+                                  (loopy--get-&map-spec val)
+                                (fn var))))
       (map-do (lambda (k v)
                 (when v
                   (pcase k
@@ -385,6 +377,10 @@ Type is one of `list' or `array'."
 
 ;;;; Pcase pattern
 
+(defmacro loopy--pcase-flip-1 (fn arg2 arg1)
+  "Copied from now obsolete `pcase--flip' for older versions of Emacs."
+  `(,fn ,arg1 ,arg2))
+
 (defun loopy--pcase-flip (fn arg2)
   "Wrapper macro for compatibility with obsoletion of `pcase--flip'.
 
@@ -392,7 +388,7 @@ FN is the function.  ARG2 is the argument to move to the 
second
 position of the call to FN in the pattern."
   (static-if (>= emacs-major-version 30)
       `(,fn _ ,arg2)
-    `(pcase--flip ,fn ,arg2)))
+    `(loopy--pcase-flip-1 ,fn ,arg2)))
 
 (defun loopy--get-var-pattern (var)
   "Get the correct variable pattern.
@@ -431,22 +427,21 @@ MAP-OR-KEY-VARS is whether there are map or key 
variables."
                    (app cdr-safe ,(loopy--pcase-pat-positional-list-pattern
                                    (cdr pos-vars) opt-vars
                                    rest-var map-or-key-vars))))
-   (opt-vars (loopy--pcase-let-workaround (var default supplied)
-               (pcase-let* ((`(,var ,default ,supplied ,_length)
-                             (loopy--get-&optional-spec (car opt-vars)))
-                            (var2 (loopy--get-var-pattern var)))
-                 `(and (pred listp)
-                       (app car-safe (or (and (pred null)
-                                              ,@(when supplied
-                                                  `((let ,supplied nil)))
-                                              (let ,var2 ,default))
-                                         ,(if supplied
-                                              `(and (let ,supplied t)
-                                                    ,var2)
-                                            var2)))
-                       (app cdr-safe ,(loopy--pcase-pat-positional-list-pattern
-                                       nil (cdr opt-vars)
-                                       rest-var map-or-key-vars))))))
+   (opt-vars (pcase-let* ((`(,var ,default ,supplied ,_length)
+                           (loopy--get-&optional-spec (car opt-vars)))
+                          (var2 (loopy--get-var-pattern var)))
+               `(and (pred listp)
+                     (app car-safe (or (and (pred null)
+                                            ,@(when supplied
+                                                `((let ,supplied nil)))
+                                            (let ,var2 ,default))
+                                       ,(if supplied
+                                            `(and (let ,supplied t)
+                                                  ,var2)
+                                          var2)))
+                     (app cdr-safe ,(loopy--pcase-pat-positional-list-pattern
+                                     nil (cdr opt-vars)
+                                     rest-var map-or-key-vars)))))
    (rest-var (loopy--get-var-pattern rest-var))
    ;; `pcase' allows `(,a ,b) to match (1 2 3), so we need to make
    ;; sure there aren't more values left.  However, if we are using
@@ -677,11 +672,10 @@ holding the property list."
   ;; then we can use simpler patterns since we don't need to store the
   ;; value of the key.
   (cl-flet ((get-var-data (var-form)
-              (loopy--pcase-let-workaround (key var default supplied)
-                (pcase-let ((`(,key ,var ,default ,supplied)
-                             (loopy--get-&key-spec var-form)))
-                  (list key (loopy--get-var-pattern var)
-                        default supplied)))))
+              (pcase-let ((`(,key ,var ,default ,supplied)
+                           (loopy--get-&key-spec var-form)))
+                (list key (loopy--get-var-pattern var)
+                      default supplied))))
     (if allow-other-keys
         `(and ,@(mapcar (lambda (var-form)
                           (pcase-let ((`(,key ,var ,default ,supplied) 
(get-var-data var-form))
@@ -761,34 +755,33 @@ holding the property list."
   "Build a `pcase' pattern for the `&map' variables MAP-VARS."
   (let ((mapsym (gensym "map")))
     `(and (pred mapp)
-          ,@(mapcar (loopy--pcase-let-workaround (key var default supplied)
-                      (lambda (var-form)
-                        (pcase-let ((`(,key ,var ,default ,supplied)
-                                     (loopy--get-&map-spec var-form)))
-                          (unless var
-                            (signal 'loopy-&map-var-malformed (list var-form)))
-                          (setq var (loopy--get-var-pattern var))
-                          (cond
-                           (supplied
-                            `(app (lambda (,mapsym)
-                                    ;; The default implementations of `map-elt'
-                                   ;; uses `map-contains-key' (which might be
-                                   ;; expensive) when given a default value, so
-                                   ;; we use a generated default to avoid
-                                   ;; calling it twice.
-                                    ,(let ((defsym (list 'quote (gensym 
"loopy--map-contains-test")))
-                                          (valsym (gensym "loopy--map-elt")))
-                                      (macroexp-let2* nil ((keysym key))
-                                        `(let ((,valsym (map-elt ,mapsym 
,keysym ,defsym)))
-                                           (if (equal ,valsym ,defsym)
-                                               (cons nil ,default)
-                                             (cons t ,valsym))))))
-                                  (,'\` ((,'\, ,supplied) . (,'\, ,var)))))
-                           (default
-                            `(app (lambda (,mapsym) (map-elt ,mapsym ,key 
,default))
-                                  ,var))
-                           (t
-                            `(app ,(loopy--pcase-flip 'map-elt key) ,var))))))
+          ,@(mapcar (lambda (var-form)
+                      (pcase-let ((`(,key ,var ,default ,supplied)
+                                   (loopy--get-&map-spec var-form)))
+                        (unless var
+                          (signal 'loopy-&map-var-malformed (list var-form)))
+                        (setq var (loopy--get-var-pattern var))
+                        (cond
+                         (supplied
+                          `(app (lambda (,mapsym)
+                                  ;; The default implementations of `map-elt'
+                                 ;; uses `map-contains-key' (which might be
+                                 ;; expensive) when given a default value, so
+                                 ;; we use a generated default to avoid
+                                 ;; calling it twice.
+                                  ,(let ((defsym (list 'quote (gensym 
"loopy--map-contains-test")))
+                                        (valsym (gensym "loopy--map-elt")))
+                                    (macroexp-let2* nil ((keysym key))
+                                      `(let ((,valsym (map-elt ,mapsym ,keysym 
,defsym)))
+                                         (if (equal ,valsym ,defsym)
+                                             (cons nil ,default)
+                                           (cons t ,valsym))))))
+                                (,'\` ((,'\, ,supplied) . (,'\, ,var)))))
+                         (default
+                          `(app (lambda (,mapsym) (map-elt ,mapsym ,key 
,default))
+                                ,var))
+                         (t
+                          `(app ,(loopy--pcase-flip 'map-elt key) ,var)))))
                     map-vars))))
 
 (defun loopy--pcase-pat-&aux-pattern (aux-vars)
@@ -885,6 +878,15 @@ See the Info node `(loopy)Basic Destructuring'."
                      (loopy--pcase-pat-&aux-pattern aux-vars))))))))
 
 ;;;; Destructuring for Iteration and Accumulation Commands
+(defun loopy--pcase-make-erroring-branch (pattern)
+  "Create a branch for `pcase-compile-patterns' that reports an error for 
PATTERN."
+  ;; It looks like Pcase provides only a single variable matching the symbol
+  ;; in VARVALS, but we use `alist-get' just to be sure.
+  (cons 'loopy--pcase-unmatched
+        (lambda (varvals &rest _)
+          `(signal 'loopy-bad-run-time-destructuring
+                   (list (quote ,pattern)
+                         ,(car (alist-get 'loopy--pcase-unmatched 
varvals)))))))
 
 (cl-defun loopy--pcase-destructure-for-iteration (var val &key error)
   "Destructure VAL according to VAR as by `pcase-let'.
@@ -900,65 +902,19 @@ the pattern doesn't match."
   (if (symbolp var)
       `((setq ,var ,val)
         ,var)
-    (let ((var-list)
-          (destructuring-expression)
-          (val-holder (gensym "loopy--pcase-workaround")))
-      (cl-flet ((signaler (&rest _)
-                  `(signal 'loopy-bad-run-time-destructuring
-                           (list (quote ,var)
-                                 ,val-holder))))
-        ;; This sets `destructuring-expression' and `var-list'.
-        (setq destructuring-expression
-              ;; This holding variable seems to be needed for the older method,
-              ;; before the introduction of `pcase-compile-patterns'.  In some 
cases,
-              ;; it just evaluates `VAL' repeatedly, which is bad for functions
-              ;; that work with state and bad for efficiency.
-              ;;
-              ;; Regardless, we also use it to report the value that caused the
-              ;; error.
-              `(let ((,val-holder ,val))
-                 ,(if (fboundp 'pcase-compile-patterns)
-                      (pcase-compile-patterns
-                       val-holder
-                       (remq nil
-                             (list (cons var
-                                         (lambda (varvals &rest _)
-                                           (cons 'setq (mapcan (cl-function
-                                                                (lambda ((var 
val &rest rest))
-                                                                  (push var 
var-list)
-                                                                  (list var 
val)))
-                                                               varvals))))
-                                   (when error
-                                     (cons '_ #'signaler)))))
-                    ;; NOTE: In Emacs versions less than 28, this functionality
-                    ;;       technically isn't public, but this is what the 
developers
-                    ;;       recommend.
-                    (pcase--u
-                     (remq
-                      nil
-                      (list (list (pcase--match val-holder
-                                                (pcase--macroexpand
-                                                 (if error
-                                                     var
-                                                   `(or ,var 
pcase--dontcare))))
-                                  (lambda (vars)
-                                    (cons 'setq
-                                          (mapcan (lambda (v)
-                                                    (let ((destr-var (car v))
-                                                          ;; Use `cadr' for 
Emacs 28+, `cdr' for less.
-                                                          (destr-val (funcall 
(eval-when-compile
-                                                                               
 (if (version< emacs-version "28")
-                                                                               
     #'cdr
-                                                                               
   #'cadr))
-                                                                              
v)))
-                                                      (push destr-var var-list)
-                                                      (list destr-var 
destr-val)))
-                                                  vars))))
-                            (when error
-                              (list (pcase--match val-holder
-                                                  (pcase--macroexpand '_))
-                                    #'signaler)))))))))
-      (list destructuring-expression
+    (let* ((var-list nil)
+           (always-used-cases
+            (cons var (lambda (varvals &rest _)
+                        (cons 'setq (mapcan (pcase-lambda (`(,var ,val . 
,rest))
+                                              (push var var-list)
+                                              (list var val))
+                                            varvals))))))
+      (list (pcase-compile-patterns
+             val
+             (if error
+                 (list always-used-cases
+                       (loopy--pcase-make-erroring-branch var))
+               (list always-used-cases)))
             (seq-uniq var-list #'eq)))))
 
 (defun loopy--pcase-destructure-for-with-vars (bindings)
@@ -978,98 +934,39 @@ or, if using implicit variables, a value .  VAL is a 
value, and
 should only be used if VAR-OR-VAL is a variable.  ERROR is when
 an error should be signaled if the pattern doesn't match."
   (let* ((instructions)
-         (full-main-body)
-         ;; This holding variable seems to be needed for the older method,
-         ;; before the introduction of `pcase-compile-patterns'.  In some 
cases,
-         ;; it just evaluates `VAL' repeatedly, which is bad for functions
-         ;; that work with state and bad for efficiency.
-         (value-holder (gensym "loopy--pcase-workaround")))
-    (cl-flet ((signaler (&rest _)
-                `(signal 'loopy-bad-run-time-destructuring
-                         (list (quote ,var)
-                               ,value-holder))))
-      (if (fboundp 'pcase-compile-patterns)
-          (setq full-main-body
-                (pcase-compile-patterns
-                 value-holder
-                 (remq nil
-                       (list (cons var
-                                   (lambda (varvals &rest _)
-                                     (let ((destr-main-body nil))
-                                       (dolist (varval varvals)
-                                         (let ((destr-var (cl-first varval))
-                                               (destr-val (cl-second varval)))
-                                           (loopy--bind-main-body (main-body 
other-instructions)
-                                               (loopy--parse-loop-command
-                                                `(,name ,destr-var ,destr-val 
,@args))
-                                             ;; Just push the other 
instructions, but
-                                             ;; gather the main body 
expressions.
-                                             (dolist (instr other-instructions)
-                                               (push instr instructions))
-                                             (push main-body 
destr-main-body))))
-                                       ;; The lambda returns the destructured 
main body,
-                                       ;; which needs to be wrapped by Pcase's
-                                       ;; destructured bindings.
-                                       ;;
-                                       ;; We keep these in the order returned 
by
-                                       ;; Pcase just in case Pcase uses state
-                                       ;; (such as push and pop).  It does not
-                                       ;; appear to use state, but we do it
-                                       ;; anyway.
-                                       (thread-last destr-main-body
-                                                    nreverse
-                                                    (apply #'append)
-                                                    macroexp-progn))))
-                             (when error
-                               (cons '_ #'signaler))))))
-        ;; NOTE: In Emacs versions less than 28, this functionality technically
-        ;; isn't public, but this is what the developers recommend.
-        (setq full-main-body
-              (pcase--u
-               (remq nil `((,(pcase--match value-holder
-                                           (pcase--macroexpand
-                                            (if error
-                                                var
-                                              `(or ,var pcase--dontcare))))
-                            ,(lambda (vars)
-                               (let ((destr-main-body nil))
-                                 (dolist (v vars)
-                                   (let ((destr-var (car v))
-                                         ;; Use `cadr' for Emacs 28+, `cdr' 
for less.
-                                         (destr-val (funcall (eval-when-compile
-                                                               (if (version< 
emacs-version "28")
-                                                                   #'cdr
-                                                                 #'cadr))
-                                                             v)))
-                                     (loopy--bind-main-body (main-body 
other-instructions)
-                                         (loopy--parse-loop-command
-                                          `(,name ,destr-var ,destr-val 
,@args))
-                                       ;; Just push the other instructions, but
-                                       ;; gather the main body expressions.
-                                       (dolist (instr other-instructions)
-                                         (push instr instructions))
-                                       (push main-body destr-main-body))))
-                                 ;; The lambda returns the destructured main 
body,
-                                 ;; which needs to be wrapped by Pcase's
-                                 ;; destructured bindings.
-                                 ;;
-                                 ;; We keep these in the order returned by
-                                 ;; Pcase just in case Pcase uses state
-                                 ;; (such as push and pop).  It does not
-                                 ;; appear to use state, but we do it
-                                 ;; anyway.
-                                 (thread-last destr-main-body
-                                              nreverse
-                                              (apply #'append)
-                                              macroexp-progn))))
-                           ,(when error
-                              (list (pcase--match value-holder 
(pcase--macroexpand '_))
-                                    #'signaler))))))))
+         (always-used-cases
+          (cons var
+                (lambda (varvals &rest _)
+                  (let ((destr-main-body nil))
+                    (pcase-dolist (`(,destr-var ,destr-val) varvals)
+                      (loopy--bind-main-body (main-body other-instructions)
+                          (loopy--parse-loop-command
+                           `(,name ,destr-var ,destr-val ,@args))
+                        ;; Just push the other instructions, but
+                        ;; gather the main body expressions.
+                        (dolist (instr other-instructions)
+                          (push instr instructions))
+                        (push main-body destr-main-body)))
+                    ;; The lambda returns the destructured main body,
+                    ;; which needs to be wrapped by Pcase's
+                    ;; destructured bindings.
+                    ;;
+                    ;; We keep these in the order returned by
+                    ;; Pcase just in case Pcase uses state
+                    ;; (such as push and pop).  It does not
+                    ;; appear to use state, but we do it
+                    ;; anyway.
+                    (thread-last destr-main-body
+                                 nreverse
+                                 (apply #'append)
+                                 macroexp-progn))))))
     ;; Finally, return the instructions.
-    ;; We don't know all of the cases when the value holder is needed,
-    ;; so we just always use it.
-    `((loopy--main-body (let ((,value-holder ,val))
-                          ,full-main-body))
+    `((loopy--main-body ,(pcase-compile-patterns
+                          val
+                          (if error
+                              (list always-used-cases
+                                    (loopy--pcase-make-erroring-branch var))
+                            (list always-used-cases))))
       ,@(nreverse instructions))))
 
 ;;;; Destructuring Generalized Variables
diff --git a/lisp/loopy-misc.el b/lisp/loopy-misc.el
index cba29d72378..a36c56ed7f3 100644
--- a/lisp/loopy-misc.el
+++ b/lisp/loopy-misc.el
@@ -418,19 +418,5 @@ KEY transforms those elements and ELEMENT."
       ('eq    `(memq   ,element ,list))
       (_ form))))
 
-(cl-defmacro loopy--pcase-let-workaround (variables form)
-  "Wrap FORM in a `let' with VARIABLES bound to nil on Emacs less than 28.
-
-Prior to Emacs 28, it was not guaranteed that `pcase-let' bound
-unmatched variables."
-  (declare (indent 1))
-  (static-if (< emacs-major-version 28)
-      `(let ,(mapcar (lambda (sym) `(,sym nil))
-                     variables)
-         ,(cons 'ignore variables)
-         ,form)
-    (ignore variables)
-    form))
-
 (provide 'loopy-misc)
 ;;; loopy-misc.el ends here
diff --git a/lisp/loopy.el b/lisp/loopy.el
index 1270b67a2ec..b526e057d82 100644
--- a/lisp/loopy.el
+++ b/lisp/loopy.el
@@ -658,21 +658,20 @@ macro `loopy' itself."
        ;; Don't want to accidentally rebind variables to `nil'
        ;; or to accidentally mis-use commands that need
        ;; different initial values.
-       (loopy--pcase-let-workaround (var new-val)
-         (pcase-let ((`(,var ,new-val) instruction-value))
-           (pcase var
-             ((pred loopy--with-bound-p) nil)
-             ((and (app loopy--command-bound-p `(,_place . ,old-val))
-                   (guard (not (equal new-val old-val))))
-              ;; TODO: Switch from raising a warning to raising an error.
-              ;; (signal 'loopy-incompatible-accumulation-initializations
-              ;;         (list :in place :var var :old old-val :new new-val))
-              (display-warning
-               'loopy
-               (format "loopy: Conflicting accumulation starting values: `%s', 
%s, %s\nThis will be an error in the future.  To resolve this error, use `with' 
to explicitly specify a starting value."
-                       var old-val new-val)
-               :warning))
-             (_ (push instruction-value loopy--accumulation-vars))))))
+       (pcase-let ((`(,var ,new-val) instruction-value))
+         (pcase var
+           ((pred loopy--with-bound-p) nil)
+           ((and (app loopy--command-bound-p `(,_place . ,old-val))
+                 (guard (not (equal new-val old-val))))
+            ;; TODO: Switch from raising a warning to raising an error.
+            ;; (signal 'loopy-incompatible-accumulation-initializations
+            ;;         (list :in place :var var :old old-val :new new-val))
+            (display-warning
+             'loopy
+             (format "loopy: Conflicting accumulation starting values: `%s', 
%s, %s\nThis will be an error in the future.  To resolve this error, use `with' 
to explicitly specify a starting value."
+                     var old-val new-val)
+             :warning))
+           (_ (push instruction-value loopy--accumulation-vars)))))
 
       (loopy--other-vars
        (loopy--validate-binding instruction-value)
@@ -1043,7 +1042,8 @@ instead of this macro.
   (declare (debug (&rest [sexp form])))
   (macroexp-progn
    (cl-loop for (var val) on args by #'cddr
-            collect (car (loopy--destructure-for-iteration-default var val)))))
+            collect (car (loopy--pcase-destructure-for-iteration
+                          `(loopy ,var) val :error t)))))
 
 ;;;###autoload
 (defmacro loopy-let* (bindings &rest body)
@@ -1055,25 +1055,18 @@ you wish to use `pcase' destructuring, you should use 
`pcase-let'
 instead of this macro."
   (declare (debug ((&rest [sexp form]) body))
            (indent 1))
-  ;; Because Emacs versions less than 28 weren't guaranteed to bind all
-  ;; variables in Pcase, we need to use the same approach we do for
-  ;; destructuring `with' bindings, instead of just passing the bindings to
-  ;; `pcase' directly.
-  (let ((new-binds))
-    (dolist (bind bindings)
-      (cl-destructuring-bind (var val)
-          bind
-        (if (symbolp var)
-            (push bind new-binds)
-          (let ((sym (gensym)))
-            (push `(,sym ,val) new-binds)
-            (cl-destructuring-bind (var-set-expr var-list)
-                (loopy--pcase-destructure-for-iteration `(loopy ,var) sym 
:error t)
-              (dolist (var var-list)
-                (push var new-binds))
-              (push `(_ ,var-set-expr) new-binds))))))
-    `(let* ,(nreverse new-binds)
-       ,@body)))
+  ;; NOTE: We don't use `pcase-let*' here because we want to keep
+  ;;       the signal of `loopy-bad-run-time-destructuring'.
+  (cl-flet ((pcase-maker ((var val) body)
+              `(pcase ,val
+                 ((loopy ,var) ,body)
+                 (fail (signal 'loopy-bad-run-time-destructuring
+                               (list (quote (loopy ,var)) fail))))))
+    (cl-loop with rev = (reverse bindings)
+             with result = (pcase-maker (car rev) (macroexp-progn body))
+             for bind in (cdr rev)
+             do (setq result (pcase-maker bind result))
+             finally return result)))
 
 ;;;###autoload
 (defmacro loopy-ref (bindings &rest body)


Reply via email to