diff --git a/.github/workflows/check.yml b/.github/workflows/check.yml index 389075036..29fedcbfd 100644 --- a/.github/workflows/check.yml +++ b/.github/workflows/check.yml @@ -21,6 +21,12 @@ jobs: - stable - beta - nightly + mb_const_generics: + - "" + - "--features const-generics" + exclude: + - mb_const_generics: "--features const-generics" + rust: 1.37.0 include: - os: macos-latest rust: stable @@ -36,11 +42,11 @@ jobs: - uses: actions-rs/cargo@v1 with: command: test - args: --verbose --features "strict" + args: --verbose --features "strict" ${{ matrix.mb_const_generics }} - uses: actions-rs/cargo@v1 with: command: doc - args: --features "strict" + args: --features "strict" ${{ matrix.mb_const_generics }} clippy: name: clippy + fmt diff --git a/Cargo.toml b/Cargo.toml index 783c5b0ed..ba6a42ec5 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -28,4 +28,5 @@ scale-info = { version = "1.0", default-features = false, optional = true } i128 = [] strict = [] force_unix_path_separator = [] + const-generics = [] scale_info = ["scale-info/derive"] diff --git a/build/generic_const_mappings.rs b/build/generic_const_mappings.rs new file mode 100644 index 000000000..20360e041 --- /dev/null +++ b/build/generic_const_mappings.rs @@ -0,0 +1,91 @@ +use super::*; + +pub fn emit_impls() -> ::std::io::Result<()> { + let out_dir = ::std::env::var("OUT_DIR").unwrap(); + let dest = ::std::path::Path::new(&out_dir).join("generic_const_mappings.rs"); + println!( + "cargo:rustc-env=TYPENUM_BUILD_GENERIC_CONSTS={}", + dest.display() + ); + let mut f = ::std::fs::File::create(&dest).unwrap(); + + #[allow(clippy::write_literal)] + write!(f, "{}", "\ +#[cfg(doc)] +use generic_const_mappings::*; + +/// Module with some `const`-generics-friendly definitions, to help bridge the gap +/// between those and `typenum` types. +/// +/// - It requires the `const-generics` crate feature to be enabled. +/// +/// The main type to use here is [`U`], although [`Const`] and [`ToUInt`] may be needed +/// in a generic context. +#[allow(warnings)] // script-generated code +pub mod generic_const_mappings { + use crate::*; + + /// The main mapping from a generic `const: usize` to a [`UInt`]: [`U`] is expected to work like [`UN`]. + /// + /// - It requires the `const-generics` crate feature to be enabled. + /// + /// [`U`]: `U` + /// [`UN`]: `U42` + /// + /// # Example + /// + /// ```rust + /// use typenum::*; + /// + /// assert_type_eq!(U<42>, U42); + /// ``` + /// + /// This can even be used in a generic `const N: usize` context, provided the + /// genericity is guarded by a `where` clause: + /// + /// ```rust + /// use typenum::*; + /// + /// struct MyStruct; + /// + /// trait MyTrait { type AssocType; } + /// + /// impl MyTrait + /// for MyStruct + /// where + /// Const : ToUInt, + /// { + /// type AssocType = U; + /// } + /// + /// assert_type_eq!( as MyTrait>::AssocType, U42); + /// ``` + pub type U = as ToUInt>::Output; + + /// Used to allow the usage of [`U`] in a generic context. + pub struct Const; + + /// Used to allow the usage of [`U`] in a generic context. + pub trait ToUInt { + /// The [`UN`][`crate::U42`] type corresponding to `Self = Const`. + type Output; + } +\ + ")?; + + for uint in uints() { + write!( + f, + " + impl ToUInt for Const<{uint}> {{ + type Output = U{uint}; + }} +\ + ", + uint = uint, + )?; + } + write!(f, "}}")?; + f.flush()?; + Ok(()) +} diff --git a/build/main.rs b/build/main.rs index d7e525642..903c6e4c8 100644 --- a/build/main.rs +++ b/build/main.rs @@ -4,6 +4,8 @@ use std::fs::File; use std::io::Write; use std::path::Path; +#[cfg(feature = "const-generics")] +mod generic_const_mappings; mod op; mod tests; @@ -75,19 +77,22 @@ pub fn gen_int(i: i64) -> IntCode { )] pub fn no_std() {} -// fixme: get a warning when testing without this -#[allow(dead_code)] -fn main() { - let highest: u64 = 1024; - +const HIGHEST: u64 = 1024; +fn uints() -> impl Iterator { // Use hardcoded values to avoid issues with cross-compilation. // See https://github.com/paholg/typenum/issues/162 let first2: u32 = 11; // (highest as f64).log(2.0).round() as u32 + 1; let first10: u32 = 4; // (highest as f64).log(10.0) as u32 + 1; - let uints = (0..(highest + 1)) + (0..(HIGHEST + 1)) .chain((first2..64).map(|i| 2u64.pow(i))) - .chain((first10..20).map(|i| 10u64.pow(i))); + .chain((first10..20).map(|i| 10u64.pow(i))) +} +// fixme: get a warning when testing without this +#[allow(dead_code)] +fn main() { + println!("cargo:rerun-if-changed=build/main.rs"); // Allow caching the generation if `src/*` files change. + let out_dir = env::var("OUT_DIR").unwrap(); let dest = Path::new(&out_dir).join("consts.rs"); #[cfg(not(feature = "force_unix_path_separator"))] @@ -163,11 +168,11 @@ pub mod consts {{ pub type True = B1; pub type False = B0; ", - highest = highest + highest = HIGHEST, ) .unwrap(); - for u in uints { + for u in uints() { writeln!(f, " pub type U{} = {};", u, gen_uint(u)).unwrap(); if u <= ::std::i64::MAX as u64 && u != 0 { let i = u as i64; @@ -184,4 +189,7 @@ pub mod consts {{ tests::build_tests().unwrap(); op::write_op_macro().unwrap(); + + #[cfg(feature = "const-generics")] + generic_const_mappings::emit_impls().unwrap(); } diff --git a/src/lib.rs b/src/lib.rs index 98367c802..9c7c11ed7 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -71,12 +71,16 @@ use core::cmp::Ordering; mod generated { include!(concat!(env!("OUT_DIR"), "/op.rs")); include!(concat!(env!("OUT_DIR"), "/consts.rs")); + #[cfg(feature = "const-generics")] + include!(concat!(env!("OUT_DIR"), "/generic_const_mappings.rs")); } #[cfg(not(feature = "force_unix_path_separator"))] mod generated { include!(env!("TYPENUM_BUILD_OP")); include!(env!("TYPENUM_BUILD_CONSTS")); + #[cfg(feature = "const-generics")] + include!(env!("TYPENUM_BUILD_GENERIC_CONSTS")); } pub mod bit; @@ -91,7 +95,6 @@ pub mod array; pub use crate::{ array::{ATerm, TArr}, - consts::*, generated::consts, int::{NInt, PInt}, marker_traits::*, @@ -100,6 +103,21 @@ pub use crate::{ uint::{UInt, UTerm}, }; +#[doc(no_inline)] +#[rustfmt::skip] +pub use consts::{ + False, True, B0, B1, + U0, U1, U2, *, + N1, N2, Z0, P1, P2, *, +}; + +#[cfg(feature = "const-generics")] +pub use crate::generated::generic_const_mappings; + +#[cfg(feature = "const-generics")] +#[doc(no_inline)] +pub use generic_const_mappings::{Const, ToUInt, U}; + /// A potential output from `Cmp`, this is the type equivalent to the enum variant /// `core::cmp::Ordering::Greater`. #[derive(Eq, PartialEq, Ord, PartialOrd, Clone, Copy, Hash, Debug, Default)]