From 3e88901ee3200bc8e3a481f52fb05a6c35546a95 Mon Sep 17 00:00:00 2001 From: Tony Arcieri Date: Sun, 1 Feb 2026 15:16:11 -0700 Subject: [PATCH] cpubits: add `cfg(cpubits = "...")` override Adds support for setting a `cfg` to override the automatic target-based detection in the `cpubits!` macro and explicitly specify what bit size should be used. --- .github/workflows/cpubits.yml | 48 +++++++++++++----- cpubits/Cargo.toml | 5 +- cpubits/src/lib.rs | 91 ++++++++++++++++++++++++++++++----- 3 files changed, 119 insertions(+), 25 deletions(-) diff --git a/.github/workflows/cpubits.yml b/.github/workflows/cpubits.yml index a4f72227..d737edaf 100644 --- a/.github/workflows/cpubits.yml +++ b/.github/workflows/cpubits.yml @@ -21,27 +21,38 @@ jobs: strategy: matrix: include: - # 32-bit Linux + # `aarch64` Linux + - target: aarch64-unknown-linux-gnu + rust: 1.85.0 # MSRV + runner: ubuntu-24.04-arm + - target: aarch64-unknown-linux-gnu + rust: stable + runner: ubuntu-24.04-arm + + # `aarch64` macOS + - target: aarch64-apple-darwin + rust: 1.85.0 # MSRV + runner: macos-latest + - target: aarch64-apple-darwin + rust: stable + runner: macos-latest + + # `x86` Linux (32-bit) - target: i686-unknown-linux-gnu - platform: ubuntu-latest + runner: ubuntu-latest rust: 1.85.0 # MSRV deps: sudo apt update && sudo apt install gcc-multilib - # 64-bit Linux + # `x86_64` Linux - target: x86_64-unknown-linux-gnu - platform: ubuntu-latest + runner: ubuntu-latest rust: 1.85.0 # MSRV - # 64-bit Windows + # `x86_64` Windows - target: x86_64-pc-windows-msvc - platform: windows-latest + runner: windows-latest rust: 1.85.0 # MSRV - - # 64-bit macOS - - target: x86_64-apple-darwin - platform: macos-latest - rust: 1.85.0 # MSRV - runs-on: ${{ matrix.platform }} + runs-on: ${{ matrix.runner != '' && matrix.runner || 'ubuntu-latest' }} steps: - uses: actions/checkout@v6 - uses: RustCrypto/actions/cargo-cache@master @@ -87,3 +98,16 @@ jobs: toolchain: ${{ matrix.rust }} targets: wasm32-wasip1 - run: cargo test --target wasm32-wasip1 + + # Test `cpubits="32"` on a 64-bit target + test-override-32-on-64: + env: + RUSTFLAGS: '-Dwarnings --cfg cpubits="32"' + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v6 + - uses: RustCrypto/actions/cargo-cache@master + - uses: dtolnay/rust-toolchain@master + with: + toolchain: stable + - run: cargo test diff --git a/cpubits/Cargo.toml b/cpubits/Cargo.toml index 207c544d..46d184df 100644 --- a/cpubits/Cargo.toml +++ b/cpubits/Cargo.toml @@ -15,5 +15,6 @@ target CPU, which in some cases may differ from its address size a.k.a. `target_pointer_width`. Implemented as `macro_rules!` """ -[lints] -workspace = true +[lints.rust.unexpected_cfgs] +level = "warn" +check-cfg = ['cfg(cpubits, values("16", "32", "64"))'] diff --git a/cpubits/src/lib.rs b/cpubits/src/lib.rs index 5c95f89e..78ed49ad 100644 --- a/cpubits/src/lib.rs +++ b/cpubits/src/lib.rs @@ -4,7 +4,7 @@ html_logo_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg", html_favicon_url = "https://raw.githubusercontent.com/RustCrypto/media/6ee8e381/logo.svg" )] -#![allow(clippy::doc_markdown)] +#![warn(clippy::pedantic)] //! # Supported bit sizes //! @@ -18,6 +18,48 @@ //! ```text //! expected values for `target_pointer_width` are: `16`, `32`, and `64` //! ``` +//! +//! # Overriding the selection result via `cfg` +//! +//! This crate supports overriding its detection heuristics via an explicit `cfg` setting: +//! +//! - `cpubits = "16"`: force 16-bit +//! - `cpubits = "32"`: force 32-bit +//! - `cpubits = "64"`: force 64-bit +//! +//! This can be useful for testing different backends, and also in the event you would like to +//! override the default detection result (in which case we would appreciate it if you opened an +//! issue and let us know why). +//! +//! You can set `cfg` via the `RUSTFLAGS` environment variable: +//! +//! ```console +//! $ RUSTFLAGS='--cfg cpubits="64"' cargo build --release +//! ``` +//! +//! Or you can persistently configure it for your project in `.cargo/config.toml`: +//! +//! ```toml +//! # In .cargo/config.toml +//! [build] +//! rustflags = ['--cfg', 'cpubits="64"'] +//! ``` +//! +//! ## Lint configuration for `cfg(cpubits)` +//! +//! If you are using the `cpubits!` macro you will notice the following warning being emitted: +//! +//! ```text +//! warning: unexpected `cfg` condition name: `cpubits` +//! ``` +//! +//! You will need to add the following configuration to your `Cargo.toml` to silence the warning: +//! +//! ```toml +//! [lints.rust.unexpected_cfgs] +//! level = "warn" +//! check-cfg = ['cfg(cpubits, values("16", "32", "64"))'] +//! ``` // End of toplevel rustdoc, beginning of macro documentation. We put the detailed docs on the macro // itself so we can re-export it, and people can easily get to these docs from the re-exported @@ -47,6 +89,11 @@ /// } /// ``` /// +/// NOTE: rustc will complain: "warning: unexpected `cfg` condition name: `cpubits`" +/// +/// See the [lint configuration for `cfg(cpubits)`](./index.html#lint-configuration-for-cfgcpubits) +/// documentation for how to silence the warning. +/// /// ## Grouping multiple bit sizes /// /// If you would like to group together 16-bit and 32-bit platforms, you can do so as follows: @@ -100,8 +147,8 @@ /// certain targets from 32-bit to 64-bit ones. /// /// This 64-bit promotion occurs if `any` of the following `cfg`s are true: -/// - ARMv7: `all(target_arch = "arm", target_feature = "v7")` -/// - WASM: `target_arch = "wasm32"` +/// - `armv7`: `all(target_arch = "arm", target_feature = "v7")` +/// - `wasm32`: `target_arch = "wasm32"` #[macro_export] macro_rules! cpubits { // Only run the given block if we have selected a 16-bit word size, i.e. the code will be @@ -204,22 +251,35 @@ macro_rules! cpubits { ) => { $crate::cfg_if! { @__items () ; + // The following are effectively `if`/`else` clauses in a Lispy syntax, where each + // 2-tuple is `( ( predicate ) ( body ) )`. The first clause with a matching predicate + // is taken and the rest are ignored just like `if`/`else`. + // + // We first match on each of the explicit overrides, and if none of them are configured + // apply our heuristic logic which allows certain targets to be overridden to use + // 64-bit backends, as configured in the `enable_64_bit` predicate above. + ( + ( cpubits = "16" ) + ( $( $tokens16 )* ) + ), + ( + ( cpubits = "32" ) + ( $( $tokens32 )* ) + ), + ( + ( cpubits = "64" ) + ( $( $tokens64 )* ) + ), ( ( target_pointer_width = "16" ) ( $( $tokens16 )* ) ), ( - (all( - target_pointer_width = "32", - not($( $enable_64_bit )+) - )) + ( all(target_pointer_width = "32", not($( $enable_64_bit )+)) ) ( $( $tokens32 )* ) ), ( - (any( - target_pointer_width = "64", - $( $enable_64_bit )+ - )) + ( any(target_pointer_width = "64", $( $enable_64_bit )+) ) ( $( $tokens64 )* ) ), ( @@ -317,6 +377,7 @@ mod tests { use super::CPUBITS; /// Return the expected number of bits for the target. + #[cfg(not(any(cpubits = "16", cpubits = "32", cpubits = "64")))] fn expected_bits() -> u32 { // Duplicated 64-bit override predicates need to go here if cfg!(any( @@ -331,6 +392,7 @@ mod tests { } } + #[cfg(not(any(cpubits = "16", cpubits = "32", cpubits = "64")))] #[test] fn cpubits_works() { assert_eq!(CPUBITS, expected_bits()); @@ -351,6 +413,7 @@ mod tests { } /// Test for the `16 | 32` syntax. + #[cfg(not(any(cpubits = "16", cpubits = "32", cpubits = "64")))] #[test] fn cpubits_16_or_32_vs_64() { const BITS: u32 = { @@ -366,4 +429,10 @@ mod tests { bits => unreachable!("#{bits}-bits should be one of: 16, 32, 64"), } } + + #[cfg(cpubits = "32")] + #[test] + fn cpubits_32_bit_override() { + assert_eq!(CPUBITS, 32); + } }