diff options
| author | Mattias EngdegÄrd | 2026-01-19 12:02:12 +0100 |
|---|---|---|
| committer | Mattias EngdegÄrd | 2026-01-22 10:56:59 +0100 |
| commit | 45089f9588e1fccda16fd4a69a618695453c8d88 (patch) | |
| tree | 10d6498bd20dc9cb93c02f1add685eac9935d07e | |
| parent | 2696eff451e6b42edddca66c807c320cb89aee35 (diff) | |
| download | emacs-45089f9588e1fccda16fd4a69a618695453c8d88.tar.gz emacs-45089f9588e1fccda16fd4a69a618695453c8d88.zip | |
* lisp/emacs-lisp/macroexp.el (macroexp--expand-all): Speed up.
Manicure pcase patterns to avoid performance-sapping internal functions
and switch-breaking gaps, resulting in smaller code and less allocation.
| -rw-r--r-- | lisp/emacs-lisp/macroexp.el | 181 |
1 files changed, 99 insertions, 82 deletions
diff --git a/lisp/emacs-lisp/macroexp.el b/lisp/emacs-lisp/macroexp.el index dcb519b33b5..d9ca6f0b19a 100644 --- a/lisp/emacs-lisp/macroexp.el +++ b/lisp/emacs-lisp/macroexp.el | |||
| @@ -469,16 +469,23 @@ Assumes the caller has bound `macroexpand-all-environment'." | |||
| 469 | (macroexp-warn-and-return | 469 | (macroexp-warn-and-return |
| 470 | (format-message "`condition-case' without handlers") | 470 | (format-message "`condition-case' without handlers") |
| 471 | exp-body (list 'suspicious 'condition-case) t form)))) | 471 | exp-body (list 'suspicious 'condition-case) t form)))) |
| 472 | (`(,(or 'defvar 'defconst) ,(and name (pred symbolp)) . ,_) | 472 | (`(,(or 'defvar 'defconst) . ,args) |
| 473 | (push name macroexp--dynvars) | 473 | (if (and (car-safe args) (symbolp (car-safe args))) |
| 474 | (macroexp--all-forms form 2)) | 474 | (progn |
| 475 | (`(function ,(and f `(lambda . ,_))) | 475 | (push (car args) macroexp--dynvars) |
| 476 | (let ((macroexp--dynvars macroexp--dynvars)) | 476 | (macroexp--all-forms form 2)) |
| 477 | (macroexp--cons fn | 477 | form)) |
| 478 | (macroexp--cons (macroexp--all-forms f 2) | 478 | (`(function . ,rest) |
| 479 | nil | 479 | (if (and (eq (car-safe (car-safe rest)) 'lambda) |
| 480 | (cdr form)) | 480 | (null (cdr rest))) |
| 481 | form))) | 481 | (let ((f (car rest))) |
| 482 | (let ((macroexp--dynvars macroexp--dynvars)) | ||
| 483 | (macroexp--cons fn | ||
| 484 | (macroexp--cons (macroexp--all-forms f 2) | ||
| 485 | nil | ||
| 486 | (cdr form)) | ||
| 487 | form))) | ||
| 488 | form)) | ||
| 482 | (`(,(or 'function 'quote) . ,_) form) | 489 | (`(,(or 'function 'quote) . ,_) form) |
| 483 | (`(,(and fun (or 'let 'let*)) . ,(or `(,bindings . ,body) | 490 | (`(,(and fun (or 'let 'let*)) . ,(or `(,bindings . ,body) |
| 484 | pcase--dontcare)) | 491 | pcase--dontcare)) |
| @@ -495,82 +502,88 @@ Assumes the caller has bound `macroexpand-all-environment'." | |||
| 495 | (macroexp--all-forms body)) | 502 | (macroexp--all-forms body)) |
| 496 | (cdr form)) | 503 | (cdr form)) |
| 497 | form))) | 504 | form))) |
| 498 | (`(while) | 505 | (`(while . ,args) |
| 499 | (macroexp-warn-and-return | 506 | (if args |
| 500 | (format-message "missing `while' condition") | 507 | (macroexp--all-forms form 1) |
| 501 | `(signal 'wrong-number-of-arguments '(while 0)) | 508 | (macroexp-warn-and-return |
| 502 | nil 'compile-only form)) | 509 | (format-message "missing `while' condition") |
| 503 | (`(unwind-protect ,expr) | 510 | `(signal 'wrong-number-of-arguments '(while 0)) |
| 504 | (macroexp-warn-and-return | 511 | nil 'compile-only form))) |
| 505 | (format-message "`unwind-protect' without unwind forms") | 512 | (`(unwind-protect . ,args) |
| 506 | (macroexp--expand-all expr) | 513 | (if (cdr-safe args) |
| 507 | (list 'suspicious 'unwind-protect) t form)) | 514 | (macroexp--all-forms form 1) |
| 508 | (`(setq ,(and var (pred symbolp) | 515 | (macroexp-warn-and-return |
| 509 | (pred (not booleanp)) (pred (not keywordp))) | 516 | (format-message "`unwind-protect' without unwind forms") |
| 510 | ,expr) | 517 | (macroexp--expand-all (car-safe args)) |
| 511 | ;; Fast path for the setq common case. | 518 | (list 'suspicious 'unwind-protect) t form))) |
| 512 | (let ((new-expr (macroexp--expand-all expr))) | ||
| 513 | (if (eq new-expr expr) | ||
| 514 | form | ||
| 515 | `(,fn ,var ,new-expr)))) | ||
| 516 | (`(setq . ,args) | 519 | (`(setq . ,args) |
| 517 | ;; Normalize to a sequence of (setq SYM EXPR). | 520 | (let ((nargs (length args)) |
| 518 | ;; Malformed code is translated to code that signals an error | 521 | (var (car-safe args))) |
| 519 | ;; at run time. | 522 | (if (and (= nargs 2) |
| 520 | (let ((nargs (length args))) | 523 | (symbolp var) |
| 521 | (if (oddp nargs) | 524 | (not (booleanp var)) (not (keywordp var))) |
| 522 | (macroexp-warn-and-return | 525 | ;; Fast path for the common case. |
| 523 | (format-message "odd number of arguments in `setq' form") | 526 | (let* ((expr (nth 1 args)) |
| 524 | `(signal 'wrong-number-of-arguments '(setq ,nargs)) | 527 | (new-expr (macroexp--expand-all expr))) |
| 525 | nil 'compile-only fn) | 528 | (if (eq new-expr expr) |
| 526 | (let ((assignments nil)) | 529 | form |
| 527 | (while (consp (cdr-safe args)) | 530 | `(,fn ,var ,new-expr))) |
| 528 | (let* ((var (car args)) | 531 | ;; Normalize to a sequence of (setq SYM EXPR). |
| 529 | (expr (cadr args)) | 532 | ;; Malformed code is translated to code that signals an error |
| 530 | (new-expr (macroexp--expand-all expr)) | 533 | ;; at run time. |
| 531 | (assignment | 534 | (if (oddp nargs) |
| 532 | (if (and (symbolp var) | 535 | (macroexp-warn-and-return |
| 533 | (not (booleanp var)) (not (keywordp var))) | 536 | (format-message "odd number of arguments in `setq' form") |
| 534 | `(,fn ,var ,new-expr) | 537 | `(signal 'wrong-number-of-arguments '(setq ,nargs)) |
| 535 | (macroexp-warn-and-return | 538 | nil 'compile-only fn) |
| 536 | (format-message "attempt to set %s `%s'" | 539 | (let ((assignments nil)) |
| 537 | (if (symbolp var) | 540 | (while (consp (cdr-safe args)) |
| 538 | "constant" | 541 | (let* ((var (car args)) |
| 539 | "non-variable") | 542 | (expr (cadr args)) |
| 540 | var) | 543 | (new-expr (macroexp--expand-all expr)) |
| 541 | (cond | 544 | (assignment |
| 542 | ((keywordp var) | 545 | (if (and (symbolp var) |
| 543 | ;; Accept `(setq :a :a)' for compatibility. | 546 | (not (booleanp var)) |
| 544 | `(if (eq ,var ,new-expr) | 547 | (not (keywordp var))) |
| 545 | ,var | 548 | `(,fn ,var ,new-expr) |
| 546 | (signal 'setting-constant (list ',var)))) | 549 | (macroexp-warn-and-return |
| 547 | ((symbolp var) | 550 | (format-message "attempt to set %s `%s'" |
| 548 | `(signal 'setting-constant (list ',var))) | 551 | (if (symbolp var) |
| 549 | (t | 552 | "constant" |
| 550 | `(signal 'wrong-type-argument | 553 | "non-variable") |
| 551 | (list 'symbolp ',var)))) | 554 | var) |
| 552 | nil 'compile-only var)))) | 555 | (cond |
| 553 | (push assignment assignments)) | 556 | ((keywordp var) |
| 554 | (setq args (cddr args))) | 557 | ;; Accept `(setq :a :a)' for compatibility. |
| 555 | (cons 'progn (nreverse assignments)))))) | 558 | ;; FIXME: Why, exactly? It's useless. |
| 556 | (`(,(and fun `(lambda . ,_)) . ,args) | 559 | `(if (eq ,var ,new-expr) |
| 557 | (macroexp--cons (macroexp--all-forms fun 2) | 560 | ,var |
| 558 | (macroexp--all-forms args) | 561 | (signal 'setting-constant (list ',var)))) |
| 559 | form)) | 562 | ((symbolp var) |
| 563 | `(signal 'setting-constant (list ',var))) | ||
| 564 | (t | ||
| 565 | `(signal 'wrong-type-argument | ||
| 566 | (list 'symbolp ',var)))) | ||
| 567 | nil 'compile-only var)))) | ||
| 568 | (push assignment assignments)) | ||
| 569 | (setq args (cddr args))) | ||
| 570 | (cons 'progn (nreverse assignments))))))) | ||
| 560 | (`(funcall ,exp . ,args) | 571 | (`(funcall ,exp . ,args) |
| 561 | (let ((eexp (macroexp--expand-all exp)) | 572 | (let ((eexp (macroexp--expand-all exp)) |
| 562 | (eargs (macroexp--all-forms args))) | 573 | (eargs (macroexp--all-forms args))) |
| 563 | (pcase eexp | 574 | (if (eq (car-safe eexp) 'function) |
| 564 | ;; Rewrite (funcall #'foo bar) to (foo bar), in case `foo' | 575 | (let ((f (cadr eexp))) |
| 565 | ;; has a compiler-macro, or to unfold it. | 576 | (cond |
| 566 | ((and `#',f | 577 | ;; Rewrite (funcall #'foo bar) to (foo bar), in case `foo' |
| 567 | (guard (and (symbolp f) | 578 | ;; has a compiler-macro, or to unfold it. |
| 568 | ;; bug#46636 | 579 | ((and (symbolp f) |
| 569 | (not (or (special-form-p f) (macrop f)))))) | 580 | ;; bug#46636 |
| 570 | (macroexp--expand-all `(,f . ,eargs))) | 581 | (not (or (special-form-p f) (macrop f)))) |
| 571 | (`#'(lambda . ,_) | 582 | (macroexp--expand-all `(,f . ,eargs))) |
| 572 | (macroexp--unfold-lambda `(,fn ,eexp . ,eargs))) | 583 | ((eq (car-safe f) 'lambda) |
| 573 | (_ `(,fn ,eexp . ,eargs))))) | 584 | (macroexp--unfold-lambda `(,fn ,eexp . ,eargs))) |
| 585 | (t `(,fn ,eexp . ,eargs)))) | ||
| 586 | `(,fn ,eexp . ,eargs)))) | ||
| 574 | (`(funcall . ,_) form) ;bug#53227 | 587 | (`(funcall . ,_) form) ;bug#53227 |
| 575 | (`(,(and func (pred symbolp)) . ,_) | 588 | (`(,(and func (pred symbolp)) . ,_) |
| 576 | (let ((handler (function-get func 'compiler-macro))) | 589 | (let ((handler (function-get func 'compiler-macro))) |
| @@ -597,6 +610,10 @@ Assumes the caller has bound `macroexpand-all-environment'." | |||
| 597 | newform | 610 | newform |
| 598 | (macroexp--expand-all form))) | 611 | (macroexp--expand-all form))) |
| 599 | (macroexp--expand-all newform)))))) | 612 | (macroexp--expand-all newform)))))) |
| 613 | (`(,(and fun `(lambda . ,_)) . ,args) | ||
| 614 | (macroexp--cons (macroexp--all-forms fun 2) | ||
| 615 | (macroexp--all-forms args) | ||
| 616 | form)) | ||
| 600 | (_ form)))))) | 617 | (_ form)))))) |
| 601 | 618 | ||
| 602 | ;;;###autoload | 619 | ;;;###autoload |