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}