Subject: bug#46387: 28.0.50; Compiled code making a variable dynamic stopped working

From: Mattias Engdegård <[email-masked]>
Date: Tue, 9 Feb 2021 11:32:58 +0100

8 feb. 2021 kl. 18.29 skrev Michael Heerdegen <[email-masked]>:

> the latest changes in byte-opt.el seem to have broken some of my code.
> It worked since yesterday, and it still works when interpreted, but the
> compiled code raises this error today:

Intereresting, thank you! And sorry about that.

>  (with-suppressed-warnings ((lexical date original-date number))
>    (defvar date)
>    (defvar original-date)
>    (defvar number))

There's a lack of clarity regarding the exact semantics of local `defvar`. The manual says that its effect is confined to the current lexical scope, but what exactly does that mean?

As it turns out, parts of the implementation have different opinions about that. As you observed, the recently added optimisation on master takes a strict syntactic view: even a `progn` is a lexical scope, and `with-suppressed-warnings` wraps its body in a `progn`; thus your `defvar` declarations have no effect outside that construct.

However, the interpreter is at the other extreme end and takes a very dynamic view. For example:

(defun f (x)
  (if x (defvar my-var))
  (let ((my-var 17))
    (do-something)))

Here, whether my-var is a lexical or dynamic variable depends on the argument x! The compiler unsurprisingly is of more static persuasion but sadly hazy on the details. For example, the above function is compiled (in Emacs 27) with my-var lexically bound, but if we say

(defun g ()
  (if (= 1 2) (defvar my-var))
  (let ((my-var 17))
    (do-something)))

then my-var becomes dynamic. Ouch.

While we ponder over the problem, you may try separate with-suppressed-warnings for each variable. Ie,

  (with-suppressed-warnings ((lexical date))
    (defvar date))
  (with-suppressed-warnings ((lexical original-date))
    (defvar original-date))
  (with-suppressed-warnings ((lexical number))
    (defvar number))

hoping that each single-expression `progn` will rapidly decay into its confined expression (a defvar) and thus will be syntactically in the right lexical scope.

(Stefan, it looks like your latest Gnus patch may fall in the same trap. Or?)