diff --git a/src/context/shared.rs b/src/context/shared.rs index ad89636..e03ebae 100644 --- a/src/context/shared.rs +++ b/src/context/shared.rs @@ -164,7 +164,6 @@ impl TypleContext { Ok((pattern, start..end)) } - // This can return :: or which needs to be cleaned up by the caller. pub(super) fn replace_qself_path( &self, qself: &mut Option, @@ -197,8 +196,8 @@ impl TypleContext { path.segments = segments.collect(); } PathArguments::AngleBracketed(args) => { - // T::<0>::default() -> ::default() - // T::<0> -> + // T::<0>::default() -> T0::default() + // T::<0> -> T0 if args.args.len() != 1 { abort!(first, "expected one type parameter"); } @@ -211,17 +210,32 @@ impl TypleContext { let Some(value) = evaluate_usize(expr) else { abort!(expr, "unsupported tuple type index"); }; - // T<{4}>::State -> ::State - // T<{4}> -> :: - *qself = Some(QSelf { - lt_token: token::Lt::default(), - ty: Box::new(self.get_type(typle, value, first.span())?), - position: 0, - as_token: None, - gt_token: token::Gt::default(), - }); - path.leading_colon = Some(token::PathSep::default()); - path.segments = segments.collect(); + // T<{4}>::State -> T4::State + // T<{4}> -> T4 + match self.get_type(typle, value, first.span())? { + Type::Path(syn::TypePath { + qself: first_qself, + path: first_path, + }) => { + *qself = first_qself; + path.segments = first_path + .segments + .into_iter() + .chain(segments) + .collect(); + } + ty => { + *qself = Some(QSelf { + lt_token: token::Lt::default(), + ty: Box::new(ty), + position: 0, + as_token: None, + gt_token: token::Gt::default(), + }); + path.leading_colon = Some(token::PathSep::default()); + path.segments = segments.collect(); + } + } } _ => { abort!(args, "Require const parameter (wrap {} around expression)"); diff --git a/src/context/type.rs b/src/context/type.rs index fef310b..cba5688 100644 --- a/src/context/type.rs +++ b/src/context/type.rs @@ -5,7 +5,7 @@ use crate::context::shared::Replacements; use super::*; impl TypleContext { - // Replace `T`` with `(T0, T1,...)`` and `T<1>`` with `T1`` + // Replace `T` with `(T0, T1,...)` and `T<1>` with `T1` pub(super) fn replace_type(&self, ty: &mut Type) -> syn::Result<()> { match ty { Type::Array(array) => { diff --git a/src/lib.rs b/src/lib.rs index ebb7481..a525a5b 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -128,21 +128,6 @@ //! ) //! ``` //! -//! # Aggregation -//! -//! The [`typle_fold!`] macro reduces a tuple to a single value. -//! -//! ```rust -//! # use typle::typle; -//! #[typle(Tuple for 0..=12)] -//! fn sum>(t: T) -> u32 { -//! typle_fold!(0; i in .. => |total| total + t[[i]]) -//! } -//! -//! assert_eq!(sum(()), 0); -//! assert_eq!(sum((1, 4, 9, 16)), 30); -//! ``` -//! //! # Conditionals //! //! The `typle!` macro accepts an `if` statement with an optional `else` clause. @@ -171,13 +156,12 @@ //! assert_eq!(even_to_string((0, vec![1], 2, 3)), ("0".to_owned(), vec![1], "2".to_owned(), 3)); //! ``` //! -//! The associated constant `LAST` is always equal to `LEN - 1`. This is convenient for cases where -//! the final component should be treated differently, such as avoiding an additional clone. `LAST` -//! is not defined when `LEN == 0` and will cause a compilation error. -//! //! The `typle_const!` macro supports const-if on a boolean typle index expression. const-if allows -//! conditional branches that do not compile, as long as the branch are `false` at compile-time. The -//! following code compiles for `T::LEN == 0` because `T::LAST` appears in an `else` branch that is +//! conditional branches that do not compile, as long as the invalid branch is `false` at compile time. +//! +//! The associated constant `LAST` is always equal to `LEN - 1`. `LAST` is not defined when +//! `LEN == 0` and will cause a compilation error. The following code uses `T::LAST` but compiles +//! successfully for `T::LEN == 0` because `T::LAST` only appears in an `else` branch that is //! not compiled when `T::LEN == 0`. //! //! ``` @@ -217,10 +201,6 @@ //! } //! ``` //! -//! Without `typle_const!` this code will fail to compile. When there is no compilation error -//! `typle_const!` is not needed. With a constant condition the compiler will compile the false -//! branch but will optimize it out. -//! //! # Iteration //! //! Use the `typle_index!` macro in a `for` loop to iterate over a range bounded @@ -256,6 +236,21 @@ //! assert_eq!(m.interleave(), [14, 9]); //! ``` //! +//! # Aggregation +//! +//! The [`typle_fold!`] macro reduces a tuple to a single value. +//! +//! ```rust +//! # use typle::typle; +//! #[typle(Tuple for 0..=12)] +//! fn sum>(t: T) -> u32 { +//! typle_fold!(0; i in .. => |total| total + t[[i]]) +//! } +//! +//! assert_eq!(sum(()), 0); +//! assert_eq!(sum((1, 4, 9, 16)), 30); +//! ``` +//! //! # Selection //! //! Indexing using `[[i]]` only works with tuple index expressions. To select a component from a @@ -357,10 +352,10 @@ //! T2: Extract, //! T3: Extract, //! { -//! S0((), Option<::State>), -//! S1((::Output,), Option<::State>), -//! S2((::Output, ::Output), Option<::State>), -//! S3((::Output, ::Output, ::Output), Option<::State>), +//! S0((), Option), +//! S1((T0::Output,), Option), +//! S2((T0::Output, T1::Output), Option), +//! S3((T0::Output, T1::Output, T2::Output), Option), //! } //! ``` //! @@ -457,10 +452,10 @@ //! # T2: Extract, //! # T3: Extract, //! # { -//! # S0((), Option<::State>), -//! # S1((::Output,), Option<::State>), -//! # S2((::Output, ::Output), Option<::State>), -//! # S3((::Output, ::Output, ::Output), Option<::State>), +//! # S0((), Option), +//! # S1((T0::Output,), Option), +//! # S2((T0::Output, T1::Output), Option), +//! # S3((T0::Output, T1::Output, T2::Output), Option), //! # } //! # impl Extract for std::convert::Infallible { //! # type State = std::convert::Infallible; @@ -527,7 +522,7 @@ //! //! - The typle trait bound (`Tuple` in the examples) can only be applied to an //! unqualified type identifier, not to non-path types or associated types. -//! - `typle` does not work when the tuple types are only associated types +//! - The `#[typle]` macro does not work when the tuple types are only associated types //! because [associated types cannot distinguish implementations](https://github.com/rust-lang/rust/issues/20400). //! See [this file](https://github.com/jongiddy/typle/blob/main/tests/compile/unzip.rs) //! for workarounds. @@ -542,15 +537,43 @@ //! T: Tuple, //! {} //! ``` +//! - A `typle!` macro that contains a bound can only provide one bound. It is not +//! possible to use `+` to provide multiple bounds. +//! ```rust +//! # use typle::typle; +//! # use std::{ops::{Add, Mul}, time::Duration}; +//! #[typle(Tuple for 0..=3)] +//! fn sum_and_product( +//! s: S, +//! t: T, +//! ) -> (typle![i in ..T::LEN * 2 => if i % 2 == 0 { +//! as Add>>::Output +//! } else { +//! as Mul>>::Output +//! }]) +//! where +//! S<_>: Copy, +//! T<_>: Copy, +//! // Cannot use `typle!(i in .. => S<{i}>: Add> + Mul>)` +//! // but can add bounds in separate macros. +//! typle!(i in .. => S<{i}>: Add>): Tuple::Bounds, +//! typle!(i in .. => S<{i}>: Mul>): Tuple::Bounds, +//! { +//! (typle![i in ..T::LEN * 2 => if i % 2 == 0 { +//! s[[i / 2]] + t[[i / 2]] +//! } else { +//! s[[i / 2]] * t[[i / 2]] +//! }]) +//! } +//! ``` //! - Standalone `async` and `unsafe` functions are not supported. //! - Standalone functions require explicit lifetimes on references: //! ```rust //! # use std::hash::{Hash, Hasher}; //! # use typle::typle; //! #[typle(Tuple for 1..=3)] -//! pub fn hash<'a, T, S: Hasher>(tuple: &'a T, state: &'a mut S) +//! pub fn hash<'a, T: Tuple, S: Hasher>(tuple: &'a T, state: &'a mut S) //! where -//! T: Tuple, //! T<_>: Hash, //! T<{T::LAST}>: ?Sized, //! { @@ -571,18 +594,24 @@ //! } //! } //! ``` -//! - Typle index variables cannot be shadowed: +//! - Typle index variables can be shadowed by inner typle index variables +//! but cannot be shadowed by standard variables: //! ```rust //! # use typle::typle; //! # #[typle(Tuple for 1..=1)] //! # fn test(t: T) where T: Tuple { //! let mut v = vec![]; +//! //! for typle_index!(i) in 2..=3 { -//! let i = 1; -//! v.push(i); +//! for typle_index!(i) in 4..=5 { +//! let i = 1; // this `i` is ignored +//! v.push(i); // this `i` comes from `4..=5` +//! } +//! v.push(i); // this `i` comes from `2..=3` //! } -//! assert_eq!(v, [2, 3]); +//! assert_eq!(v, [4, 5, 2, 4, 5, 3]); //! # } +//! # test((1,)); //! ``` //! - Due to interaction of `typle` with other macros, passing some types and //! expressions to a macro may produce unexpected results. To help work around @@ -591,14 +620,20 @@ //! //! ```rust //! # use typle::typle; -//! # #[typle(Tuple for 3..=3)] -//! # fn test1(t: T) { -//! assert_eq!( -//! stringify!([T, T::LEN, typle_ty!(T), typle_expr!(T::LEN)]), -//! "[T, T :: LEN, (T0, T1, T2), 3]" -//! ); -//! # } -//! # test1((1, 2, 3)); +//! #[typle(Tuple for 3..=3)] +//! fn test_macro(t: T) +//! where +//! T<_>: PartialEq + std::fmt::Debug, +//! { +//! assert_eq!( +//! stringify!([T, T::LEN, typle_ty!(T), typle_expr!(T::LEN)]), +//! "[T, T :: LEN, (T0, T1, T2), 3]" +//! ); +//! for typle_index!(i) in 0..T::LEN { +//! assert_eq!(typle_expr!(t[[i]]), typle_expr!(i)); +//! } +//! } +//! test_macro((0, 1, 2)); //! ``` //! diff --git a/tests/compile/mod.expanded.rs b/tests/compile/mod.expanded.rs index b2b3aae..cd80bf0 100644 --- a/tests/compile/mod.expanded.rs +++ b/tests/compile/mod.expanded.rs @@ -158,10 +158,10 @@ pub mod doc_typle { T2: Extract, T3: Extract, { - S0((), Option<::State>), - S1((::Output,), Option<::State>), - S2((::Output, ::Output), Option<::State>), - S3((::Output, ::Output, ::Output), Option<::State>), + S0((), Option), + S1((T0::Output,), Option), + S2((T0::Output, T1::Output), Option), + S3((T0::Output, T1::Output, T2::Output), Option), } pub struct TupleSequence { tuple: T, @@ -2395,7 +2395,7 @@ pub mod type_alias { T0: Process, T1: Process, T2: Process, - = (Option<::Output>, Option<::Output>, Option<::Output>); + = (Option, Option, Option); } pub mod typle_args { #![allow(unused)] @@ -2741,7 +2741,7 @@ pub mod typle_args { where T0: HandleStuff, { - type Output = (::Output,); + type Output = (T0::Output,); fn handle_stuff(&self, input: Input) -> Self::Output { { (self.handlers.0.handle_stuff(input),) } } @@ -2751,7 +2751,7 @@ pub mod typle_args { T0: HandleStuff, T1: HandleStuff, { - type Output = (::Output, ::Output); + type Output = (T0::Output, T1::Output); fn handle_stuff(&self, input: Input) -> Self::Output { { ( @@ -2767,7 +2767,7 @@ pub mod typle_args { T1: HandleStuff, T2: HandleStuff, { - type Output = (::Output, ::Output, ::Output); + type Output = (T0::Output, T1::Output, T2::Output); fn handle_stuff(&self, input: Input) -> Self::Output { { ( @@ -2796,10 +2796,10 @@ pub mod typle_fold { where T0: UsefulTrait + std::fmt::Display, { - type UsefulType = ::UsefulType; + type UsefulType = T0::UsefulType; const SIZE: usize = (loop { let total = 0; - let total = total + ::SIZE; + let total = total + T0::SIZE; break total; }); fn display(&self) -> String { @@ -2810,13 +2810,13 @@ pub mod typle_fold { where T0: UsefulTrait + std::fmt::Display, T1: UsefulTrait + std::fmt::Display, - ::UsefulType: IsUseful<::UsefulType>, + T1::UsefulType: IsUseful, { - type UsefulType = <::UsefulType as IsUseful<::UsefulType>>::State; + type UsefulType = >::State; const SIZE: usize = (loop { let total = 0; - let total = total + ::SIZE; - let total = total + ::SIZE; + let total = total + T0::SIZE; + let total = total + T1::SIZE; break total; }); fn display(&self) -> String { @@ -2832,19 +2832,17 @@ pub mod typle_fold { T0: UsefulTrait + std::fmt::Display, T1: UsefulTrait + std::fmt::Display, T2: UsefulTrait + std::fmt::Display, - ::UsefulType: IsUseful<::UsefulType>, - ::UsefulType: IsUseful< - <::UsefulType as IsUseful<::UsefulType>>::State, - >, + T1::UsefulType: IsUseful, + T2::UsefulType: IsUseful<>::State>, { - type UsefulType = <::UsefulType as IsUseful< - <::UsefulType as IsUseful<::UsefulType>>::State, + type UsefulType = >::State, >>::State; const SIZE: usize = (loop { let total = 0; - let total = total + ::SIZE; - let total = total + ::SIZE; - let total = total + ::SIZE; + let total = total + T0::SIZE; + let total = total + T1::SIZE; + let total = total + T2::SIZE; break total; }); fn display(&self) -> String { diff --git a/tests/expand/enum.expanded.rs b/tests/expand/enum.expanded.rs index b47b737..4efec6c 100644 --- a/tests/expand/enum.expanded.rs +++ b/tests/expand/enum.expanded.rs @@ -9,8 +9,8 @@ where T0: Process, T1: Process, { - S0(Option<::State>, [u64; 0]), - S1(Option<::State>, [u64; 1]), + S0(Option, [u64; 0]), + S1(Option, [u64; 1]), U0 { u: [u32; 0] }, U1 { u: [u32; 1] }, V0, diff --git a/tests/expand/struct.expanded.rs b/tests/expand/struct.expanded.rs index 2c81837..4423888 100644 --- a/tests/expand/struct.expanded.rs +++ b/tests/expand/struct.expanded.rs @@ -3,7 +3,7 @@ type TupleSequenceOutput where T0: Extract, T1: Extract, -= (Option<::Output>, Option<::Output>); += (Option, Option); struct SeqIntoIter where T0: Into, diff --git a/tests/expand/where.expanded.rs b/tests/expand/where.expanded.rs index c90fbae..59bd216 100644 --- a/tests/expand/where.expanded.rs +++ b/tests/expand/where.expanded.rs @@ -41,7 +41,7 @@ where { fn g() { let f: TupleD = TupleD::::new(); - ::output_to_bytestream(); + T0::output_to_bytestream(); } } impl TraitD for TupleD @@ -53,6 +53,6 @@ where { fn g() { let f: TupleD = TupleD::::new(); - ::output_to_bytestream(); + T0::output_to_bytestream(); } }