Emacs Lisp has no standard and no version number of its own. The language is whatever the running Emacs provides, so a file works or fails depending on the Emacs version. This page lists what each release added to the language and to the file formats around it, then shows how a file states which Emacs it needs and how code can test for it. Releases 27 to 31 and the development line were read from the NEWS files. Releases 22 to 26 were not, and are marked as such.
Releases and dates
| Release | Date | Basis |
|---|---|---|
| 22 | 2007 | remembered, not checked |
| 23 | 2009 | remembered, not checked |
| 24.1 | June 2012 | remembered, not checked |
| 25.1 | September 2016 | remembered, not checked |
| 26.1 | May 2018 | remembered, not checked |
| 27.1 | August 2020 | the NEWS files do not state release dates; the dates for 27.1 to 30.1 are from the project’s published release history |
| 28.1 | April 2022 | as above |
| 29.1 | July 2023 | as above |
| 30.1 | June 2024 | as above |
| 31 | no date given here | NEWS.31 has sections for 31.1 and 31.2 |
| 32 (development) | – | the current NEWS file is headed “Emacs 32.1” |
Emacs 22 to 26 (not checked against the release notes)
This table is from memory. The release notes for these versions were not among the downloaded files, so each entry is a well-known headline only, and the list is not complete.
| Release | Headline changes |
|---|---|
| 22 (2007) | wider Unicode and multibyte text support; the GTK+ graphical interface |
| 23 (2009) | characters are Unicode internally; UTF-8 is the usual default encoding; the old cl.el library in common use |
| 24 (2012) | lexical binding becomes available (the lexical-binding variable and cookie); cl-lib, with cl- prefixed names, replaces cl.el; package.el; generalized variables reworked; pcase |
| 25 (2016) | dynamic modules (loadable shared libraries written in C); seq.el with generic sequence functions |
| 26 (2018) | threads (cooperative, in Lisp); display-line-numbers-mode |
Emacs 27
| Area | Change |
|---|---|
| Numbers | integers of any size (bignums) through the GMP library, with bignump and fixnump; a bundled mini-gmp is used when GMP is not found |
| JSON | native json-serialize, json-insert, json-parse-string, json-parse-buffer, when built with the Jansson library |
| Start-up | an early-init.el file in user-emacs-directory, loaded before graphics and before the package system start |
| Build | a portable dumper replaces unexec; dynamic module support is on by default |
| Lexical binding | used by default for M-:, --eval, lisp-interaction-mode and ielm-mode |
| Language | variables may be omitted after &optional and &rest; setq-local takes several pairs; condition-case accepts t as a catch-all; proper-list-p; ignore-error; with-suppressed-warnings; print-quoted is now t by default |
| Removed | old-style backquotes now signal an error; functions obsolete since Emacs 22 and 24 were removed |
| Libraries | a new jsonrpc library; byte-compile-dynamic was made obsolete |
Emacs 28
| Area | Change |
|---|---|
| Compilation | optional native compilation (Native compilation); a byte-compiler check for missing dynamic variable declarations; the load argument of byte-compile-file is obsolete |
| Symbols | shorthands: the file-local variable read-symbol-shorthands lets a file abbreviate long prefixes without changing its source |
| Lexical binding | a lexical-binding cookie is always heeded, even when enable-local-variables is nil |
| Language | a success handler in condition-case; dlet; make-closure; always; pcase-setq and a cl-type pattern for pcase |
| Functions | string-search, string-replace, ensure-list, file-name-concat, directory-empty-p, length<, length> and length= |
| Commands | interactive may list the modes a command applies to; the notes warn that this makes byte-code which older Emacs cannot use |
| JSON | stricter number parsing; json-available-p; some json-encode-* functions made obsolete |
Emacs 29
| Area | Change |
|---|---|
| Parsing | optional tree-sitter support, with new major modes based on it |
| Packages | Eglot (the language-server client) and use-package are now part of Emacs |
| Database | optional built-in access to SQLite databases |
| Build | a pure GTK (PGTK) build option; --with-native-compilation=aot for ahead-of-time native compilation |
| Native code | native-compile-prune-cache, startup-redirect-eln-cache, and symbol-file can report .eln files |
| Closures | interpreted closures are safe for space: they capture only the part of the lexical environment they use, as byte-compiled closures already did |
| Language | while-let; setopt; with-memoization; pos-bol and pos-eol; readablep; compiled-function-p; the declare form interactive-args |
| Keys | the keymap-set family and key-valid-p replace define-key and related functions with a stricter key syntax |
| Libraries | new seq functions; oclosure; textsec; a file’s local-variables section may define fallback modes |
Emacs 30
| Area | Change |
|---|---|
| Native code | enabled by default when libgccjit is available; new compilation-safety and native-comp-async-warnings-errors-kind; subr-native-elisp-p renamed native-comp-function-p; the ftype declaration |
| Function objects | evaluating a lambda gives an interpreted-function object, no longer a list starting with lambda or closure. It has the same slots as a byte-code function (argument list, docstring and interactive form in the same places), so code that took such functions apart with car or cdr must use aref. New types closure (the parent of interpreted-function and byte-code-function) and interpreted-function, with closurep, interpreted-function-p and make-interpreted-closure |
| Byte-code | byte-code is always loaded eagerly; fetch-bytecode and byte-compile-dynamic do nothing |
| Errors | the special form handler-bind runs a handler without unwinding the stack, so a backtrace can be recorded at the point of the error |
| Conditionals | static-if hides a form from the evaluator or byte-compiler by a compile-time condition, which avoids warnings about code that never runs |
| Language | value<, a general comparison function; sort gains new arguments; major-mode-remap and a new derived-mode-p API; drop; merge-ordered-lists; require-with-check |
| Reader | \x with no hexadecimal digit after it is now an error |
| Strings | a function or macro whose body is only a string literal returns that string and does not use it as its documentation |
| Removals | defadvice is marked obsolete; the millisec argument of sleep-for is obsolete |
| Packages | a stub of the Compat library, tree-sitter additions, an Android port |
Emacs 31
| Area | Change |
|---|---|
| Lexical binding | loading a file with no lexical-binding cookie prints a warning; the default can be changed with (set-default-toplevel-value 'lexical-binding t); files loaded with -x or --script use lexical binding |
| Macros | incf and decf; cond*, an alternative to cond and pcase that can also use pcase patterns; static-when and static-unless; setopt-local |
| Functions | plusp, minusp, oddp, evenp; take-while and drop-while; all and any; hash-table-contains-p; set-local |
| Native code | native-compile-directory; native-comp-async-on-battery-power |
| Build | the old unexec dumper is removed, leaving the portable dumper |
| Obsolete | if-let and when-let (use if-let*, when-let* and and-let*); the rx atom any |
| Incompatible | string mutation restricted further; nested backquotes no longer supported in pcase patterns; the compiler combines more program constants |
| Text | Unicode 17 |
The development line (NEWS for 32.1)
The current NEWS file is for Emacs 32.1 and is still being written, so this list can change. At the time it was read, it said that the regexp engine matches in linear time, that ignore can be used as a place that discards the value (like /dev/null), and that Emacs supports Unicode 18.
The lexical-binding story
Emacs Lisp began with dynamic binding only, where every variable is global while its binding lasts. Lexical binding is selected per file by a cookie on the first line (-*- lexical-binding: t -*-); see File structure and Variables and scope. It became available in Emacs 24 (remembered, not checked). From the notes that were read: Emacs 27 made it the default for interactive evaluation; Emacs 28 made a cookie always count; Emacs 29 made interpreted closures capture only what they use; Emacs 30 changed interpreted functions into objects; Emacs 31 warns when a loaded file has no cookie and lets users change the default. The dynamic dialect has not been removed. In the function objects, the constants slot of an interpreted function is nil for the dynamic dialect and holds the environment for the lexical one.
How to tell which Emacs a file needs
| Where | What it tells you |
|---|---|
Package-Requires header | the packages and the least Emacs version the file’s author promises it works on: ;; Package-Requires: ((emacs "27.1")) |
| the first line cookie | whether the file expects lexical binding; no cookie means the dynamic dialect on Emacs 30 and earlier, and a warning on 31 |
byte 4 of a .elc | the Emacs major version that compiled it (the previous one when compiled by a development build); see Byte-compilation. The comment line ;;; in Emacs version X gives the full version |
| use of newer names | a function or macro from the tables above (static-if, incf, handler-bind) shows the earliest Emacs that can run the file |
In code, the variables emacs-version, emacs-major-version and emacs-minor-version tell the running version, and version-to-list, version< and version<= compare version strings:
(when (version<= "29.1" emacs-version)
(setopt fill-column 80))
(when (>= emacs-major-version 30)
(message "Emacs 30 or later"))
(if (fboundp 'treesit-available-p)
(message "tree-sitter functions exist")
(message "no tree-sitter functions"))
A test at run time is checked after compiling, so the compiler may still warn about the newer function. static-if (Emacs 30) and static-when (Emacs 31) decide at compile time and hide the other branch from the compiler:
(static-if (>= emacs-major-version 30)
(defalias 'my-sort #'sort)
(defun my-sort (seq pred) (sort (copy-sequence seq) pred)))
The featurep test is for features such as (featurep 'native-compile) or (featurep 'treesit), which depend on how Emacs was built and not only on its version. Since Emacs 30 a stub of the Compat library, which provides newer functions to older Emacs, is part of Emacs; the full library is on GNU ELPA.
Not described
Known, not yet covered
- Reference: Emacs 22 to 26. They are in the table from memory only and were not checked against their release notes; the dates are the author’s recollection. The
NEWSfiles for those versions were not downloaded. - Reference: Emacs 27 to 31 are summarised, not complete. Each release has hundreds of entries for modes and packages that do not touch the language; only language and file-format items are listed. Releases 31.2, 30.2 and the other bug-fix releases are not listed.
- Reference: release dates of 31 and 32, and a date for 22 and 23 beyond the year, are not stated.
- Reference: other Emacs-like implementations (the XEmacs fork and others) are not described.
See also
- Byte-compilation – the version byte and compatibility of
.elcfiles - Native compilation – the
.elncache, per Emacs build - Packages and autoloads –
Package-Requiresandprovide - Variables and scope – lexical and dynamic binding
- Lexical Binding – the manual’s section