[go: up one dir, main page]

ghost/
lib.rs

1//! [![github]](https://github.com/dtolnay/ghost) [![crates-io]](https://crates.io/crates/ghost) [![docs-rs]](https://docs.rs/ghost)
2//!
3//! [github]: https://img.shields.io/badge/github-8da0cb?style=for-the-badge&labelColor=555555&logo=github
4//! [crates-io]: https://img.shields.io/badge/crates.io-fc8d62?style=for-the-badge&labelColor=555555&logo=rust
5//! [docs-rs]: https://img.shields.io/badge/docs.rs-66c2a5?style=for-the-badge&labelColor=555555&logo=docs.rs
6//!
7//! <br>
8//!
9//! **Define your own PhantomData and similarly behaved unit types.**
10//!
11//! # Background
12//!
13//! [`PhantomData`] as defined by the Rust standard library is magical in that
14//! the same type is impossible to define in ordinary Rust code. It is defined
15//! in the standard library like this:
16//!
17//! [`PhantomData`]: core::marker::PhantomData
18//!
19//! ```
20//! # const IGNORE: &str = stringify! {
21//! #[lang = "phantom_data"]
22//! pub struct PhantomData<T: ?Sized>;
23//! # };
24//! ```
25//!
26//! The `#[lang = "..."]` attribute indicates that this is a [lang item], a
27//! special case known to the compiler. It is the only type permitted to carry
28//! an unused type parameter.
29//!
30//! [lang item]: https://manishearth.github.io/blog/2017/01/11/rust-tidbits-what-is-a-lang-item/
31//!
32//! If we try to define an equivalent unit struct with type parameter, the
33//! compiler rejects that.
34//!
35//! ```compile_fail
36//! struct MyPhantom<T: ?Sized>;
37//! ```
38//!
39//! ```text
40//! error[E0392]: parameter `T` is never used
41//!  --> src/main.rs:1:18
42//!   |
43//! 1 | struct MyPhantom<T: ?Sized>;
44//!   |                  ^ unused type parameter
45//!   |
46//!   = help: consider removing `T` or using a marker such as `std::marker::PhantomData`
47//! ```
48//!
49//! This crate provides a `#[phantom]` attribute that makes it possible to
50//! define unit structs with generic parameters.
51//!
52//! # Examples
53//!
54//! ```
55//! use ghost::phantom;
56//!
57//! #[phantom]
58//! struct MyPhantom<T: ?Sized>;
59//!
60//! fn main() {
61//!     // Proof that MyPhantom behaves like PhantomData.
62//!     let _: MyPhantom<u8> = MyPhantom::<u8>;
63//!     assert_eq!(0, std::mem::size_of::<MyPhantom<u8>>());
64//! }
65//!
66//! // Proof that MyPhantom is not just a re-export of PhantomData.
67//! // If it were a re-export, these would be conflicting impls.
68//! trait Trait {}
69//! impl<T> Trait for std::marker::PhantomData<T> {}
70//! impl<T> Trait for MyPhantom<T> {}
71//!
72//! // Proof that MyPhantom is local to the current crate.
73//! impl<T> MyPhantom<T> {
74//! }
75//! ```
76//!
77//! The implementation accepts where-clauses, lifetimes, multiple generic
78//! parameters, and derives. Here is a contrived invocation that demonstrates
79//! everything at once:
80//!
81//! ```
82//! use ghost::phantom;
83//!
84//! #[phantom]
85//! #[derive(Copy, Clone, Default, Hash, PartialOrd, Ord, PartialEq, Eq, Debug)]
86//! struct Crazy<'a, V: 'a, T> where &'a V: IntoIterator<Item = T>;
87//!
88//! fn main() {
89//!     let _ = Crazy::<'static, Vec<String>, &'static String>;
90//!
91//!     // Lifetime elision.
92//!     let crazy = Crazy::<Vec<String>, &String>;
93//!     println!("{:?}", crazy);
94//! }
95//! ```
96//!
97//! # Variance
98//!
99//! The `#[phantom]` attribute accepts attributes on individual generic
100//! parameters (both lifetime and type parameters) to make them contravariant or
101//! invariant. The default is covariance.
102//!
103//! - `#[contra]` — contravariant generic parameter
104//! - `#[invariant]` — invariant generic parameter
105//!
106//! The implications of variance are explained in more detail by the [Subtyping
107//! chapter] of the Rustonomicon.
108//!
109//! [Subtyping chapter]: https://doc.rust-lang.org/nomicon/subtyping.html
110//!
111//! ```
112//! use ghost::phantom;
113//!
114//! #[phantom]
115//! struct ContravariantLifetime<#[contra] 'a>;
116//!
117//! fn f<'a>(arg: ContravariantLifetime<'a>) -> ContravariantLifetime<'static> {
118//!     // This coercion is only legal because the lifetime parameter is
119//!     // contravariant. If it were covariant (the default) or invariant,
120//!     // this would not compile.
121//!     arg
122//! }
123//!
124//! #[phantom]
125//! struct Demo<A, #[contra] B, #[invariant] C>;
126//! #
127//! # fn main() {}
128//! ```
129//!
130//! # Documentation
131//!
132//! There are two alternatives for how to handle Rustdoc documentation on
133//! publicly exposed phantom types.
134//!
135//! You may provide documentation directly on the phantom struct in the obvious
136//! way, but Rustdoc will blithely display the somewhat distracting
137//! implementation details of the mechanism emitted by the `#[phantom]` macro.
138//! This way should be preferred if you need to document any public methods, as
139//! methods will not be visible in the other alternative.
140//!
141//! ```
142//! use ghost::phantom;
143//!
144//! /// Documentation.
145//! #[phantom]
146//! pub struct MyPhantom<T: ?Sized>;
147//!
148//! impl<T: ?Sized> MyPhantom<T> {
149//!     /// Documentation on methods.
150//!     pub fn foo() {}
151//! }
152//! #
153//! # fn main() {}
154//! ```
155//!
156//! If you aren't adding methods or don't need methods to be rendered in the
157//! documentation, the recommended idiom is as follows. Rustdoc will show a much
158//! less distracting type signature and all of your trait impls, but will not
159//! show inherent methods.
160//!
161//! ```
162//! mod private {
163//!     use ghost::phantom;
164//!
165//!     #[phantom]
166//!     pub struct MyPhantom<T: ?Sized>;
167//! }
168//!
169//! /// Documentation goes here.
170//! #[allow(type_alias_bounds)]
171//! pub type MyPhantom<T: ?Sized> = private::MyPhantom<T>;
172//!
173//! #[doc(hidden)]
174//! pub use self::private::*;
175//! #
176//! # fn main() {}
177//! ```
178//!
179//! # Use cases
180//!
181//! Entirely up to your imagination. Just to name one, how about a typed
182//! registry library that admits the following syntax for iterating over values
183//! registered of a particular type:
184//!
185//! ```no_run
186//! # use ghost::phantom;
187//! #
188//! # #[phantom]
189//! # struct Registry<T>;
190//! #
191//! # impl<T> IntoIterator for Registry<T> {
192//! #     type Item = T;
193//! #     type IntoIter = Iter<T>;
194//! #     fn into_iter(self) -> Self::IntoIter {
195//! #         unimplemented!()
196//! #     }
197//! # }
198//! #
199//! # struct Iter<T>(T);
200//! #
201//! # impl<T> Iterator for Iter<T> {
202//! #     type Item = T;
203//! #     fn next(&mut self) -> Option<Self::Item> {
204//! #         unimplemented!()
205//! #     }
206//! # }
207//! #
208//! # struct Flag;
209//! #
210//! # fn main() {
211//! for flag in Registry::<Flag> {
212//!     /* ... */
213//! }
214//! # }
215//! ```
216
217#![doc(html_root_url = "https://docs.rs/ghost/0.1.20")]
218#![allow(
219    clippy::doc_markdown,
220    // https://github.com/rust-lang/rust-clippy/issues/8538
221    clippy::iter_with_drain,
222    clippy::needless_doctest_main,
223    clippy::needless_pass_by_value,
224    clippy::too_many_lines
225)]
226
227extern crate proc_macro;
228
229mod derive;
230mod parse;
231mod variance;
232mod visibility;
233
234use proc_macro::TokenStream;
235use proc_macro2::{Ident, Span};
236use quote::quote;
237use syn::parse::Nothing;
238use syn::{parse_macro_input, GenericParam, Token};
239
240use crate::parse::UnitStruct;
241
242/// Define your own PhantomData and similarly behaved unit types.
243///
244/// Please refer to the [crate level documentation](index.html) for explanation
245/// and examples.
246#[proc_macro_attribute]
247pub fn phantom(args: TokenStream, input: TokenStream) -> TokenStream {
248    parse_macro_input!(args as Nothing);
249    let input = parse_macro_input!(input as UnitStruct);
250
251    let ident = &input.ident;
252    let call_site = Span::call_site();
253    let void_namespace = Ident::new(&format!("__void_{}", ident), call_site);
254    let value_namespace = Ident::new(&format!("__value_{}", ident), call_site);
255
256    let vis = &input.vis;
257    let vis_super = visibility::vis_super(vis);
258
259    let (derives, attrs) = match derive::expand(&input.attrs, &input) {
260        Ok(split) => split,
261        Err(err) => return err.to_compile_error().into(),
262    };
263
264    let void = Ident::new(
265        if ident == "Void" { "__Void" } else { "Void" },
266        Span::call_site(),
267    );
268
269    let type_param = Ident::new(
270        if ident == "TypeParam" {
271            "__TypeParam"
272        } else {
273            "TypeParam"
274        },
275        Span::call_site(),
276    );
277
278    let mut generics = input.generics;
279    let where_clause = generics.where_clause.take();
280    let mut impl_generics = Vec::new();
281    let mut ty_generics = Vec::new();
282    let mut phantoms = Vec::new();
283    for param in &mut generics.params {
284        match param {
285            GenericParam::Type(param) => {
286                let ident = &param.ident;
287                let elem = quote!(#ident);
288                impl_generics.push(quote!(#ident: ?::core::marker::Sized));
289                ty_generics.push(quote!(#ident));
290                phantoms.push(variance::apply(param, elem, &type_param));
291            }
292            GenericParam::Lifetime(param) => {
293                let lifetime = &param.lifetime;
294                let elem = quote!(&#lifetime ());
295                impl_generics.push(quote!(#lifetime));
296                ty_generics.push(quote!(#lifetime));
297                phantoms.push(variance::apply(param, elem, &type_param));
298            }
299            GenericParam::Const(_) => {}
300        }
301    }
302
303    let impl_generics = &impl_generics;
304    let ty_generics = &ty_generics;
305    let enum_token = Token![enum](input.struct_token.span);
306
307    TokenStream::from(quote! {
308        mod #void_namespace {
309            enum #void {}
310            #[automatically_derived]
311            impl ::core::marker::Copy for #void {}
312            #[automatically_derived]
313            #[allow(clippy::expl_impl_clone_on_copy)]
314            impl ::core::clone::Clone for #void {
315                fn clone(&self) -> Self {
316                    *self
317                }
318            }
319
320            #[repr(C, packed)]
321            struct #type_param<T: ?::core::marker::Sized>([*const T; 0]);
322            #[automatically_derived]
323            impl<T: ?::core::marker::Sized> ::core::marker::Copy for #type_param<T> {}
324            #[automatically_derived]
325            #[allow(clippy::expl_impl_clone_on_copy)]
326            impl<T: ?::core::marker::Sized> ::core::clone::Clone for #type_param<T> {
327                fn clone(&self) -> Self {
328                    *self
329                }
330            }
331            #[automatically_derived]
332            unsafe impl<T: ?::core::marker::Sized + ::core::marker::Send> ::core::marker::Send for #type_param<T> {}
333            #[automatically_derived]
334            unsafe impl<T: ?::core::marker::Sized + ::core::marker::Sync> ::core::marker::Sync for #type_param<T> {}
335
336            #[allow(non_camel_case_types)]
337            #vis_super struct #ident <#(#impl_generics),*> (
338                #(
339                    self::#type_param<#phantoms>,
340                )*
341                self::#void,
342            );
343
344            #[automatically_derived]
345            impl <#(#impl_generics),*> ::core::marker::Copy
346            for #ident <#(#ty_generics),*> {}
347
348            #[automatically_derived]
349            #[allow(clippy::expl_impl_clone_on_copy)]
350            impl <#(#impl_generics),*> ::core::clone::Clone
351            for #ident <#(#ty_generics),*> {
352                fn clone(&self) -> Self {
353                    *self
354                }
355            }
356        }
357
358        mod #value_namespace {
359            #[doc(hidden)]
360            #vis_super use super::#ident::#ident;
361        }
362
363        #(#attrs)*
364        #vis #enum_token #ident #generics #where_clause {
365            __Phantom(#void_namespace::#ident <#(#ty_generics),*>),
366            #ident,
367        }
368
369        #[doc(hidden)]
370        #vis use self::#value_namespace::*;
371
372        #derives
373    })
374}