cargo / typenum / audit
cargo : typenum @ 1.20.1
PE Patrick Elsen signed 2026-06-02 published 2026-06-02

Claims

has-binarieshas-build-exechas-fuzz-testshas-install-exechas-integration-testshas-property-testshas-unit-testsimpl-algorithmimpl-concurrencyimpl-cryptoimpl-datastructureimpl-interpreterimpl-jitimpl-parserimpl-protocolis-benignuses-concurrencyuses-cryptouses-environmentuses-execuses-filesystemuses-interpreteruses-jituses-networkuses-unsafe

Summary

typenum 1.20.1 is a #![no_std] library that encodes integers in the Rust type system for compile-time arithmetic, comparison, and bitwise operations. No build.rs, no proc macros, no unsafe (enforced via forbid(unsafe_code)), no runtime I/O, one optional dependency (scale-info). Code matches upstream VCS byte-for-byte; generated constants and ~1700 integration tests are checked in. One low-severity finding: CHANGELOG header advertises stale MSRV (1.37.0 vs. actual 1.41.0).

Report

Subject

typenum is a Rust library that encodes integers in the type system so that arithmetic, comparison, bitwise operations, and ordering can be performed at compile time. The crate exposes type-level bits (B0, B1), type-level unsigned integers (UTerm, UInt<U, B>), type-level signed integers (Z0, PInt<U>, NInt<U>), a type-level array (TArr), the comparison markers (Less, Equal, Greater), and a large set of trait-based type operators (Add, Sub, Mul, Div, Rem, BitAnd, BitOr, BitXor, Shl, Shr, Cmp, Min, Max, Pow, Gcd, Sqrt, Logarithm2, …). The op! macro provides a more ergonomic infix syntax over those operators via a shunting-yard macro_rules! parser. The consts module re-exports pre-named aliases (U0U1024, powers of 2, powers of 2 minus 1, powers of 10 up to 10^19, P*/N* signed counterparts, plus U3600). The crate is #![no_std] and only depends on libcore; the optional scale_info feature pulls in scale-info for Parity Substrate compatibility, and the optional const-generics feature exposes U<const N: usize> mappings.

Methodology

Tooling used:

  • openvet audit new (0.6.0) to fetch and unpack the crate from crates.io and clone the upstream GitHub repository at the commit recorded in .cargo_vcs_info.json.
  • diff -r (Apple Darwin) to compare published crate contents against the upstream VCS checkout.
  • grep to search across contents/src and contents/tests for unsafe, FFI declarations, process::*, std::net::, std::fs::, env::, panic-prone calls (panic!, expect, unwrap, unimplemented!, unreachable!), transmute, raw-pointer manipulation, and heap allocators.
  • Manual reading of the seven hand-written source files under src/ (~6.5 K LOC: lib.rs, bit.rs, int.rs, uint.rs, array.rs, tuple.rs, marker_traits.rs, operator_aliases.rs, private.rs, type_operators.rs) and surveys of the three generated files under src/gen/ (~12 K LOC) and the generated integration-test file tests/generated.rs (~21 K LOC).
  • Spot-checks of the generated content: the bit encoding of U6 (UInt<UInt<UInt<UTerm, B1>, B1>, B0> = 110₂) and the test test_4_Mul_3 (U4 * U3 == U12).

The crate ships no build.rs, no proc macros, and #![forbid(unsafe_code)] is set at the crate root, so static review is sufficient — no need to inspect generated MIR or runtime behaviour.

The published typenum-1.20.1.crate was diffed against the upstream repository at the commit pinned in .cargo_vcs_info.json. All files under src/ and the tests/generated.rs integration-test file match byte-for-byte; the only differences are cargo's Cargo.toml normalisation and the upstream-only .envrc, .git, .github/, clippy.toml, flake.lock, flake.nix, justfile, and generate/ workspace member (all explicitly excluded via the exclude list in Cargo.toml.orig).

Results

The diff between published contents and the upstream repository shows no unexpected changes. The crate contains no binary artefacts (justifying has-binaries) and no build.rs. The 1.18.0 changelog records the removal of an earlier build.rs that generated the constants in favour of checking the output into the repository under src/gen/; the 1.20.0 changelog records the removal of the test-generation build.rs in favour of checking the generated tests into tests/generated.rs. The published Cargo.toml accordingly sets build = false. The [lib] section sets no proc-macro = true, so the crate is a normal library. Together this justifies has-build-exec and has-install-exec.

