Subject: bug#63288: 30.0.50; Emacs 30 packages fail to build with native comp on some machines

From: Pip Cet <[email-masked]>
Date: Sun, 16 Feb 2025 00:14:38 +0000

"Eli Zaretskii" <[email-masked]> writes:

> Ping! Can we please make some progress in this matter?

Okay, I've got it.  The problem is present on master as well, so we
should fix it.

This code in eieio-core.el is the culprit:

(progn
  ;; Arrange for field access not to bother checking if the access is indeed
  ;; made to an eieio--class object.
  (eval-when-compile (cl-declaim (optimize (safety 0))))

(cl-defstruct (eieio--class
               (:constructor nil)
               (:constructor eieio--class-make (name))
               (:include cl--class)
               (:copier nil))
  children
  initarg-tuples                  ;; initarg tuples list
  (class-slots nil :type (vector-of eieio--slot))
  class-allocation-values         ;; class allocated value vector
  default-object-cache ;; what a newly created object would look like.
                       ; This will speed up instantiation time as
                       ; only a `copy-sequence' will be needed, instead of
                       ; looping over all the values and setting them from
                       ; the default.
  options ;; storage location of tagged class option
          ; Stored outright without modifications or stripping
  )
  ;; Set it back to the default value.  NOTE: Using the default
  ;; `safety' value does NOT give the default
  ;; `byte-compile-delete-errors' value.  Therefore limit this (and
  ;; the above `cl-declaim') to compile time so that we don't affect
  ;; code which only loads this library.
  (eval-when-compile (cl-declaim (optimize (safety 1)))))

If package.el is compiled before eieio.el is (which can happen with 48
threads!), the above code is executed when package.el loads eieio-core
(via url-handlers -> url-parse -> auth-source -> eieio), leaving
cl--optimize-safety at 1 and byte-compile-delete-errors at t.

However, the maximum value of cl--optimize-safety is 3, which is the
only level at which byte-compile-delete-errors is set to nil.

IOW, this single-character patch fixes it:

>From 5a5080e7a75867916c877f4763f94dcf9dd6ef81 Mon Sep 17 00:00:00 2001
From: Pip Cet <[email-masked]>
Subject: [PATCH] Fix compilation errors caused by reduced safety level
 (bug#63288)

* lisp/emacs-lisp/eieio-core.el: Restore 'safety' to 3, not 1, so
byte-compile-delete-errors is reset to nil.
---
 lisp/emacs-lisp/eieio-core.el | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lisp/emacs-lisp/eieio-core.el b/lisp/emacs-lisp/eieio-core.el
index f95fd65fa5c..865906131fe 100644
--- a/lisp/emacs-lisp/eieio-core.el
+++ b/lisp/emacs-lisp/eieio-core.el
@@ -106,7 +106,7 @@ eieio-default-superclass
   ;; `byte-compile-delete-errors' value.  Therefore limit this (and
   ;; the above `cl-declaim') to compile time so that we don't affect
   ;; code which only loads this library.
-  (eval-when-compile (cl-declaim (optimize (safety 1)))))
+  (eval-when-compile (cl-declaim (optimize (safety 3)))))

 (eval-and-compile
-- 
2.48.1

Stefan, it seems strange to me that cl--optimize-safety at 1 sometimes
corresponds to byte errors being deleted, and sometimes it doesn't.  I
suspect we might want this change instead:

>From d79b21f213f52fd8ce70943e7f9128d05b88ce59 Mon Sep 17 00:00:00 2001
From: Pip Cet <[email-masked]>
Subject: [PATCH] Don't allow the byte compiler to delete errors at default
 safety (bug#63288)

* lisp/emacs-lisp/cl-macs.el (cl--do-proclaim): Change
'byte-compile-delete-errors' to take effect only at 'safety' level 0.
---
 lisp/emacs-lisp/cl-macs.el | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/lisp/emacs-lisp/cl-macs.el b/lisp/emacs-lisp/cl-macs.el
index caaffcf19be..42830ca9f7a 100644
--- a/lisp/emacs-lisp/cl-macs.el
+++ b/lisp/emacs-lisp/cl-macs.el
@@ -2701,7 +2701,7 @@ cl--do-proclaim
 	 (let ((speed (assq (nth 1 (assq 'speed (cdr spec)))
 			    '((0 nil) (1 t) (2 t) (3 t))))
 	       (safety (assq (nth 1 (assq 'safety (cdr spec)))
-			     '((0 t) (1 t) (2 t) (3 nil)))))
+			     '((0 t) (1 nil) (2 nil) (3 nil)))))
 	   (if speed (setq cl--optimize-speed (car speed)
 			   byte-optimize (nth 1 speed)))
 	   (if safety (setq cl--optimize-safety (car safety)
-- 
2.48.1

This would synchronize byte-compile-delete-errors to have the correct
default value at the (default?) safety level 1.

To reproduce the problem on master:

1. Build the tree
2. rm -f lisp/emacs-lisp/eieio-core.elc
3. rm -f lisp/emacs-lisp/package.elc
4. make -C lisp emacs-lisp/package.elc
5. inspect the newly-built lisp/emacs-lisp/package.elc to see that it
has the incorrect compilation output for package-read-from-string.

As a tangent, it would be nice to randomize elc compilation order to
find such errors, but it's not fully automatable, because even now,
parallel builds aren't reproducible.

Pip