Your message dated Tue, 06 Oct 2026 14:40:10 +0000
with message-id <[email protected]>
and subject line Bug#1147945: fixed in rust-tracing-core 0.1.36-2
has caused the Debian Bug report #1147945,
regarding rust-tracing-core: Niche optimization assumption will fail for newer 
Rust
to be marked as done.

This means that you claim that the problem has been dealt with.
If this is not the case it is now your responsibility to reopen the
Bug report if necessary, and/or fix the problem forthwith.

(NB: If you are a system administrator and have no idea what this
message is talking about, this may indicate a serious mail system
misconfiguration somewhere. Please contact [email protected]
immediately.)


-- 
1147945: https://bugs.debian.org/cgi-bin/bugreport.cgi?bug=1147945
Debian Bug Tracking System
Contact [email protected] with problems
--- Begin Message ---
Source: rust-tracing-core
Version: 0.1.36-1
Severity: normal
Tags: patch upstream

Dear rust-tracing-core maintainers,

Currently, rust-tracing-core assumes that Rust niche optimization[1]
remains the
same, even though Rust makes no such guarantee. As of Rust 1.97, this
hardcoded
assumption is no longer true, meaning that the
metadata::tests::level_filter_reprs
test will break[2]:

167s ---- metadata::tests::level_filter_reprs stdout ----
167s
167s thread 'metadata::tests::level_filter_reprs' (5315) panicked at
src/metadata.rs:1119:13:
167s assertion `left == right` failed: repr changed for LevelFilter::OFF
167s left: 5
167s right: 18446744073709551615
167s stack backtrace:
167s 0: __rustc::rust_begin_unwind
167s at /usr/src/rustc-1.97.1/library/std/src/panicking.rs:689:5
167s 1: core::panicking::panic_fmt
167s at /usr/src/rustc-1.97.1/library/core/src/panicking.rs:80:14
167s 2: core::panicking::assert_failed_inner
167s at /usr/src/rustc-1.97.1/library/core/src/panicking.rs:434:23
167s 3: core::panicking::assert_failed::<usize, usize>
167s at /usr/src/rustc-1.97.1/library/core/src/panicking.rs:394:5
167s 4: tracing_core::metadata::tests::level_filter_reprs
167s at ./src/metadata.rs:1119:13
167s 5: tracing_core::metadata::tests::level_filter_reprs::{closure#0}
167s at ./src/metadata.rs:1102:28
167s 6: <tracing_core::metadata::tests::level_filter_reprs::{closure#0} as
core::ops::function::FnOnce<()>>::call_once
167s at /usr/src/rustc-1.97.1/library/core/src/ops/function.rs:250:5
167s 7: <fn() -> core::result::Result<(), alloc::string::String> as
core::ops::function::FnOnce<()>>::call_once
167s at /usr/src/rustc-1.97.1/library/core/src/ops/function.rs:250:5
167s note: Some details are omitted, run with `RUST_BACKTRACE=full` for a
verbose backtrace.

Naturally, this bug will not impact Debian until Rust is updated to 1.97.
However, it is affecting Ubuntu and being tracked as LP: #2167368.

I have attached a patch which cherry-picks the upstream fix[3]. The fix
makes it so the crate works with all Rust versions past the MSRV -- I
successfully ran all tests with both 1.93.1 and 1.97.1, and I verified that
the package builds against Debian unstable.

Please let me know if you need anything else.

Best wishes,

Max

[1]: https://www.0xatticus.com/posts/understanding_rust_niche/
[2]:
https://autopkgtest.ubuntu.com/results/autopkgtest-stonking/stonking/amd64/r/rust-tracing-core/20260912_123714_a8bc3@/log.gz

[3]: https://github.com/tokio-rs/tracing/pull/3537
Description: Get LevelFilter::OFF niche representation dynamically
 Determining the value for `Option::None` at compile time via a union
 transmute ensures that we always match the exact representation
 selected by the compiler's niche optimization for `LevelFilter` on any
 Rust version. The niche optimization changed in Rust 1.97.
 .
 This patch has been cherry-picked from upstream to unblock the Rust
 1.97 transition. It can be dropped once we get an upstream tracing-core
 version which contains the fix.
Author: Augie Fackler <[email protected]>
Origin: upstream, https://github.com/tokio-rs/tracing/pull/3537.diff
Bug: https://github.com/tokio-rs/tracing/pull/3537
Bug-Ubuntu: https://bugs.launchpad.net/bugs/2167368
---
--- a/src/metadata.rs
+++ b/src/metadata.rs
@@ -677,12 +677,13 @@
     const INFO_USIZE: usize = LevelInner::Info as usize;
     const DEBUG_USIZE: usize = LevelInner::Debug as usize;
     const TRACE_USIZE: usize = LevelInner::Trace as usize;
-    // Using the value of the last variant + 1 ensures that we match the value
-    // for `Option::None` as selected by the niche optimization for
-    // `LevelFilter`. If this is the case, converting a `usize` value into a
-    // `LevelFilter` (in `LevelFilter::current`) will be an identity conversion,
-    // rather than generating a lookup table.
-    const OFF_USIZE: usize = LevelInner::Error as usize + 1;
+    // Dynamically determining the value for `Option::None` at compile time
+    // via `core::mem::transmute` ensures that we always match the exact representation
+    // selected by the compiler's niche optimization for `LevelFilter` on any
+    // Rust version. This guarantees that converting a `usize` value into a
+    // `LevelFilter` (in `LevelFilter::current`) is always a zero-cost identity
+    // conversion, rather than generating a lookup table.
+    const OFF_USIZE: usize = unsafe { core::mem::transmute(LevelFilter::OFF) };
 
     /// Returns a `LevelFilter` that matches the most verbose [`Level`] that any
     /// currently active [`Subscriber`] will enable.
@@ -886,11 +887,10 @@
 //    `Option<Level>`) compiles down to a single integer value. This is
 //    necessary for storing the global max in an `AtomicUsize`, and for ensuring
 //    that we use fast integer-integer comparisons, as mentioned previously. In
-//    order to ensure this, we exploit the niche optimization. The niche
-//    optimization for `Option<{enum with a numeric repr}>` will choose
-//    `(HIGHEST_DISCRIMINANT_VALUE + 1)` as the representation for `None`.
-//    Therefore, the integer representation of `LevelFilter::OFF` (which is
-//    `None`) will be the number 5. `OFF` must compare higher than every other
+//    order to ensure this, we exploit the niche optimization. The exact raw
+//    representation selected by `rustc` for `Option::None` may vary across
+//    compiler versions, so `LevelFilter::OFF_USIZE` is determined dynamically
+//    via `core::mem::transmute`. `OFF` must compare higher than every other
 //    level in order for it to filter as expected. Since we want to use a single
 //    `cmp` instruction, we can't special-case the integer value of `OFF` to
 //    compare higher, as that will generate more code. Instead, we need it to be
@@ -911,7 +911,7 @@
 impl PartialEq<LevelFilter> for Level {
     #[inline(always)]
     fn eq(&self, other: &LevelFilter) -> bool {
-        self.0 as usize == filter_as_usize(&other.0)
+        self.0 as usize == filter_as_usize_sort_key(other.0)
     }
 }
 
@@ -952,42 +952,46 @@
 impl PartialOrd<LevelFilter> for Level {
     #[inline(always)]
     fn partial_cmp(&self, other: &LevelFilter) -> Option<cmp::Ordering> {
-        Some(filter_as_usize(&other.0).cmp(&(self.0 as usize)))
+        Some(filter_as_usize_sort_key(other.0).cmp(&(self.0 as usize)))
     }
 
     #[inline(always)]
     fn lt(&self, other: &LevelFilter) -> bool {
-        filter_as_usize(&other.0) < (self.0 as usize)
+        filter_as_usize_sort_key(other.0) < (self.0 as usize)
     }
 
     #[inline(always)]
     fn le(&self, other: &LevelFilter) -> bool {
-        filter_as_usize(&other.0) <= (self.0 as usize)
+        filter_as_usize_sort_key(other.0) <= (self.0 as usize)
     }
 
     #[inline(always)]
     fn gt(&self, other: &LevelFilter) -> bool {
-        filter_as_usize(&other.0) > (self.0 as usize)
+        filter_as_usize_sort_key(other.0) > (self.0 as usize)
     }
 
     #[inline(always)]
     fn ge(&self, other: &LevelFilter) -> bool {
-        filter_as_usize(&other.0) >= (self.0 as usize)
+        filter_as_usize_sort_key(other.0) >= (self.0 as usize)
     }
 }
 
+const USE_NATIVE_SORT: bool = LevelFilter::OFF_USIZE > LevelInner::Error as usize;
+
 #[inline(always)]
-fn filter_as_usize(x: &Option<Level>) -> usize {
+fn filter_as_usize_sort_key(x: Option<Level>) -> usize {
     match x {
-        Some(Level(f)) => *f as usize,
-        None => LevelFilter::OFF_USIZE,
+        Some(Level(f)) => f as usize,
+        // The niche optimization for LevelFilter::OFF isn't guaranteed
+        // to be the last variant + 1, so we explicitly return that for sorting here.
+        None => if USE_NATIVE_SORT { LevelFilter::OFF_USIZE } else { LevelInner::Error as usize + 1 },
     }
 }
 
 impl PartialEq<Level> for LevelFilter {
     #[inline(always)]
     fn eq(&self, other: &Level) -> bool {
-        filter_as_usize(&self.0) == other.0 as usize
+        filter_as_usize_sort_key(self.0) == other.0 as usize
     }
 }
 
@@ -999,56 +1003,56 @@
 
     #[inline(always)]
     fn lt(&self, other: &LevelFilter) -> bool {
-        filter_as_usize(&other.0) < filter_as_usize(&self.0)
+        filter_as_usize_sort_key(other.0) < filter_as_usize_sort_key(self.0)
     }
 
     #[inline(always)]
     fn le(&self, other: &LevelFilter) -> bool {
-        filter_as_usize(&other.0) <= filter_as_usize(&self.0)
+        filter_as_usize_sort_key(other.0) <= filter_as_usize_sort_key(self.0)
     }
 
     #[inline(always)]
     fn gt(&self, other: &LevelFilter) -> bool {
-        filter_as_usize(&other.0) > filter_as_usize(&self.0)
+        filter_as_usize_sort_key(other.0) > filter_as_usize_sort_key(self.0)
     }
 
     #[inline(always)]
     fn ge(&self, other: &LevelFilter) -> bool {
-        filter_as_usize(&other.0) >= filter_as_usize(&self.0)
+        filter_as_usize_sort_key(other.0) >= filter_as_usize_sort_key(self.0)
     }
 }
 
 impl Ord for LevelFilter {
     #[inline(always)]
     fn cmp(&self, other: &Self) -> cmp::Ordering {
-        filter_as_usize(&other.0).cmp(&filter_as_usize(&self.0))
+        filter_as_usize_sort_key(other.0).cmp(&filter_as_usize_sort_key(self.0))
     }
 }
 
 impl PartialOrd<Level> for LevelFilter {
     #[inline(always)]
     fn partial_cmp(&self, other: &Level) -> Option<cmp::Ordering> {
-        Some((other.0 as usize).cmp(&filter_as_usize(&self.0)))
+        Some((other.0 as usize).cmp(&filter_as_usize_sort_key(self.0)))
     }
 
     #[inline(always)]
     fn lt(&self, other: &Level) -> bool {
-        (other.0 as usize) < filter_as_usize(&self.0)
+        (other.0 as usize) < filter_as_usize_sort_key(self.0)
     }
 
     #[inline(always)]
     fn le(&self, other: &Level) -> bool {
-        (other.0 as usize) <= filter_as_usize(&self.0)
+        (other.0 as usize) <= filter_as_usize_sort_key(self.0)
     }
 
     #[inline(always)]
     fn gt(&self, other: &Level) -> bool {
-        (other.0 as usize) > filter_as_usize(&self.0)
+        (other.0 as usize) > filter_as_usize_sort_key(self.0)
     }
 
     #[inline(always)]
     fn ge(&self, other: &Level) -> bool {
-        (other.0 as usize) >= filter_as_usize(&self.0)
+        (other.0 as usize) >= filter_as_usize_sort_key(self.0)
     }
 }
 
@@ -1101,7 +1105,7 @@
     #[test]
     fn level_filter_reprs() {
         let mapping = [
-            (LevelFilter::OFF, LevelInner::Error as usize + 1),
+            (LevelFilter::OFF, LevelFilter::OFF_USIZE),
             (LevelFilter::ERROR, LevelInner::Error as usize),
             (LevelFilter::WARN, LevelInner::Warn as usize),
             (LevelFilter::INFO, LevelInner::Info as usize),
@@ -1119,4 +1123,41 @@
             assert_eq!(expected, repr, "repr changed for {:?}", filter)
         }
     }
+
+    #[test]
+    fn level_filter_ordering() {
+        assert!(LevelFilter::OFF < LevelFilter::ERROR);
+        assert!(LevelFilter::OFF < LevelFilter::WARN);
+        assert!(LevelFilter::OFF < LevelFilter::INFO);
+        assert!(LevelFilter::OFF < LevelFilter::DEBUG);
+        assert!(LevelFilter::OFF < LevelFilter::TRACE);
+
+        assert!(LevelFilter::OFF < Level::ERROR);
+        assert!(LevelFilter::OFF < Level::WARN);
+        assert!(LevelFilter::OFF < Level::INFO);
+        assert!(LevelFilter::OFF < Level::DEBUG);
+        assert!(LevelFilter::OFF < Level::TRACE);
+
+        assert!(LevelFilter::ERROR < LevelFilter::WARN);
+        assert!(LevelFilter::WARN < LevelFilter::INFO);
+        assert!(LevelFilter::INFO < LevelFilter::DEBUG);
+        assert!(LevelFilter::DEBUG < LevelFilter::TRACE);
+
+        assert!(Level::ERROR < LevelFilter::WARN);
+        assert!(Level::WARN < LevelFilter::INFO);
+        assert!(Level::INFO < LevelFilter::DEBUG);
+        assert!(Level::DEBUG < LevelFilter::TRACE);
+
+        assert!(LevelFilter::ERROR > LevelFilter::OFF);
+        assert!(LevelFilter::WARN > LevelFilter::OFF);
+        assert!(LevelFilter::INFO > LevelFilter::OFF);
+        assert!(LevelFilter::DEBUG > LevelFilter::OFF);
+        assert!(LevelFilter::TRACE > LevelFilter::OFF);
+
+        assert!(Level::ERROR > LevelFilter::OFF);
+        assert!(Level::WARN > LevelFilter::OFF);
+        assert!(Level::INFO > LevelFilter::OFF);
+        assert!(Level::DEBUG > LevelFilter::OFF);
+        assert!(Level::TRACE > LevelFilter::OFF);
+    }
 }

--- End Message ---
--- Begin Message ---
Source: rust-tracing-core
Source-Version: 0.1.36-2
Done: Max Gilmour <[email protected]>

We believe that the bug you reported is fixed in the latest version of
rust-tracing-core, which is due to be installed in the Debian FTP archive.

A summary of the changes between this version and the previous one is
attached.

Thank you for reporting the bug, which will now be closed.  If you
have further comments please address them to [email protected],
and the maintainer will reopen the bug report if appropriate.

Debian distribution maintenance software
pp.
Max Gilmour <[email protected]> (supplier of updated rust-tracing-core 
package)

(This message was generated automatically at their request; if you
believe that there is a problem with it please contact the archive
administrators by mailing [email protected])


-----BEGIN PGP SIGNED MESSAGE-----
Hash: SHA512

Format: 1.8
Date: Tue, 15 Sep 2026 09:12:44 -0700
Source: rust-tracing-core
Built-For-Profiles: derivative.ubuntu noudeb
Architecture: source
Version: 0.1.36-2
Distribution: unstable
Urgency: medium
Maintainer: Debian Rust Maintainers 
<[email protected]>
Changed-By: Max Gilmour <[email protected]>
Closes: 1147945
Launchpad-Bugs-Fixed: 2167368
Changes:
 rust-tracing-core (0.1.36-2) unstable; urgency=medium
 .
   * Team upload
   * Package tracing-core 0.1.36 from crates.io using debcargo 2.8.4
   * Cherry-pick proposed patch to fix build with rustc 1.97 (LP: #2167368)
     (Closes: #1147945)
Checksums-Sha1:
 7e493c9215eeaa598a9b96f5dfd68a32684eb011 2343 rust-tracing-core_0.1.36-2.dsc
 ebc61a7c8822072006ff89c614b270137b37e5cf 5648 
rust-tracing-core_0.1.36-2.debian.tar.xz
 741be1a4d0c764cde0df50a334874660e268cfa1 8333 
rust-tracing-core_0.1.36-2_source.buildinfo
Checksums-Sha256:
 8a80d4417511b3954cd3877ef8a0af5cecc06f13ff96c683beeb891a6d4f4a33 2343 
rust-tracing-core_0.1.36-2.dsc
 7724e9f05aef7326e24f301a0b0dece7413dc171578d7e8811193f7515d11680 5648 
rust-tracing-core_0.1.36-2.debian.tar.xz
 4eed309051473fdbefe9224d27f30a67fe2a9ce44550780a612aa28861c66a48 8333 
rust-tracing-core_0.1.36-2_source.buildinfo
Files:
 185f0ece8d7515dfd6b832ade41f8436 2343 rust optional 
rust-tracing-core_0.1.36-2.dsc
 8e0ffff6033f11e9cb4898ae1f198186 5648 rust optional 
rust-tracing-core_0.1.36-2.debian.tar.xz
 863bc2b0ba389a90d44a11198cce1522 8333 rust optional 
rust-tracing-core_0.1.36-2_source.buildinfo

-----BEGIN PGP SIGNATURE-----

iQIzBAEBCgAdFiEETQvhLw5HdtiqzpaW5mx3Wuv+bH0FAmrE/uQACgkQ5mx3Wuv+
bH3txg/9GY1fKBveq08Qmo9w14ekykNH4DsYny3ZdR/qUEphn4cFS2HRO0tzJCc+
pWyJCmJKIvdY5zHUrVyBJc4XmN83G4FIxZzPO7wjlhboCE4eqjKEApghgEmeKQSl
YXa/JVuP7LN9EbKgTy302MpIZwMXgRN+eQqlWmL/Dnyedxj2iBbs7EDkF99+oqjZ
TEXqVOqUvk7v0RujnMMgkizdb1IRum7uZp1E1Vhq9BdSXC9goVwZ47SZCR0Zl8J7
qGK/pYxhBha88oSsdNhlIJDzVw+1Hl8rJfXRj+FuSdudT8CnZ9rQqLmGqh6Ylqye
aGcNH/jebTnN0yD5LaQ0mXFRGm51bivbrIAQ+MPVuPnKRktucRnFFdOFl5/BEKqR
HSJSD7UY5TGNxmmRx6T3JsQ/SGXk/DwqNR79tCEJj/1Yl/++OLtfZzpz46/M9zhj
TVmEPY4MyC9c8WTwAWDLnEXLXaHsXMAIjlVQbJxNDsHyDWgImwCUgnsTXlmMd3RC
tX+bSlMtfPsz+v8Xqp7FagwNXTHoyrPKfDTvxXg+5FVGeAj/VCtEqK2RCrvSlfid
3EqRgf9Uwj0S93XOLFjSGL86uCt/pERLgEHbfEmlN1sPb33HuqK4cAQyI9Z2tBkE
+zQpGGzkfCWavz4vJMU7SqrMyrBti/YT3OsfPeBeFWLrsila0zA=
=ANro
-----END PGP SIGNATURE-----

Attachment: pgpDtSLVMLnyy.pgp
Description: PGP signature


--- End Message ---

Reply via email to