The codebase was reviewed for unsafe, FFI, process spawning, network or filesystem I/O, environment variables, concurrency primitives, JIT/interpreter behaviour, and cryptography. None was found, justifying uses-unsafe (enforced by #![forbid(unsafe_code)] at the crate root), uses-exec, uses-network, uses-filesystem, uses-environment, uses-concurrency, uses-crypto, uses-jit, and uses-interpreter, and likewise the corresponding implementation claims impl-crypto, impl-interpreter, impl-jit, impl-protocol, and impl-concurrency. Although the crate implements type-level arithmetic, the work happens entirely in the type system via trait resolution at compile time, not in runtime code paths; the corresponding impl-* claims (impl-algorithm, impl-datastructure, impl-parser) are about runtime algorithms and data structures and are therefore asserted false. The op! macro is a macro_rules! shunting-yard expansion to nested type aliases; it is not a parser in the data-format sense and is also covered by impl-parser.

Test coverage is provided by per-module #[cfg(test)] unit tests (truth-table coverage of bit operations in src/bit.rs; binary-formatting coverage in src/uint.rs; bit-creation in src/bit.rs) — justifying has-unit-tests — and by the auto-generated integration-test file tests/generated.rs with roughly 1 700 #[test] functions exercising bitwise, arithmetic, and comparison operations across the unsigned-integer constants, justifying has-integration-tests. No fuzz harness and no property-test harness ship with the crate or appear in the upstream tree, justifying has-fuzz-tests and has-property-tests.

One low-severity quality finding (FINDING-1) was recorded: the header of CHANGELOG.md continues to advertise an MSRV of 1.37.0 even though Cargo.toml declares rust-version = "1.41.0" and the changelog entry for 1.20.0 itself records the bump. Documentation-only inconsistency; no impact on compilation or behaviour.

No malicious behaviour was identified, justifying is-benign.

Conclusion

typenum is a mature, narrowly-scoped library that pushes integer arithmetic into the Rust type system. The crate has no runtime I/O of any kind, no unsafe (compiler-enforced via forbid(unsafe_code)), no build script, no proc macros, no procedural code execution at compile time beyond standard trait resolution, and only one optional dependency (scale-info). The hand-written code is well documented, the generated portions are clearly labelled and reproduced from a separate generate/ workspace member in the upstream repository, and the generated integration-test suite provides broad coverage of the arithmetic operators. The one finding is a stale MSRV claim in the changelog header.

Findings(1)

FINDING-1 quality low

CHANGELOG header advertises stale MSRV

The CHANGELOG.md header states:

The MSRV (Minimum Supported Rust Version) is 1.37.0, and typenum is tested against this Rust version.

The actual MSRV is 1.41.0:

  • Cargo.toml declares rust-version = "1.41.0".
  • The 1.20.0 changelog entry includes [changed] MSRV now 1.41.0.
  • The upstream CI workflow (vcs/.github/workflows/check.yml) tests against 1.41.0, not 1.37.0.

The header line was not updated when the MSRV was bumped in 1.20.0. Documentation-only inconsistency, no runtime impact.

Annotations(7)

CHANGELOG.md

CHANGELOG.md, line 5-6

The MSRV (Minimum Supported Rust Version) is 1.37.0, and typenum is tested
against this Rust version.

Header advertises MSRV 1.37.0, but Cargo.toml.orig sets rust-version = "1.41.0" and the 1.20.0 entry below records the bump. See FINDING-1.

Cargo.toml

Cargo.toml, line 12-19

[package]
edition = "2018"
rust-version = "1.41.0"
name = "typenum"
version = "1.20.1"
build = false
exclude = [
    "/.envrc",

build = false and no build.rs in the crate root: no compile-time code execution on downstream consumers. The lib section sets no proc-macro = true; the crate is a normal library. Justifies has-build-exec and has-install-exec.

The CHANGELOG records that the build.rs was removed in 1.18.0 and the test-generation build.rs was removed in 1.20.0 in favour of checking the generated files into the source tree (now src/gen/* and tests/generated.rs).

src/bit.rs

src/bit.rs, line 187-232

#[cfg(test)]
mod bit_op_tests {
    use core::ops::{BitAnd, BitOr, BitXor, Not};

    use crate::{B0, B1};

    // macro for testing operation results. Uses `Same` to ensure the types are equal and
    // not just the values they evaluate to.
    macro_rules! test_bit_op {
        ($op:ident $Lhs:ident = $Answer:ident) => {{
            type Test = <<$Lhs as $op>::Output as $crate::Same<$Answer>>::Output;
            assert_eq!(
                <$Answer as $crate::Bit>::to_u8(),
                <Test as $crate::Bit>::to_u8()
            );
        }};
        ($Lhs:ident $op:ident $Rhs:ident = $Answer:ident) => {{
            type Test = <<$Lhs as $op<$Rhs>>::Output as $crate::Same<$Answer>>::Output;
            assert_eq!(
                <$Answer as $crate::Bit>::to_u8(),
                <Test as $crate::Bit>::to_u8()
            );
        }};
    }

    #[test]
    fn bit_operations() {
        test_bit_op!(Not B0 = B1);
        test_bit_op!(Not B1 = B0);

        test_bit_op!(B0 BitAnd B0 = B0);
        test_bit_op!(B0 BitAnd B1 = B0);
        test_bit_op!(B1 BitAnd B0 = B0);
        test_bit_op!(B1 BitAnd B1 = B1);

        test_bit_op!(B0 BitOr B0 = B0);
        test_bit_op!(B0 BitOr B1 = B1);
        test_bit_op!(B1 BitOr B0 = B1);
        test_bit_op!(B1 BitOr B1 = B1);

        test_bit_op!(B0 BitXor B0 = B0);
        test_bit_op!(B0 BitXor B1 = B1);
        test_bit_op!(B1 BitXor B0 = B1);
        test_bit_op!(B1 BitXor B1 = B0);
    }
}

Unit tests for bit operations: every truth-table entry for Not, BitAnd, BitOr, BitXor is exercised via the test_bit_op! macro, which uses Same to confirm both the type and the runtime value match. Justifies has-unit-tests.

src/gen

Three files (consts.rs, generic_const_mappings.rs, op.rs) marked // THIS IS GENERATED CODE. Produced by the generate/ workspace member in the upstream repo (not shipped in the published crate). The generated files were spot-checked: for example, U6 = UInt<UInt<UInt<UTerm, B1>, B1>, B0> correctly encodes binary 110 with the outermost bit as the LSB.

src/int.rs

src/int.rs, line 160-169

// Simply negating the result of e.g. `U::I8` will result in overflow for `std::i8::MIN`. Instead,
// we use the fact that `U: NonZero` by subtracting one from the `U::U8` before negating.
impl<U: Unsigned + NonZero> Integer for NInt<U> {
    const I8: i8 = -((U::U8 - 1) as i8) - 1;
    const I16: i16 = -((U::U16 - 1) as i16) - 1;
    const I32: i32 = -((U::U32 - 1) as i32) - 1;
    const I64: i64 = -((U::U64 - 1) as i64) - 1;
    #[cfg(feature = "i128")]
    const I128: i128 = -((U::U128 - 1) as i128) - 1;
    const ISIZE: isize = -((U::USIZE - 1) as isize) - 1;

Careful handling of i8::MIN overflow: the const expressions -((U::U8 - 1) as i8) - 1 avoid the overflow that would occur from naive -(U::I8) when U == 128. The U: NonZero bound guarantees U::U8 >= 1, so the subtraction does not underflow.

src/lib.rs

src/lib.rs, line 42-48

#![no_std]
#![forbid(unsafe_code)]
#![warn(missing_docs)]
#![cfg_attr(feature = "strict", deny(missing_docs))]
#![cfg_attr(feature = "strict", deny(warnings))]
#![doc(html_root_url = "https://docs.rs/typenum/1.20.1")]
#![cfg_attr(docsrs, feature(doc_cfg))]

Crate-level attributes: #![no_std] (libcore only) and #![forbid(unsafe_code)] (compiler-enforced absence of unsafe). Justifies uses-unsafe.

tests/generated.rs

Auto-generated integration test file with ~1700 #[test] functions exercising bitwise, arithmetic, and comparison operations across the unsigned-integer constants. Each test asserts via Same<Expected>::Output that the type-level result matches the expected constant. Spot-checked test_4_Mul_3 (U4 * U3 == U12). Justifies has-integration-tests.