From 61d827852aefc21a933111fcb05ea05631080eac Mon Sep 17 00:00:00 2001 From: Kaido Kert Date: Sat, 4 Jul 2026 19:32:29 -0700 Subject: [PATCH 1/3] Add HasPersonality projection; 0.1.1 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Downstream personality gates (modmath's NonCt marker) previously required the gating crate to name every backend type — an optional regular dep per backend, since the orphan rule forces the impl into whichever crate owns the trait. HasPersonality inverts the flow: the carrier declares its personality once (type P = P for typestate carriers, Nct for primitives), and consumers bound on HasPersonality

without naming any backend. The declaration is the carrier author's contract; it cannot be verified structurally, same trust model as any trait impl. --- Cargo.toml | 2 +- src/lib.rs | 2 +- src/personality.rs | 39 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 41 insertions(+), 2 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 360a8ddb..3d7862f7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -8,7 +8,7 @@ categories = ["algorithms", "science", "no-std"] license = "MIT OR Apache-2.0" repository = "https://github.com/kaidokert/num-traits" name = "const-num-traits" -version = "0.1.0" +version = "0.1.1" readme = "README.md" # Include-list of the files that ship. Anything not listed here is never packaged. include = [ diff --git a/src/lib.rs b/src/lib.rs index 394eea50..4544789d 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -142,7 +142,7 @@ pub use crate::ops::wrapping::{ WrappingAbs, WrappingAdd, WrappingDiv, WrappingMul, WrappingNeg, WrappingPow, WrappingRem, WrappingShl, WrappingShr, WrappingSub, }; -pub use crate::personality::{Ct, Nct, Personality, PersonalityMarker, PersonalityTag}; +pub use crate::personality::{Ct, HasPersonality, Nct, Personality, PersonalityMarker, PersonalityTag}; pub use crate::pow::{Pow, checked_pow, pow}; pub use crate::sign::{Signed, Signum, Unsigned, abs, abs_sub, signum}; diff --git a/src/personality.rs b/src/personality.rs index e6fb875a..22aae9db 100644 --- a/src/personality.rs +++ b/src/personality.rs @@ -90,6 +90,35 @@ impl Personality for Ct { /// Use as `_p: PersonalityMarker

` in struct definitions. pub type PersonalityMarker

= PhantomData P>; +/// Projects a carrier type's [`Personality`] at the type level. +/// +/// Implemented by carrier types whose implementation shape is selected +/// by a personality parameter (e.g. a bigint's `FixedUInt` +/// projects `P`), and by the primitive integers, which are always +/// [`Nct`] — their arithmetic is variable-time on common hardware. +/// +/// Consumers bound on the projection to gate algorithm choice by +/// personality: `T: HasPersonality

` admits a type into +/// variable-time code paths, `P = Ct` into constant-time-only ones. +/// The declaration is the carrier author's contract; there is no way +/// to verify it structurally. +pub trait HasPersonality { + /// The carrier's personality. + type P: Personality; +} + +macro_rules! impl_has_personality_nct { + ($($t:ty),*) => { + $( + impl HasPersonality for $t { + type P = Nct; + } + )* + }; +} + +impl_has_personality_nct!(u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize); + #[cfg(test)] mod tests { use super::*; @@ -102,6 +131,16 @@ mod tests { assert_eq!(Ct, Ct); } + #[test] + fn has_personality_projects_nct_for_primitives() { + fn assert_nct>() {} + assert_nct::(); + assert_nct::(); + assert_nct::(); + assert_nct::(); + assert_nct::(); + } + #[test] fn const_tag_dispatch() { const fn dispatched() -> u32 { From aa3532bf2d2f1bcab8d3c35895fbc0e827a3a075 Mon Sep 17 00:00:00 2001 From: Kaido Kert Date: Sat, 4 Jul 2026 19:48:10 -0700 Subject: [PATCH 2/3] personality: drop FixedUInt reference, correct Nct timing rationale MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The HasPersonality doc named fixed-bigint's concrete FixedUInt type and justified the primitives' Nct projection with "arithmetic is variable-time on common hardware" — which is false for add/sub/bitwise/shifts (constant- time everywhere) and for multiply on mainstream cores. Primitives project Nct because it is the conservative default: a single hardware-backed impl, no CT variant selected, no CT contract — and division alone is genuinely operand-dependent on common CPUs. --- src/personality.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/personality.rs b/src/personality.rs index 22aae9db..977041d8 100644 --- a/src/personality.rs +++ b/src/personality.rs @@ -93,9 +93,12 @@ pub type PersonalityMarker

= PhantomData P>; /// Projects a carrier type's [`Personality`] at the type level. /// /// Implemented by carrier types whose implementation shape is selected -/// by a personality parameter (e.g. a bigint's `FixedUInt` -/// projects `P`), and by the primitive integers, which are always -/// [`Nct`] — their arithmetic is variable-time on common hardware. +/// by a personality parameter (a bigint parameterized over its +/// personality projects that parameter), and by the primitive integers, +/// which project [`Nct`]: they expose a single hardware-backed +/// implementation, select no constant-time variant, and make no +/// constant-time guarantee — integer division in particular has +/// operand-dependent latency on common CPUs. /// /// Consumers bound on the projection to gate algorithm choice by /// personality: `T: HasPersonality

` admits a type into From a392b40937b7e3147ea08562913d122b7f15e2df Mon Sep 17 00:00:00 2001 From: Kaido Kert Date: Sat, 4 Jul 2026 20:03:16 -0700 Subject: [PATCH 3/3] personality: rustfmt the HasPersonality export and impl list cargo fmt wraps the widened root re-export and the impl_has_personality_nct! argument list; the CI gate runs fmt --check, so this unbreaks it. --- src/lib.rs | 4 +++- src/personality.rs | 4 +++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 4544789d..391cda40 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -142,7 +142,9 @@ pub use crate::ops::wrapping::{ WrappingAbs, WrappingAdd, WrappingDiv, WrappingMul, WrappingNeg, WrappingPow, WrappingRem, WrappingShl, WrappingShr, WrappingSub, }; -pub use crate::personality::{Ct, HasPersonality, Nct, Personality, PersonalityMarker, PersonalityTag}; +pub use crate::personality::{ + Ct, HasPersonality, Nct, Personality, PersonalityMarker, PersonalityTag, +}; pub use crate::pow::{Pow, checked_pow, pow}; pub use crate::sign::{Signed, Signum, Unsigned, abs, abs_sub, signum}; diff --git a/src/personality.rs b/src/personality.rs index 977041d8..32ede147 100644 --- a/src/personality.rs +++ b/src/personality.rs @@ -120,7 +120,9 @@ macro_rules! impl_has_personality_nct { }; } -impl_has_personality_nct!(u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize); +impl_has_personality_nct!( + u8, u16, u32, u64, u128, usize, i8, i16, i32, i64, i128, isize +); #[cfg(test)] mod tests {