specialize/
lib.rs

1#![cfg_attr(test, feature(specialization))]
2
3/*!
4 * Experimental specialization macros.
5 *
6 * Syntax is highly volatile and subject to change. The use of these macros
7 * requires an unstable rust compiler and the `#![feature(specialization)]`
8 * crate attribute.
9 *
10 * ## constrain!()
11 *
12 * `constrain!()` attempts to add additional trait bounds to a generic type.
13 *
14 * ```
15 * # #![feature(specialization)]
16 * # #[macro_use]
17 * # extern crate specialize;
18 * # fn main() { }
19 * use std::io::{Write, Read, Seek, SeekFrom, Cursor, Repeat};
20 *
21 * fn generic_function<T: Read>(read: &mut T) {
22 *     if let Some(read_seek) = constrain!(ref mut [read: Read + Seek] = Cursor<Repeat>) {
23 *         read_seek.seek(SeekFrom::Start(0));
24 *     } else if constrain!(type [T: Write]) {
25 *         // ...
26 *     }
27 * }
28 * ```
29 * 
30 * ### Caveats and Notes
31 *
32 * There are a few oddities in the above example...
33 *
34 * 1. You must specify a name for the trait to be used behind the scenes for
35 *    specialization, as in `IsReadSeek`. It may be called whatever you like.
36 * 2. A default fallback type that implements the desired trait bounds must also
37 *    be provided. This type must be concrete but will never be used or
38 *    instantiated, and is only used to appease the type checker.
39 * 3. The `ref` and `mut` keywords may be left out in order to move or immutably
40 *    borrow the value.
41 * 4. The `read` value part may be an expression if surrounded in parenthesis.
42 *
43 * ### Concrete constrain!()
44 *
45 * `constrain!()` can also be used to convert a generic type into a concrete type.
46 * Similar to `Any::downcast_ref`, but enforced at compile time and without
47 * requiring the `Any` or `Reflect` trait bounds.
48 *
49 * ```
50 * # #![feature(specialization)]
51 * # #[macro_use]
52 * # extern crate specialize;
53 * # fn main() { }
54 * fn generic_function<T: ?Sized>(value: &T) -> bool {
55 *     if let Some(value_bool) = constrain!(ref value as bool) {
56 *         *value_bool
57 *     } else if constrain!(type T as i32) {
58 *         true
59 *     } else {
60 *         false
61 *     }
62 * }
63 * ```
64 *
65 * ## specialize! { }
66 *
67 * Provides a type-level `match` statement with specialization bounds.
68 *
69 * ```
70 * # #![feature(specialization)]
71 * # #[macro_use]
72 * # extern crate specialize;
73 * # fn main() { }
74 * use std::io::{Read, BufRead, Write, copy, sink};
75 *
76 * fn generic_function<T: Read>(read: &mut T) {
77 *     specialize! {
78 *         trait fn Trait::nonsensical[W: Write](&mut self, w: &mut W) -> bool where [W: Write];
79 *
80 *         match impl['a, T: Read] for T where [T: Read] {
81 *             where [T: BufRead] => self.fill_buf().and_then(|buf| w.write(buf)).is_ok(),
82 *             impl[U: Into<u8> + Clone] where [T: BufRead + Iterator<Item=&'a U>, U: 'a] =>
83 *                 w.write(&self.cloned().map(Into::into).collect::<Vec<u8>>()).is_ok(),
84 *             _ => copy(self, w).is_ok(),
85 *         }
86 *     }
87 *
88 *     Trait::nonsensical(read, &mut sink());
89 * }
90 * ```
91 *
92 * ### Caveats
93 *
94 * 1. This is not an inline statement or expression, and is instead used to
95 *    generate a trait and method pair. This means the prototype must be specified
96 *    up-front, and no variables will be automatically captured from the outside
97 *    scope.
98 * 2. Generic bounds and where clauses must be surrounded by `[]` rather than
99 *    `<>` or similar due to macro parsing limitations.
100 * 3. The specialization "more specific" rules must be followed in order to
101 *    prevent conflicting trait impls.
102 * 4. The various `where [B: Bounds...]` clauses may be omitted, and are mostly
103 *    included here for syntax demonstration.
104 *
105 */
106
107#[macro_export]
108#[doc(hidden)]
109macro_rules! specialize {
110    (
111        trait fn $trait_id:ident :: $trait_fn_id:ident
112        $($unparsed:tt)*
113    ) => {
114        specialize! {
115            @parse_fn_bounds
116            $trait_id $trait_fn_id
117            $($unparsed)*
118        }
119    };
120    (@parse_fn_bounds $trait_id:ident $trait_fn_id:ident (
121            $($trait_fn_args:tt)*
122        )
123        $($unparsed:tt)*
124    ) => {
125        specialize! {
126            @parse_fn_ty
127            $trait_id $trait_fn_id () ($($trait_fn_args)*)
128            $($unparsed)*
129        }
130    };
131    (@parse_fn_bounds $trait_id:ident $trait_fn_id:ident [
132            $($trait_fn_bounds:tt)+
133        ] (
134            $($trait_fn_args:tt)*
135        )
136
137        $($unparsed:tt)*
138    ) => {
139        specialize! {
140            @parse_fn_ty
141            $trait_id $trait_fn_id ($($trait_fn_bounds)+) ($($trait_fn_args)*)
142            $($unparsed)*
143        }
144    };
145    (@parse_fn_ty $trait_id:ident $trait_fn_id:ident $trait_fn_bounds:tt $trait_fn_args:tt
146        ;
147        $($unparsed:tt)*
148    ) => {
149        specialize! {
150            @parse_impl
151            $trait_id
152            ($trait_fn_id (()) $trait_fn_bounds () $trait_fn_args)
153            $($unparsed)*
154        }
155    };
156    (@parse_fn_ty $trait_id:ident $trait_fn_id:ident $trait_fn_bounds:tt $trait_fn_args:tt
157        -> $trait_fn_ty:ty;
158        $($unparsed:tt)*
159    ) => {
160        specialize! {
161            @parse_impl
162            $trait_id
163            ($trait_fn_id ($trait_fn_ty) $trait_fn_bounds () $trait_fn_args)
164            $($unparsed)*
165        }
166    };
167    (@parse_fn_ty $trait_id:ident $trait_fn_id:ident $trait_fn_bounds:tt $trait_fn_args:tt
168        -> $trait_fn_ty:ty where [$($trait_fn_where:tt)+];
169        $($unparsed:tt)*
170    ) => {
171        specialize! {
172            @parse_impl
173            $trait_id
174            ($trait_fn_id ($trait_fn_ty) $trait_fn_bounds (where $($trait_fn_where)+) $trait_fn_args)
175            $($unparsed)*
176        }
177    };
178    (@parse_fn_ty $trait_id:ident $trait_fn_id:ident $trait_fn_bounds:tt $trait_fn_args:tt
179        where [$($trait_fn_where:tt)+];
180        $($unparsed:tt)*
181    ) => {
182        specialize! {
183            @parse_impl
184            $trait_id
185            ($trait_fn_id (()) $trait_fn_bounds (where $($trait_fn_where)+) $trait_fn_args)
186            $($unparsed)*
187        }
188    };
189
190    (@parse_impl $trait_id:ident $trait_fn:tt
191        match impl[
192            $($trait_impl_bounds:tt)*
193        ] for $trait_impl_id:ident where [
194            $($trait_impl_where:tt)+
195        ] {
196            $($unparsed:tt)*
197        }
198    ) => {
199        specialize! {
200            @parse
201            ($trait_id $trait_impl_id ($($trait_impl_bounds)*) (, $($trait_impl_where)+))
202            $trait_fn
203            ()
204            ($($unparsed)*)
205        }
206    };
207    (@parse_impl $trait_id:ident $trait_fn:tt
208        match impl[
209            $($trait_impl_bounds:tt)*
210        ] for $trait_impl_id:ident {
211            $($unparsed:tt)*
212        }
213    ) => {
214        specialize! {
215            @parse
216            ($trait_id $trait_impl_id ($($trait_impl_bounds)*) ())
217            $trait_fn
218            ()
219            ($($unparsed)*)
220        }
221    };
222    // Parse match arms
223    // TODO: support T: X => {} match syntax without trailing comma?
224    (@parse
225        $trait_impl:tt
226        $trait_fn:tt
227        ($($clauses:tt)*)
228        (
229            where [
230                $($clause_where:tt)*
231            ] => $clause_expr:expr,
232            $($unparsed:tt)*
233        )
234    ) => {
235        specialize! { @parse
236            $trait_impl
237            $trait_fn
238            ($($clauses)*
239                (() ($($clause_where)*) $clause_expr)
240            )
241            ($($unparsed)*)
242        }
243    };
244    (@parse
245        $trait_impl:tt
246        $trait_fn:tt
247        ($($clauses:tt)*)
248        (
249            impl [
250                $($clause_bounds:tt)*
251            ] where [
252                $($clause_where:tt)*
253            ] => $clause_expr:expr,
254            $($unparsed:tt)*
255        )
256    ) => {
257        specialize! { @parse
258            $trait_impl
259            $trait_fn
260            ($($clauses)*
261                (($($clause_bounds)*) ($($clause_where)*) $clause_expr)
262            )
263            ($($unparsed)*)
264        }
265    };
266    // Match catchall
267    (@parse
268        $trait_impl:tt
269        $trait_fn:tt
270        ($($clauses:tt)*)
271        (
272            _ => $clause_expr:expr $(,)*
273        )
274    ) => {
275        specialize! { @parse
276            $trait_impl
277            $trait_fn
278            ($($clauses)*
279                (() (u8: Copy) $clause_expr)
280            )
281            ()
282        }
283    };
284    // Base case
285    (@parse
286        $trait_impl:tt
287        $trait_fn:tt
288        $clauses:tt
289        (/*unparsed*/)
290    ) => {
291        specialize! { @itemize
292            $trait_impl
293            $trait_fn
294            ()
295            $clauses
296        }
297    };
298    // Clause to trait impl
299    (@itemize
300        ($trait_id:ident $trait_impl_id:ident ($($trait_impl_bounds:tt)*) ($($trait_impl_where:tt)*))
301        ($trait_fn_id:ident ($trait_fn_ty:ty) ($($trait_fn_bounds:tt)*) ($($trait_fn_where:tt)*) ($($trait_fn_args:tt)*))
302        ($($items:tt)*)
303        ((($($clause_bounds:tt)*) ($($clause_where:tt)*) $clause_expr:expr) $($clauses:tt)*)
304    ) => {
305        specialize! { @itemize
306            ($trait_id $trait_impl_id ($($trait_impl_bounds)*) ($($trait_impl_where)*))
307            ($trait_fn_id ($trait_fn_ty) ($($trait_fn_bounds)*) ($($trait_fn_where)*) ($($trait_fn_args)*))
308            ($($items)*
309                impl<$($trait_impl_bounds)*, $($clause_bounds)*> $trait_id for $trait_impl_id where $($clause_where)* $($trait_impl_where)* {
310                    default fn $trait_fn_id<
311                        $($trait_fn_bounds)*
312                    >($($trait_fn_args)*) -> $trait_fn_ty
313                    $($trait_fn_where)* {
314                        $clause_expr
315                    }
316                }
317            )
318            ($($clauses)*)
319        }
320    };
321    // Base case
322    (@itemize
323        $trait_impl:tt
324        $trait_fn:tt
325        ($($items:tt)*)
326        (/*clauses*/)
327    ) => {
328        specialize! { @trait
329            $trait_impl
330            $trait_fn
331        }
332
333        specialize! { @items $($items)* }
334    };
335    (@trait
336        ($trait_id:ident $trait_impl_id:ident ($($trait_impl_bounds:tt)*) ($($trait_impl_where:tt)*))
337        ($trait_fn_id:ident ($trait_fn_ty:ty) ($($trait_fn_bounds:tt)*) ($($trait_fn_where:tt)*) ($($trait_fn_args:tt)*))
338    ) => {
339        specialize! { @items
340            trait $trait_id {
341                fn $trait_fn_id<
342                    $($trait_fn_bounds)*
343                >($($trait_fn_args)*) -> $trait_fn_ty
344                $($trait_fn_where)*;
345            }
346        }
347    };
348    /*(@
349        ($trait_id:ident $trait_impl_id:ident ($($trait_impl_bounds:tt)*) ($($trait_impl_where:tt)*))
350        ($trait_fn_id:ident ($trait_fn_ty:ty) ($($trait_fn_bounds:tt)*) ($($trait_fn_where:tt)*) ($($trait_fn_args:tt)*))
351        ($($items:tt)*)
352        ($($clauses:tt)*)
353        ($($unparsed:tt)*)
354    ) => {
355        specialize! { @
356            ($trait_id $trait_impl_id ($($trait_impl_bounds)*) ($($trait_impl_where)*))
357            ($trait_fn_id ($trait_fn_ty) ($($trait_fn_bounds)*) ($($trait_fn_where)*) ($($trait_fn_args)*))
358            ($($items:tt)*)
359            ($($clauses:tt)*)
360            ($($unparsed:tt)*)
361        }
362        ...
363    };*/
364    /*(@
365        $trait_impl:tt
366        $trait_fn:tt
367        $items:tt
368        $clauses:tt
369        $unparsed:tt
370    ) => {
371        specialize! { @
372            $trait_impl
373            $trait_fn
374            $items
375            $clauses
376            $unparsed
377        }
378    };*/
379    // Convert tts to items
380    (@items $($i:item)*) => { $($i)* };
381    // Drops tokens into the void
382    (@drop $($tt:tt)*) => { };
383}
384
385#[macro_export]
386#[doc(hidden)]
387macro_rules! constrain {
388    (ref mut [$value_id:tt : $($bounds:tt)*] = $default_ty:ty) => {
389        {
390            constrain! { @trait __ConstrainBounds__ ($($bounds)*) ($default_ty) }
391
392            constrain! { @expr __ConstrainBounds__::as_mut($value_id) }
393        }
394    };
395    (ref [$value_id:tt : $($bounds:tt)*] = $default_ty:ty) => {
396        {
397            constrain! { @trait __ConstrainBounds__ ($($bounds)*) ($default_ty) }
398
399            constrain! { @expr __ConstrainBounds__::as_ref($value_id) }
400        }
401    };
402    (ref [$value_id:tt : $($bounds:tt)*] = $default_ty:ty) => {
403        {
404            constrain! { @trait __ConstrainBounds__ ($($bounds)*) ($default_ty) }
405
406            constrain! { @expr __ConstrainBounds__::as_ref($value_id) }
407        }
408    };
409    (type [$value_id:tt : $($bounds:tt)*]) => {
410        {
411            constrain! { @trait __ConstrainBounds__ ($($bounds)*) }
412
413            constrain! { @expr <$value_id as __ConstrainBounds__>::is() }
414        }
415    };
416    ([$value_id:tt : $($bounds:tt)*] = $default_ty:ty) => {
417        {
418            constrain! { @trait __ConstrainBounds__ ($($bounds)*) ($default_ty) }
419
420            constrain! { @expr __ConstrainBounds__::move_($value_id) }
421        }
422    };
423    (ref mut $value_id:tt as $ty:ty) => {
424        {
425            constrain! { @trait_concrete __ConstrainEq__ ($ty) }
426
427            constrain! { @expr __ConstrainEq__::as_mut($value_id) }
428        }
429    };
430    (ref $value_id:tt as $ty:ty) => {
431        {
432            constrain! { @trait_concrete __ConstrainEq__ ($ty) }
433
434            constrain! { @expr __ConstrainEq__::as_ref($value_id) }
435        }
436    };
437    (type $ty:ty as $ty_eq:ty) => {
438        {
439            constrain! { @trait_concrete __ConstrainEq__ ($ty_eq) }
440
441            constrain! { @expr <$ty as __ConstrainEq__>::is() }
442        }
443    };
444    ($value_id:tt as $ty:ty) => {
445        {
446            constrain! { @trait_concrete __ConstrainEq__ ($ty) }
447
448            constrain! { @expr __ConstrainEq__::move_($value_id) }
449        }
450    };
451    (@trait_concrete $trait_id:ident ($ty:ty)) => {
452        trait $trait_id {
453            fn is() -> bool;
454            fn move_(self) -> Option<$ty> where Self: Sized, $ty: Sized;
455            fn as_ref(&self) -> Option<&$ty>;
456            fn as_mut(&mut self) -> Option<&mut $ty>;
457        }
458
459        impl<T: ?Sized> $trait_id for T {
460            #[inline(always)]
461            default fn is() -> bool { false }
462            #[inline(always)]
463            default fn move_(self) -> Option<$ty> where Self: Sized, $ty: Sized { None }
464            #[inline(always)]
465            default fn as_ref(&self) -> Option<&$ty> { None }
466            #[inline(always)]
467            default fn as_mut(&mut self) -> Option<&mut $ty> { None }
468        }
469
470        impl $trait_id for $ty {
471            #[inline(always)]
472            default fn is() -> bool { true }
473            #[inline(always)]
474            fn move_(self) -> Option<$ty> where Self: Sized, $ty: Sized { Some(self) }
475            #[inline(always)]
476            fn as_ref(&self) -> Option<&$ty> { Some(self) }
477            #[inline(always)]
478            fn as_mut(&mut self) -> Option<&mut $ty> { Some(self) }
479        }
480    };
481    (@trait $trait_id:ident ($($bounds:tt)*) ($default_ty:ty)) => {
482        constrain! { @items
483            trait $trait_id {
484                type Out: ?Sized + $($bounds)*;
485
486                fn is() -> bool;
487                fn move_(self) -> Option<Self::Out> where Self: Sized, Self::Out: Sized;
488                fn as_ref(&self) -> Option<&Self::Out>;
489                fn as_mut(&mut self) -> Option<&mut Self::Out>;
490            }
491
492            impl<T: ?Sized + $($bounds)*> $trait_id for T {
493                type Out = T;
494
495                #[inline(always)]
496                fn is() -> bool { true }
497                #[inline(always)]
498                fn move_(self) -> Option<Self::Out> where Self: Sized, Self::Out: Sized { Some(self) }
499                #[inline(always)]
500                fn as_ref(&self) -> Option<&Self::Out> { Some(self) }
501                #[inline(always)]
502                fn as_mut(&mut self) -> Option<&mut Self::Out> { Some(self) }
503            }
504        }
505
506        impl<T: ?Sized> $trait_id for T {
507            default type Out = $default_ty;
508
509            #[inline(always)]
510            default fn is() -> bool { false }
511            #[inline(always)]
512            default fn move_(self) -> Option<Self::Out> where Self: Sized, Self::Out: Sized { None }
513            #[inline(always)]
514            default fn as_ref(&self) -> Option<&Self::Out> { None }
515            #[inline(always)]
516            default fn as_mut(&mut self) -> Option<&mut Self::Out> { None }
517        }
518    };
519    (@trait $trait_id:ident ($($bounds:tt)*)) => {
520        constrain! { @items
521            trait $trait_id {
522                fn is() -> bool;
523            }
524
525            impl<T: ?Sized + $($bounds)*> $trait_id for T {
526                #[inline(always)]
527                fn is() -> bool { true }
528            }
529        }
530
531        impl<T: ?Sized> $trait_id for T {
532            #[inline(always)]
533            default fn is() -> bool { false }
534        }
535    };
536    (@items $($i:item)*) => { $($i)* };
537    (@expr $i:expr) => { $i };
538}
539
540#[cfg(test)]
541mod test {
542    #[test]
543    fn by_type() {
544        fn is_default<T: ?Sized>() -> bool {
545            specialize! {
546                trait fn IsDefault::is_default() -> bool;
547
548                match impl[T: ?Sized] for T {
549                    // Silly extraneous bounds for testing
550                    where [T: Default + Default, T: Default] => true,
551                    where [T: Default + Copy] => true,
552                    where [T: Default + Copy + ::std::ops::Deref, T::Target: Copy] => true,
553                    _ => false,
554                }
555            }
556
557            T::is_default()
558        }
559
560        assert_eq!(true, is_default::<()>());
561        assert_eq!(false, is_default::<[()]>());
562        assert_eq!(false, is_default::<&'static ()>());
563    }
564
565    #[test]
566    fn by_value() {
567        fn is_default<T: ?Sized>(t: &T) -> bool {
568            specialize! {
569                trait fn IsDefault::is_default(&self, true_: bool) -> bool;
570
571                match impl[T: ?Sized] for T {
572                    where [T: Default] => true && true_,
573                    where [T: Default + PartialEq] => *self == <Self as Default>::default() && true_,
574                    _ => false && true_,
575                }
576            }
577
578            t.is_default(true)
579        }
580
581        assert_eq!(true, is_default(&()));
582        assert_eq!(true, is_default(&0u8));
583        assert_eq!(false, is_default(&1u8));
584        assert_eq!(false, is_default(&[()][..]));
585    }
586
587    #[test]
588    fn fn_bounds() {
589        use std::io::{self, Write, sink};
590        use std::fmt::Display;
591
592        fn write_test<T: ?Sized>(v: &T) -> io::Result<()> {
593            specialize! {
594                trait fn WriteTest::write_test[W: Write](&self, mut w: W) -> io::Result<()>;
595
596                match impl[T: ?Sized] for T {
597                    where [T: Display] => writeln!(w, "{}", self),
598                    _ => Err(io::Error::new(io::ErrorKind::Other, "unimplemented")),
599                }
600            }
601
602            v.write_test(sink())
603        }
604
605        assert!(write_test(&0u8).is_ok());
606        assert!(write_test(&()).is_err());
607    }
608
609    #[test]
610    fn constrain_ref() {
611        use std::fmt::Display;
612
613        fn is_display<T: ?Sized>(t: &T) -> bool {
614            if let Some(display) = constrain!(ref [t: Display] = u8) {
615                println!("{}", display);
616                true
617            } else {
618                false
619            }
620        }
621
622        assert_eq!(is_display(&()), false);
623        assert_eq!(is_display(&0u32), true);
624    }
625
626    #[test]
627    fn constrain_mut() {
628        use std::io::{Write, Stdout, sink};
629
630        fn try_write<T: ?Sized>(t: &mut T) -> bool {
631            if let Some(write) = constrain!(ref mut [t: Write] = Stdout) {
632                writeln!(write, "hello!").is_ok()
633            } else {
634                false
635            }
636        }
637
638        assert_eq!(try_write(&mut ()), false);
639        assert_eq!(try_write(&mut sink()), true);
640    }
641
642    #[test]
643    fn constrain_eq() {
644        fn is_bool<T: ?Sized>() -> bool {
645            constrain!(type T as bool)
646        }
647
648        assert!(is_bool::<bool>());
649        assert!(!is_bool::<&bool>());
650        assert!(!is_bool::<u8>());
651        assert!(constrain!(type bool as bool));
652    }
653
654    #[test]
655    fn constrain_eq_value() {
656        fn is_bool<T: ?Sized>(v: &T) -> bool {
657            constrain!(ref v as bool).is_some()
658        }
659
660        assert!(is_bool(&true));
661        assert!(!is_bool(&0u8));
662    }
663}