From 0972a54a5ac52f731c0ea7c8538c8f3b7b6b36b4 Mon Sep 17 00:00:00 2001 From: Daniel Colascione Date: Fri, 7 Mar 2025 21:47:56 -0800 Subject: [PATCH] Add static-when, static-unless like static-if * lisp/subr.el (static-when, static-unless): define * doc/lispref/control.texi (Conditional Compilation): document * etc/NEWS: mention --- doc/lispref/control.texi | 13 +++++++++++++ etc/NEWS | 6 ++++++ lisp/subr.el | 26 ++++++++++++++++++++++++++ 3 files changed, 45 insertions(+) diff --git a/doc/lispref/control.texi b/doc/lispref/control.texi index a7b2b35f58a..4d00d27bd46 100644 --- a/doc/lispref/control.texi +++ b/doc/lispref/control.texi @@ -2841,6 +2841,19 @@ Test @var{condition} at macro-expansion time. If its value is non-@code{nil}, expand the macro to @var{then-form}, otherwise expand it to @var{else-forms} enclosed in a @code{progn}. @var{else-forms} may be empty. +@end defmac + +@defmac static-when condition body... +Test @var{condition} at macro-expansion time. If its value is +non-@code{nil}, expand the macro to evaluate all @var{body} forms +sequentially and return the value of the last one, or @code{nil} if there +are none. +@end defmac + +@defmac static-unless condition body... +Test @var{condition} at macro-expansion time. If its value is @code{nil}, +expand the macro to evaluate all @var{body} forms sequentially and return +the value of the last one, or @code{nil} if there are none. Here is an example of its use from CC Mode, which prevents a @code{defadvice} form being compiled in newer versions of Emacs: diff --git a/etc/NEWS b/etc/NEWS index 258f0dcc4ba..4c433fbded0 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -1507,6 +1507,12 @@ Previously, its argument was always evaluated using dynamic binding. * Lisp Changes in Emacs 31.1 ++++ +** New macros 'static-when' and 'static-unless' implement conditional +compilation like 'static-if'. +These macros evaluate their condition at macro-expansion time and are useful +for writing code that can work across different Emacs versions. + --- ** You can change the default value of 'lexical-binding'. While the default is still the use dynamic binding dialect of ELisp diff --git a/lisp/subr.el b/lisp/subr.el index 8f0366824d9..110bebed789 100644 --- a/lisp/subr.el +++ b/lisp/subr.el @@ -301,6 +301,19 @@ value of last one, or nil if there are none." (macroexp-warn-and-return (format-message "`when' with empty body") (list 'progn cond nil) '(empty-body when) t))) +(defmacro static-when (condition &rest body) + "A conditional compilation macro. +Evaluate CONDITION at macro-expansion time. If it is non-nil, +expand the macro to evaluate all BODY forms sequentially and return +the value of the last one, or nil if there are none." + (declare (indent 1) (debug t)) + (if body + (if (eval condition lexical-binding) + (cons 'progn body) + nil) + (macroexp-warn-and-return (format-message "`static-when' with empty body") + (list 'progn nil nil) '(empty-body static-when) t))) + (defmacro unless (cond &rest body) "If COND yields nil, do BODY, else return nil. When COND yields nil, eval BODY forms sequentially and return @@ -311,6 +324,19 @@ value of last one, or nil if there are none." (macroexp-warn-and-return (format-message "`unless' with empty body") (list 'progn cond nil) '(empty-body unless) t))) +(defmacro static-unless (condition &rest body) + "A conditional compilation macro. +Evaluate CONDITION at macro-expansion time. If it is nil, +expand the macro to evaluate all BODY forms sequentially and return +the value of the last one, or nil if there are none." + (declare (indent 1) (debug t)) + (if body + (if (eval condition lexical-binding) + nil + (cons 'progn body)) + (macroexp-warn-and-return (format-message "`static-unless' with empty body") + (list 'progn nil nil) '(empty-body static-unless) t))) + (defsubst subr-primitive-p (object) "Return t if OBJECT is a built-in primitive written in C. Such objects can be functions or special forms."