// Copyright 2021 Ian Jackson and contributors // SPDX-License-Identifier: GPL-3.0-or-later // There is NO WARRANTY. //! Macros for `partial_borrow`. //! //! See that crate for documentation. //! NB that the **links in this view may be broken!** use std::convert::TryInto; use std::env; use std::ffi::OsString; use std::fmt; use std::fs::File; use std::io::BufRead as _; use std::io::BufReader; use std::io::BufWriter; use std::io::Seek as _; use std::io::Write as _; use std::iter; use std::num::NonZeroU8; use std::process::Command; use std::str; use darling::{FromDeriveInput}; use fehler::{throw, throws}; use indexmap::IndexMap; use itertools::Itertools; use proc_macro2::Span as Span2; use proc_macro2::TokenStream as TokenStream2; use proc_macro_error::{abort_call_site, emit_error, proc_macro_error}; use quote::{format_ident, quote, quote_spanned, ToTokens}; use syn::DeriveInput; use syn::Token; use syn::parse::{Parse, ParseStream}; use syn::parse_macro_input; static DEBUG_ENVVAR: &str = "RUST_PARTIAL_BORROW_EXPAND_DEBUG"; static STDERR: &str = "/dev/stderr"; fn ourselves_ident() -> syn::Ident { format_ident!("partial_borrow")} fn default() -> T { Default::default() } static PERMITS: [&str;3] = ["No","Const","Mut"]; #[derive(Debug,Copy,Clone,)] enum Permission { No, Const, Mut } impl quote::IdentFragment for Permission { #[throws(fmt::Error)] fn fmt(&self, f: &mut fmt::Formatter) { write!(f, "{:?}", self)? } } mod derive; mod partial; /// **Derive partial borrowing for a struct.** /// /// # Invocation details /// /// Must be applied to a sized braced struct ("`Struct`", below) /// (By "braced struct" we mean what the Reference calls a /// `StructStruct`.) /// /// The invocation scope **must** have imported the `partial_borrow` /// crate, under that name. /// /// Takes the additional attribute `#[partial_borrow(...)]` /// where `...` may be some of the following parameters: /// /// * `Debug`: Generates a `Debug` impl for the partial struct /// (and impls for its field accessor proxies). Inaccessible fields /// are debug printed as `_`. /// /// * `partial="Partial"`: The permission-parameterised partially /// borrowed struct will be called `Partial` instead of /// `Struct__Partial`. /// /// * `module="Module"`: The module containing field definitions /// will be called `Module` instead of `Struct__`. /// /// * `suffix="identchars"`: `identchars` will be used instead of /// `__` in generated identifiers, including local variables in /// generated functions, added type and lifetime parameters, and /// the default values of `partial` and `module`. /// /// See Injected names, below. /// /// * `imported_for_test_unsafe`: Indicates that the invocation scope has /// `use partial_borrow::imports::*`. The generated code will just /// say `foo` rather than `partial_borrow::imports:foo`. This is /// useful for testing and for review by humans. /// /// **Safety!** For soundness, the derived code's uses of /// now-unqualified names must always refer to the traits and types in /// `partial_borrow::imports`. This is difficult to ensure in /// nontrivial programs. This feature is provided for maintenance, /// testing, diagnostics, etc. /// /// # Generated items /// /// This derive macro introduces two names and many trait /// implementations into the invoking scope. Both the named items /// have the same visibility as `Struct`. /// /// ## Partially borrowed struct view /// /// ```ignore /// struct Struct__Partial<...> { ... } /// ``` /// /// `Struct__Partial` is generic over all the same things as `Struct`, /// but additionally, just after the lifetimes, it has additional /// type parameters: one `P__field` for each field named `field`. /// /// You can never have an owned `Struct__Partial`, just borrows of /// it. Such a borrow represents a borrow of each individual field, /// according to the permission type parameter `P__field` (which will /// be one of /// [`Mut`](perms/struct.Mut.html) /// [`Const`](perms/struct.Const.html) /// [`No`](perms/struct.No.html)). /// /// Fields with permission `Const` are only immutably accessible, even /// via an `&mut Struct_Partial`. Fields with permission `Mut` are /// mutably accessible, but of course only if you have an `&mut /// Struct_Partial`. Fields with permission `No` are inaccessible. /// /// Each field of the borrow is a smart pointer `Struct__::F_field` /// which (when allowed) dereferences to the actual field. So /// accessing fields other than by method call typically involves an /// additional `*`. /// /// ## impls /// /// * [`Downgrade`](trait.Downgrade.html) and /// [`AsRef`](core::convert::AsRef) and /// [`AsMut`](core::convert::AsMut), to /// make references to partial structs from more powerful /// references. /// /// * [`SplitOff`](trait.SplitOff.html), /// [`SplitInto`](trait.SplitInto.html) /// and [`From`] /// to divide references into /// mutually-compatible partial struct views. /// /// * If the `Debug` parameter was specified, /// [`Debug`](std::fmt::Debug) for /// `Struct_Partial` and for field accessors `Struct__::F__field`. /// /// * [`Deref`](std::ops::Deref) impl which allows any complete, but perhaps /// partially-immutable, struct view, to deref back to an /// `&Struct`. (I.e., this works for any partial view without any /// `No` permissions, only `Const` and `Mut` ones.) /// /// * [`Deref`](std::ops::Deref) and [`DerefMut`](std::ops::DerefMut) /// impls for the field accessors. /// /// Note that the downgrade and splitting traits are implemented for /// `&Struct` and `&mut Struct` as well: you make partial struct view /// reference(s) from a complete struct reference the same way you /// do from a partial struct reference. /// /// ### Implementation of other traits for partial structs /// /// Traits which do not understand partial structs are generally not /// implemented (or implementable) for them. Trait methods that only /// take `&self` can be called via the `Deref` if all of the fields /// are at least `Const`-accessible. /// /// In principle it would be possible to provide a facility to apply /// `#[derive]` attributes to the generated struct. But it would not /// be possible to pass any attributes (on the struct or any fields), /// because the attributes might be macros which would alter the /// generated `Struct__Partial`, whose shape is important for the /// soundness. /// /// Trait and inherent impls on partial structs are fine and can be /// useful. /// /// ## Module /// /// ```ignore /// mod Struct__ { /// struct F_field { ... } /// } /// ``` /// /// This contains helper items. Notably, the `F_field` type for each /// field `field`, which dereferences to the value. /// Like `Struct__Partial`, it is not possible to obtain an owned /// field accessor struct, just references. /// /// # Injected names /// /// Many of the impls require additional generic parameters, /// all of which are generated as `X__` or `X__field` or `'x__` /// for some `X` or `x`. /// /// Here is a full list of the generic parameters injected anywhere /// where they might clash with your names: /// `'r__ P__ S__ T__ N__ R__ P__field R__field S__field`. /// But: note that additions to this list will *not* be considered a semver /// break. /// /// All this will only trouble you if you derive `PartialBrorrow` on a /// struct which has other generic parameters containing `__`. /// If any injected names might clash with other generics for your struct, /// use the `suffix` paramrter to change `__` to something else. /// /// (All this is in addition to the generated items `Struct__Partial` /// `Struct__`, documented above.) #[proc_macro_derive(PartialBorrow, attributes(partial_borrow))] pub fn derive(input: proc_macro::TokenStream) -> proc_macro::TokenStream { derive::derive(input) } /// **Conveniently specify a partially borrowed struct type**, /// by field(s). /// /// # Syntax overview /// /// Roughly /// ```ignore /// type X = partial!( Struct mut/const/! field field... , /// mut/const/! field field... * , /// ... ); /// ``` /// More formally /// ```text /// Input := BaseType FieldGroup,* /// BaseType := RustReference::TypePath /// FieldGroup := Permission FieldSpec* /// Permission := 'mut' | 'const' | '!' /// FieldSpec := '*' | RustReference::IDENTIFIER /// ``` /// # Examples /// ```ignore /// struct Struct { a:char, b:char, c:char, d:char, e:char }; /// type X = partial!( Struct mut a b c, const d, ! *); /// type Y = partial!( Struct mut a ); // a is Mut, others are Const /// ``` /// /// # Interpretation /// /// `partial!()` expands to the type of a partial view of `Struct`, /// according to the specified permissions. /// /// Each list of fields starts with a a permission: /// `mut` ([`Mut`](perms/struct.Mut.html)), /// `const` ([`Const`](perms/struct.Const.html)) or /// `!` ([`No`](perms/struct.No.html)). /// The field *lists* (not the /// individual fields) are separated by commas. /// /// `*` may appear once, in any group, and specifies the permission /// for fields not explicitly mentioned. If there is no `*`, `Const` /// is used. /// /// Each field may appear only once. Ordering is not relevant. /// /// # Expansion /// /// Use of `partial!()` can be replaced by a plain concrete type /// `Struct__Partial`. /// where `P__...` are `Mut`, `Const` or `No`, one per field. /// /// For Reasons, the actual expansion of `partial!()` is something /// much more complicated and strange involving the `Adjust` trait /// impl and `Struct__::FIELDS` constant, both generated by the derive. /// /// # `impl partial!(...) { ... }` /// /// Rust does not support inherent impls on a type derived from a /// complicated chain of trait lookups, which is what `partial!()` /// produces. /// /// If you want to make a method that takes a partial struct, you can: /// /// * Use an extension trait. e.g. via `easy_ext` /// (this is quite convenient). /// * Write out the `Struct__Partial` type directly, listing one /// `Mut` `Const` or `No` type paramter for each field in order; /// * Use a newtype and impl on that; /// * Make it a plain function rather than a method. /// /// # Safety, and the field permission and borrowck systems /// /// `partial()` allows you to name a type, for example for a variable /// or function parameter. It does not do any access control /// (borrowck consistency) checks. Those are done later, after the /// meaning of `partial!()` has been determined. /// /// If what you ask for with your uses of `partial()`, reference /// reborrowing, etc., does not satisfy the rules, you will get a /// compile error. #[proc_macro] pub fn partial(input: proc_macro::TokenStream) -> proc_macro::TokenStream { partial::partial(input) } enum DebugMode<'s> { Stderr(NonZeroU8), Write(&'s str), Compare(&'s str), } struct DebugVar(Option); impl DebugVar { pub fn new() -> Self { Self(env::var_os(DEBUG_ENVVAR)) } pub fn mode(&self) -> Option { use DebugMode::*; let ev = self.0.as_ref()?; let ev = ev.to_str().expect(DEBUG_ENVVAR); if let Ok::(level) = ev.parse() { level.try_into().map(Stderr).ok() } else if let Some(rest) = ev.strip_prefix("<") { Some(Compare(rest)) } else if let Some(rest) = ev.strip_prefix(">") { Some(Write(rest)) } else { panic!("did not understand {}", DEBUG_ENVVAR) } } } impl DebugMode<'_> { pub fn wants(dhow: &Option, mverbosity: u8) -> bool { use DebugMode::*; match dhow { Some(Stderr(level)) => u8::from(*level) >= mverbosity, None | Some(Write(_)) | Some(Compare(_)) => false, } } }