Skip to main content

implied_bounds_proc_macros/
_mod.rs

1//! Crate not intended for direct use.
2//! Use https:://docs.rs/implied-bounds instead.
3// Templated by `cargo-generate` using https://github.com/danielhenrymantilla/proc-macro-template
4#![allow(nonstandard_style, unused_imports, unused_braces)]
5
6use ::core::{
7    mem,
8    ops::Not as _,
9};
10use ::proc_macro::{
11    TokenStream,
12};
13use ::proc_macro2::{
14    Span,
15    TokenStream as TokenStream2,
16    TokenTree as TT,
17};
18use ::quote::{
19    format_ident,
20    ToTokens,
21};
22use ::syn::{*,
23    parse::{Parse, Parser, ParseStream},
24    punctuated::Punctuated,
25    Result, // Explicitly shadow it
26    spanned::Spanned,
27};
28
29use self::{
30    args::{
31        Args,
32        Crate,
33    },
34    utils::{
35        compile_warning,
36        quote, quote_spanned,
37        parse_quote, parse_quote_spanned,
38        PourIntoExt,
39        SpanLocationExt,
40    },
41};
42
43mod args;
44mod utils;
45
46///
47#[proc_macro_attribute] pub
48fn implied_bounds(
49    args: TokenStream,
50    input: TokenStream,
51) -> TokenStream
52{
53    implied_bounds_impl(args.into(), input.into())
54    //  .map(|ret| { println!("{}", ret); ret })
55        .unwrap_or_else(|err| {
56            let mut errors =
57                err .into_iter()
58                    .map(|err| Error::new(
59                        err.span(),
60                        format_args!("`#[::implied_bounds::implied_bounds]`: {}", err),
61                    ))
62            ;
63            let mut err = errors.next().unwrap();
64            errors.for_each(|cur| err.combine(cur));
65            err.to_compile_error()
66        })
67        .into()
68}
69
70fn implied_bounds_impl(
71    args: TokenStream2,
72    input: TokenStream2,
73) -> Result<TokenStream2>
74{
75    let mut args: Args = parse2(args)?;
76    let mut trait_: ItemTrait = parse2(input)?;
77
78    let _guard = Crate::init(args.krate.take());
79
80    let mut debugged_predicates = vec![];
81
82    extract_non_implied_predicates(&mut trait_, &args, &mut debugged_predicates)
83        .into_iter()
84        .map(transform_into_equivalent_implied_predicate)
85        .map(WherePredicate::Type)
86        // Let's prepend rather than append since it appears to improve the diagnostics w.r.t. our
87        // duplicated predicates.
88        .chain(mem::take(&mut trait_.generics.make_where_clause().predicates))
89        .pour_into(&mut trait_.generics.make_where_clause().predicates);
90
91    let mut ret = trait_.into_token_stream();
92    debugged_predicates.into_iter().flatten().pour_into(&mut ret);
93
94    Ok(ret)
95}
96
97/// Locate and extract the non-implied predicates present in this `trait` definition.
98///
99///   - Either the bounds on a generic parameter, _e.g._, `trait Foo<T : Clone> …`;
100///   - or the `where` predicates which do not have `Self` as the LHS / "bounded type".
101///
102/// ---
103///
104/// What about `GAT` where clauses?
105///
106/// ```rust ,ignore
107/// type GatA<const B: bool> where Self::GatA<true> : Bounds;
108/// ```
109///
110/// Well, consider:
111///
112/// ```rust ,ignore
113/// type GatB<'a> where Self : 'a;
114/// type GatC<T> where T : Copy;
115/// ```
116///
117/// The latter is impossible to express in an entailed manner, since the actual semantics are:
118///
119/// ```rust ,ignore
120/// type GatB<'a where Self : 'a>;
121/// type GatC<T : Copy>;
122/// ```
123///
124/// Maybe there is a simple mechanical/algorithmic way for syntactic heuristics to distinguish
125/// between the two. For now, this work is deemed not to be worth the effort; it shall be
126/// up to the user to rewrite/move the `Self::GatA<true> : Bounds` predicate from GAT position
127/// to "`trait` `where` clause" position.
128///
129/// ---
130///
131/// This extraction is `take()`-like, as in, it *strips* the trait of these, mutating it.
132///
133///   - (except when the predicates are repeatable, in which case a copy of the original predicates
134///     are left in place, "untouched", for the sake of diagnostics).
135///
136/// It shall be the role of the caller of this function to transform the so extracted predicates
137/// into their implied/entailed form, as "super traits" / `Self :`-bounding clauses involving
138/// an interior assoc type bound (see `::implied_bounds::ImpliedPredicate`'s docs for more info).
139fn extract_non_implied_predicates(
140    trait_: &mut ItemTrait,
141    args: &Args,
142    debugged_predicates: &mut Vec<TokenStream2>,
143) -> Vec<PredicateType>
144{
145    let mut ret = vec![];
146    let mut found_clause = false;
147    let debug_report_clause: &mut dyn FnMut(&dyn ToTokens) = if args.debug.is_some() {
148        &mut |tts| {
149            found_clause = true;
150            debugged_predicates.push(
151                compile_warning(tts, "[debug] this predicate is not implied, adjusting it…")
152            );
153        }
154    } else {
155        &mut |_| {
156            found_clause = true;
157        }
158    };
159    trait_.generics.params.iter_mut().filter_map(|param_intro| {
160        let GenericParam::Type(param_intro) = param_intro else { return None };
161        let bounds = mem::take(&mut param_intro.bounds);
162        if bounds.is_empty() {
163            return None;
164        }
165        // Non-implied bounds.
166
167        debug_report_clause(&bounds);
168        if may_be_higher_ranked(&bounds).not() {
169            // a non-higher-ranked clause shall not involve a higher-ranked assoc type;
170            // which allows duplicating it.
171            // We thus try to do that duplication unless potentially non-applicable,
172            // so as to improve the diagnostics:
173            // > `X` is not `Send`
174            // rather than:
175            // > `<Self as …ImpliedPredicate<X>>::Impls` is not `Send`
176            param_intro.bounds.clone_from(&bounds);
177        }
178        Some(PredicateType {
179            lifetimes: None,
180            bounded_ty: {
181                let T @ _ = &param_intro.ident;
182                parse_quote!( #T )
183            },
184            colon_token: param_intro.colon_token?,
185            bounds,
186        })
187    }).pour_into(&mut ret);
188    if let Some(mut where_clause) = trait_.generics.where_clause.take() {
189        let mut retained_predicates = Vec::with_capacity(where_clause.predicates.len());
190        where_clause.predicates.into_iter().filter_map(|predicate| {
191            match predicate {
192                // Handle `BoundedType : …` predicates…
193                | WherePredicate::Type(predicate)
194                if  predicate.bounds.is_empty().not()
195                    // …so long as the `BoundedType` not be `Self` (since that is
196                    // a special synonym for a super-trait, rather than a mere clause).
197                    &&  matches!(
198                            &predicate.bounded_ty,
199                            // Note: this mistakenly misses `(Self)`, but there is only
200                            // so much we can do syntactically (e.g., quid of `m!(Self)`),
201                            // and it can sometimes be a handy opt-out of this branch
202                            // for those wanting to experiment with the difference between
203                            // a "mere (entailed) clause" and a super-trait (e.g. how it
204                            // affects `dyn`-ability)
205                            Type::Path(TypePath {
206                                qself: None,
207                                path: Self_,
208                            })
209                            if Self_.is_ident("Self")
210                        )
211                        .not()
212                => {
213                    // Non-implied predicate.
214                    debug_report_clause(&predicate);
215
216                    if (
217                        predicate.lifetimes.as_ref().is_some_and(|it| it.lifetimes.is_empty().not())
218                        ||
219                        may_be_higher_ranked(&predicate.bounds)
220                    ).not()
221                    {
222                        // See previous `may_be_higher_ranked()` usage above.
223                        retained_predicates.push(WherePredicate::Type(predicate.clone()));
224                    }
225
226                    Some(predicate)
227                },
228                | _ => {
229                    retained_predicates.push(predicate);
230                    None
231                },
232            }
233        }).pour_into(&mut ret);
234        where_clause.predicates = retained_predicates.into_iter().collect();
235        trait_.generics.where_clause = Some(where_clause);
236    }
237
238    if args.allow_none.is_none() && found_clause.not() {
239        debugged_predicates.push(compile_warning(
240            &..,
241            "No non-implied clauses found for this trait, you may skip using this macro altogether.\
242            \n\n\
243            To silence this warning, use `#[…implied_bounds(allow_none, …)]`.",
244        ));
245    }
246
247    ret
248}
249
250/// Transform `#bounded_ty : #bounds` into:
251///
252/// ```rust ,ignore
253/// Self : ImpliedPredicate<#bounded_ty, Impls : #bounds>
254/// ```
255///
256/// (modulo robust pathing, and `for<>` quantification).
257fn transform_into_equivalent_implied_predicate(
258    mut predicate: PredicateType
259) -> PredicateType
260{
261    // Span red tape.
262    let opening_span = predicate.span().location();
263    let closing_span =
264        predicate
265            .bounds
266            .pairs()
267            .last()
268            .and_then(|pair| pair.to_token_stream().into_iter().last())
269            .unwrap()
270            .span()
271            .location()
272    ;
273    let closing_span_angle_bracket = quote_spanned!(closing_span=>
274        >
275    );
276
277    let krate = Crate::get().unwrap_or_else(|| quote_spanned!(opening_span=>
278        ::implied_bounds
279    ));
280
281    // Replace the original LHS / `bounded_ty` of the predicate with `Self` so that
282    // this part be entailed.
283    let bounded_ty = mem::replace(
284        &mut predicate.bounded_ty,
285        parse_quote_spanned!(opening_span=> Self ),
286    );
287    let bounds = predicate.bounds;
288    // Use the `ImpliedPredicate` trick to now express, *in an entailed manner*, that
289    // `#bounded_ty : #bounds`.
290    predicate.bounds = parse_quote_spanned!(opening_span=>
291        #krate::ImpliedPredicate<
292            #bounded_ty,
293            Impls : #bounds // ,
294        #closing_span_angle_bracket // >
295    );
296
297    predicate
298}
299
300fn may_be_higher_ranked(
301    bounds: &Punctuated<TypeParamBound, Token![+]>,
302) -> bool
303{
304    bounds.iter().any(|bound| {
305        let TypeParamBound::Trait(bound) = bound else { return false };
306        // Do we have `: for<'…> …`?
307        bound.lifetimes.as_ref().is_some_and(|it| it.lifetimes.is_empty().not())
308        ||
309        // or `: Fn…(…)` (the latter is very coarse, not all `Fn…` trait clauses
310        // are higher-ranked, but since the "bound repetition" is merely there to improve
311        // diagnostics, I don't think it warrants the effort of trying to fully visit
312        // an `Fn` clause in order to determine whether its signature is actually higher-ranked).
313        matches!(
314            bound.path.segments.last().unwrap().arguments,
315            PathArguments::Parenthesized { .. },
316        )
317    })
318}