partial_borrow_macros/
macros.rs

1// Copyright 2021 Ian Jackson and contributors
2// SPDX-License-Identifier: GPL-3.0-or-later
3// There is NO WARRANTY.
4
5//! Macros for `partial_borrow`.
6//!
7//! See that crate for documentation.
8//! NB that the **links in this view may be broken!**
9
10use std::convert::TryInto;
11use std::env;
12use std::ffi::OsString;
13use std::fmt;
14use std::fs::File;
15use std::io::BufRead as _;
16use std::io::BufReader;
17use std::io::BufWriter;
18use std::io::Seek as _;
19use std::io::Write as _;
20use std::iter;
21use std::num::NonZeroU8;
22use std::process::Command;
23use std::str;
24
25use darling::{FromDeriveInput};
26use fehler::{throw, throws};
27use indexmap::IndexMap;
28use itertools::Itertools;
29use proc_macro2::Span as Span2;
30use proc_macro2::TokenStream as TokenStream2;
31use proc_macro_error::{abort_call_site, emit_error, proc_macro_error};
32use quote::{format_ident, quote, quote_spanned, ToTokens};
33use syn::DeriveInput;
34use syn::Token;
35use syn::parse::{Parse, ParseStream};
36use syn::parse_macro_input;
37
38static DEBUG_ENVVAR: &str = "RUST_PARTIAL_BORROW_EXPAND_DEBUG";
39static STDERR: &str = "/dev/stderr";
40
41fn ourselves_ident() -> syn::Ident { format_ident!("partial_borrow")}
42
43fn default<T:Default>() -> T { Default::default() }
44
45static PERMITS: [&str;3] = ["No","Const","Mut"];
46#[derive(Debug,Copy,Clone,)]
47enum Permission { No, Const, Mut }
48impl quote::IdentFragment for Permission {
49  #[throws(fmt::Error)]
50  fn fmt(&self, f: &mut fmt::Formatter) { write!(f, "{:?}", self)? }
51}
52
53
54mod derive;
55mod partial;
56
57/// **Derive partial borrowing for a struct.**
58///
59/// # Invocation details
60///
61/// Must be applied to a sized braced struct ("`Struct`", below)
62/// (By "braced struct" we mean what the Reference calls a
63/// `StructStruct`.)
64///
65/// The invocation scope **must** have imported the `partial_borrow`
66/// crate, under that name.
67///
68/// Takes the additional attribute `#[partial_borrow(...)]`
69/// where `...` may be some of the following parameters:
70///
71///  * `Debug`: Generates a `Debug` impl for the partial struct
72///    (and impls for its field accessor proxies).  Inaccessible fields
73///    are debug printed as `_`.
74///
75///  * `partial="Partial"`: The permission-parameterised partially
76///    borrowed struct will be called `Partial` instead of
77///    `Struct__Partial`.
78///
79///  * `module="Module"`: The module containing field definitions
80///    will be called `Module` instead of `Struct__`.
81///
82///  * `suffix="identchars"`: `identchars` will be used instead of
83///    `__` in generated identifiers, including local variables in
84///    generated functions, added type and lifetime parameters, and
85///    the default values of `partial` and `module`.
86///
87///    See Injected names, below.
88///
89///  * `imported_for_test_unsafe`: Indicates that the invocation scope has
90///    `use partial_borrow::imports::*`.  The generated code will just
91///    say `foo` rather than `partial_borrow::imports:foo`.  This is
92///    useful for testing and for review by humans.
93///
94///    **Safety!** For soundness, the derived code's uses of
95///    now-unqualified names must always refer to the traits and types in
96///    `partial_borrow::imports`.  This is difficult to ensure in
97///    nontrivial programs.  This feature is provided for maintenance,
98///    testing, diagnostics, etc.
99///
100/// # Generated items
101///
102/// This derive macro introduces two names and many trait
103/// implementations into the invoking scope.  Both the named items
104/// have the same visibility as `Struct`.
105///
106/// ## Partially borrowed struct view
107///
108/// ```ignore
109/// struct Struct__Partial<...> { ... }
110/// ```
111///
112/// `Struct__Partial` is generic over all the same things as `Struct`,
113/// but additionally, just after the lifetimes, it has additional
114/// type parameters: one `P__field` for each field named `field`.
115///
116/// You can never have an owned `Struct__Partial`, just borrows of
117/// it.  Such a borrow represents a borrow of each individual field,
118/// according to the permission type parameter `P__field` (which will
119/// be one of
120/// [`Mut`](perms/struct.Mut.html)
121/// [`Const`](perms/struct.Const.html)
122/// [`No`](perms/struct.No.html)).
123///
124/// Fields with permission `Const` are only immutably accessible, even
125/// via an `&mut Struct_Partial`.  Fields with permission `Mut` are
126/// mutably accessible, but of course only if you have an `&mut
127/// Struct_Partial`.  Fields with permission `No` are inaccessible.
128///
129/// Each field of the borrow is a smart pointer `Struct__::F_field`
130/// which (when allowed) dereferences to the actual field.  So
131/// accessing fields other than by method call typically involves an
132/// additional `*`.
133///
134/// ## impls
135///
136///  * [`Downgrade`](trait.Downgrade.html) and
137///    [`AsRef`](core::convert::AsRef) and
138///    [`AsMut`](core::convert::AsMut), to
139///    make references to partial structs from more powerful
140///    references.
141///
142///  * [`SplitOff`](trait.SplitOff.html),
143///    [`SplitInto`](trait.SplitInto.html)
144///    and [`From`]
145///    to divide references into
146///    mutually-compatible partial struct views.
147///
148///  * If the `Debug` parameter was specified,
149///    [`Debug`](std::fmt::Debug) for
150///    `Struct_Partial` and for field accessors `Struct__::F__field`.
151///
152///  * [`Deref`](std::ops::Deref) impl which allows any complete, but perhaps
153///    partially-immutable, struct view, to deref back to an
154///    `&Struct`.  (I.e., this works for any partial view without any
155///    `No` permissions, only `Const` and `Mut` ones.)
156///
157///  * [`Deref`](std::ops::Deref) and [`DerefMut`](std::ops::DerefMut)
158///    impls for the field accessors.
159///
160/// Note that the downgrade and splitting traits are implemented for
161/// `&Struct` and `&mut Struct` as well: you make partial struct view
162/// reference(s) from a complete struct reference the same way you
163/// do from a partial struct reference.
164///
165/// ### Implementation of other traits for partial structs
166///
167/// Traits which do not understand partial structs are generally not
168/// implemented (or implementable) for them.  Trait methods that only
169/// take `&self` can be called via the `Deref` if all of the fields
170/// are at least `Const`-accessible.
171///
172/// In principle it would be possible to provide a facility to apply
173/// `#[derive]` attributes to the generated struct.  But it would not
174/// be possible to pass any attributes (on the struct or any fields),
175/// because the attributes might be macros which would alter the
176/// generated `Struct__Partial`, whose shape is important for the
177/// soundness.
178///
179/// Trait and inherent impls on partial structs are fine and can be
180/// useful.
181///
182/// ## Module
183///
184/// ```ignore
185/// mod Struct__ {
186///     struct F_field<Permission, FieldType, Struct> { ... }
187/// }
188/// ```
189///
190/// This contains helper items.  Notably, the `F_field` type for each
191/// field `field`, which dereferences to the value.
192/// Like `Struct__Partial`, it is not possible to obtain an owned
193/// field accessor struct, just references.
194///
195/// # Injected names
196///
197/// Many of the impls require additional generic parameters,
198/// all of which are generated as `X__` or `X__field` or `'x__`
199/// for some `X` or `x`.
200///
201/// Here is a full list of the generic parameters injected anywhere
202/// where they might clash with your names:
203/// `'r__ P__ S__ T__ N__ R__ P__field R__field S__field`.
204/// But: note that additions to this list will *not* be considered a semver
205/// break.
206///
207/// All this will only trouble you if you derive `PartialBrorrow` on a
208/// struct which has other generic parameters containing `__`.
209/// If any injected names might clash with other generics for your struct,
210/// use the `suffix` paramrter to change `__` to something else.
211///
212/// (All this is in addition to the generated items `Struct__Partial`
213/// `Struct__`, documented above.)
214
215#[proc_macro_derive(PartialBorrow, attributes(partial_borrow))]
216pub fn derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
217  derive::derive(input)
218}
219
220/// **Conveniently specify a partially borrowed struct type**,
221/// by field(s).
222///
223/// # Syntax overview
224///
225/// Roughly
226/// ```ignore
227/// type X = partial!( Struct mut/const/! field field... , 
228///                           mut/const/! field field... * ,
229///                           ... );
230/// ```
231/// More formally
232/// ```text
233/// Input      := BaseType FieldGroup,*
234/// BaseType   := RustReference::TypePath
235/// FieldGroup := Permission FieldSpec*
236/// Permission := 'mut' | 'const' | '!'
237/// FieldSpec  := '*' | RustReference::IDENTIFIER
238/// ```
239/// # Examples
240/// ```ignore
241/// struct Struct { a:char, b:char, c:char, d:char, e:char };
242/// type X = partial!( Struct mut a b c, const d, ! *);
243/// type Y = partial!( Struct mut a ); // a is Mut, others are Const
244/// ```
245///
246/// # Interpretation
247///
248/// `partial!()` expands to the type of a partial view of `Struct`,
249/// according to the specified permissions.
250///
251/// Each list of fields starts with a a permission:
252/// `mut` ([`Mut`](perms/struct.Mut.html)),
253/// `const` ([`Const`](perms/struct.Const.html)) or 
254/// `!` ([`No`](perms/struct.No.html)).
255/// The field *lists* (not the
256/// individual fields) are separated by commas.
257///
258/// `*` may appear once, in any group, and specifies the permission
259/// for fields not explicitly mentioned.  If there is no `*`, `Const`
260/// is used.
261///
262/// Each field may appear only once.  Ordering is not relevant.
263///
264/// # Expansion
265///
266/// Use of `partial!()` can be replaced by a plain concrete type
267/// `Struct__Partial<P_field0, P_field1, P_field2, ...>`.
268/// where `P__...` are `Mut`, `Const` or `No`, one per field.
269///
270/// For Reasons, the actual expansion of `partial!()` is something
271/// much more complicated and strange involving the `Adjust` trait
272/// impl and `Struct__::FIELDS` constant, both generated by the derive.
273///
274/// # `impl partial!(...) { ... }`
275///
276/// Rust does not support inherent impls on a type derived from a
277/// complicated chain of trait lookups, which is what `partial!()`
278/// produces.
279///
280/// If you want to make a method that takes a partial struct, you can:
281///
282///  * Use an extension trait. e.g. via `easy_ext`
283///    (this is quite convenient).
284///  * Write out the `Struct__Partial` type directly, listing one
285///  `Mut` `Const` or `No` type paramter for each field in order;
286///  * Use a newtype and impl on that;
287///  * Make it a plain function rather than a method.
288///
289/// # Safety, and the field permission and borrowck systems
290///
291/// `partial()` allows you to name a type, for example for a variable
292/// or function parameter.  It does not do any access control
293/// (borrowck consistency) checks.  Those are done later, after the
294/// meaning of `partial!()` has been determined.
295///
296/// If what you ask for with your uses of `partial()`, reference
297/// reborrowing, etc., does not satisfy the rules, you will get a
298/// compile error.
299#[proc_macro]
300pub fn partial(input: proc_macro::TokenStream) -> proc_macro::TokenStream {
301  partial::partial(input)
302}
303
304enum DebugMode<'s> {
305  Stderr(NonZeroU8),
306  Write(&'s str),
307  Compare(&'s str),
308}
309
310struct DebugVar(Option<OsString>);
311impl DebugVar {
312  pub fn new() -> Self { Self(env::var_os(DEBUG_ENVVAR)) }
313
314  pub fn mode(&self) -> Option<DebugMode> {
315    use DebugMode::*;
316    let ev = self.0.as_ref()?;
317    let ev = ev.to_str().expect(DEBUG_ENVVAR);
318    if let Ok::<u8,_>(level) = ev.parse() {
319      level.try_into().map(Stderr).ok()
320    }
321    else if let Some(rest) = ev.strip_prefix("<") { Some(Compare(rest)) }
322    else if let Some(rest) = ev.strip_prefix(">") { Some(Write(rest)) }
323    else { panic!("did not understand {}", DEBUG_ENVVAR) }
324  }    
325}
326
327impl DebugMode<'_> {
328  pub fn wants(dhow: &Option<Self>, mverbosity: u8) -> bool {
329    use DebugMode::*;
330    match dhow {
331      Some(Stderr(level)) => u8::from(*level) >= mverbosity,
332      None | Some(Write(_)) | Some(Compare(_)) => false,
333    }
334  }
335}