cxx_gen/syntax/
trivial.rs

1use crate::syntax::map::UnorderedMap;
2use crate::syntax::set::{OrderedSet as Set, UnorderedSet};
3use crate::syntax::{Api, Enum, ExternFn, NamedType, Pair, Struct, Type};
4use proc_macro2::Ident;
5use std::fmt::{self, Display};
6
7#[derive(Copy, Clone)]
8pub(crate) enum TrivialReason<'a> {
9    StructField(&'a Struct),
10    FunctionArgument(&'a ExternFn),
11    FunctionReturn(&'a ExternFn),
12    BoxTarget,
13    VecElement,
14    SliceElement { mutable: bool },
15    UnpinnedMut(&'a ExternFn),
16}
17
18pub(crate) fn required_trivial_reasons<'a>(
19    apis: &'a [Api],
20    all: &Set<&'a Type>,
21    structs: &UnorderedMap<&'a Ident, &'a Struct>,
22    enums: &UnorderedMap<&'a Ident, &'a Enum>,
23    cxx: &UnorderedSet<&'a Ident>,
24) -> UnorderedMap<&'a Ident, Vec<TrivialReason<'a>>> {
25    let mut required_trivial = UnorderedMap::new();
26
27    let mut insist_extern_types_are_trivial = |ident: &'a NamedType, reason| {
28        if cxx.contains(&ident.rust)
29            && !structs.contains_key(&ident.rust)
30            && !enums.contains_key(&ident.rust)
31        {
32            required_trivial
33                .entry(&ident.rust)
34                .or_insert_with(Vec::new)
35                .push(reason);
36        }
37    };
38
39    for api in apis {
40        match api {
41            Api::Struct(strct) => {
42                for field in &strct.fields {
43                    if let Type::Ident(ident) = &field.ty {
44                        let reason = TrivialReason::StructField(strct);
45                        insist_extern_types_are_trivial(ident, reason);
46                    }
47                }
48            }
49            Api::CxxFunction(efn) | Api::RustFunction(efn) => {
50                if let Some(receiver) = &efn.receiver {
51                    if receiver.mutable && !receiver.pinned {
52                        let reason = TrivialReason::UnpinnedMut(efn);
53                        insist_extern_types_are_trivial(&receiver.ty, reason);
54                    }
55                }
56                for arg in &efn.args {
57                    match &arg.ty {
58                        Type::Ident(ident) => {
59                            let reason = TrivialReason::FunctionArgument(efn);
60                            insist_extern_types_are_trivial(ident, reason);
61                        }
62                        Type::Ref(ty) => {
63                            if ty.mutable && !ty.pinned {
64                                if let Type::Ident(ident) = &ty.inner {
65                                    let reason = TrivialReason::UnpinnedMut(efn);
66                                    insist_extern_types_are_trivial(ident, reason);
67                                }
68                            }
69                        }
70                        _ => {}
71                    }
72                }
73                if let Some(ret) = &efn.ret {
74                    match ret {
75                        Type::Ident(ident) => {
76                            let reason = TrivialReason::FunctionReturn(efn);
77                            insist_extern_types_are_trivial(ident, reason);
78                        }
79                        Type::Ref(ty) => {
80                            if ty.mutable && !ty.pinned {
81                                if let Type::Ident(ident) = &ty.inner {
82                                    let reason = TrivialReason::UnpinnedMut(efn);
83                                    insist_extern_types_are_trivial(ident, reason);
84                                }
85                            }
86                        }
87                        _ => {}
88                    }
89                }
90            }
91            _ => {}
92        }
93    }
94
95    for ty in all {
96        match ty {
97            Type::RustBox(ty) => {
98                if let Type::Ident(ident) = &ty.inner {
99                    let reason = TrivialReason::BoxTarget;
100                    insist_extern_types_are_trivial(ident, reason);
101                }
102            }
103            Type::RustVec(ty) => {
104                if let Type::Ident(ident) = &ty.inner {
105                    let reason = TrivialReason::VecElement;
106                    insist_extern_types_are_trivial(ident, reason);
107                }
108            }
109            Type::SliceRef(ty) => {
110                if let Type::Ident(ident) = &ty.inner {
111                    let reason = TrivialReason::SliceElement {
112                        mutable: ty.mutable,
113                    };
114                    insist_extern_types_are_trivial(ident, reason);
115                }
116            }
117            _ => {}
118        }
119    }
120
121    required_trivial
122}
123
124// Context:
125// "type {type} should be trivially move constructible and trivially destructible in C++ to be used as {what} in Rust"
126// "needs a cxx::ExternType impl in order to be used as {what}"
127pub(crate) fn as_what<'a>(name: &'a Pair, reasons: &'a [TrivialReason]) -> impl Display + 'a {
128    struct Description<'a> {
129        name: &'a Pair,
130        reasons: &'a [TrivialReason<'a>],
131    }
132
133    impl<'a> Display for Description<'a> {
134        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
135            let mut field_of = Set::new();
136            let mut argument_of = Set::new();
137            let mut return_of = Set::new();
138            let mut box_target = false;
139            let mut vec_element = false;
140            let mut slice_shared_element = false;
141            let mut slice_mut_element = false;
142            let mut unpinned_mut = Set::new();
143
144            for reason in self.reasons {
145                match reason {
146                    TrivialReason::StructField(strct) => {
147                        field_of.insert(&strct.name.rust);
148                    }
149                    TrivialReason::FunctionArgument(efn) => {
150                        argument_of.insert(&efn.name.rust);
151                    }
152                    TrivialReason::FunctionReturn(efn) => {
153                        return_of.insert(&efn.name.rust);
154                    }
155                    TrivialReason::BoxTarget => box_target = true,
156                    TrivialReason::VecElement => vec_element = true,
157                    TrivialReason::SliceElement { mutable } => {
158                        if *mutable {
159                            slice_mut_element = true;
160                        } else {
161                            slice_shared_element = true;
162                        }
163                    }
164                    TrivialReason::UnpinnedMut(efn) => {
165                        unpinned_mut.insert(&efn.name.rust);
166                    }
167                }
168            }
169
170            let mut clauses = Vec::new();
171            if !field_of.is_empty() {
172                clauses.push(Clause::Set {
173                    article: "a",
174                    desc: "field of",
175                    set: &field_of,
176                });
177            }
178            if !argument_of.is_empty() {
179                clauses.push(Clause::Set {
180                    article: "an",
181                    desc: "argument of",
182                    set: &argument_of,
183                });
184            }
185            if !return_of.is_empty() {
186                clauses.push(Clause::Set {
187                    article: "a",
188                    desc: "return value of",
189                    set: &return_of,
190                });
191            }
192            if box_target {
193                clauses.push(Clause::Ty1 {
194                    article: "type",
195                    desc: "Box",
196                    param: self.name,
197                });
198            }
199            if vec_element {
200                clauses.push(Clause::Ty1 {
201                    article: "a",
202                    desc: "vector element in Vec",
203                    param: self.name,
204                });
205            }
206            if slice_shared_element || slice_mut_element {
207                clauses.push(Clause::Slice {
208                    article: "a",
209                    desc: "slice element in",
210                    shared: slice_shared_element,
211                    mutable: slice_mut_element,
212                    param: self.name,
213                });
214            }
215            if !unpinned_mut.is_empty() {
216                clauses.push(Clause::Set {
217                    article: "a",
218                    desc: "non-pinned mutable reference in signature of",
219                    set: &unpinned_mut,
220                });
221            }
222
223            for (i, clause) in clauses.iter().enumerate() {
224                if i == 0 {
225                    write!(f, "{} ", clause.article())?;
226                } else if i + 1 < clauses.len() {
227                    write!(f, ", ")?;
228                } else {
229                    write!(f, " or ")?;
230                }
231                clause.fmt(f)?;
232            }
233
234            Ok(())
235        }
236    }
237
238    enum Clause<'a> {
239        Set {
240            article: &'a str,
241            desc: &'a str,
242            set: &'a Set<&'a Ident>,
243        },
244        Ty1 {
245            article: &'a str,
246            desc: &'a str,
247            param: &'a Pair,
248        },
249        Slice {
250            article: &'a str,
251            desc: &'a str,
252            shared: bool,
253            mutable: bool,
254            param: &'a Pair,
255        },
256    }
257
258    impl<'a> Clause<'a> {
259        fn article(&self) -> &'a str {
260            match self {
261                Clause::Set { article, .. }
262                | Clause::Ty1 { article, .. }
263                | Clause::Slice { article, .. } => article,
264            }
265        }
266
267        fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
268            match self {
269                Clause::Set {
270                    article: _,
271                    desc,
272                    set,
273                } => {
274                    write!(f, "{} ", desc)?;
275                    for (i, ident) in set.iter().take(3).enumerate() {
276                        if i > 0 {
277                            write!(f, ", ")?;
278                        }
279                        write!(f, "`{}`", ident)?;
280                    }
281                    Ok(())
282                }
283                Clause::Ty1 {
284                    article: _,
285                    desc,
286                    param,
287                } => write!(f, "{}<{}>", desc, param.rust),
288                Clause::Slice {
289                    article: _,
290                    desc,
291                    shared,
292                    mutable,
293                    param,
294                } => {
295                    write!(f, "{} ", desc)?;
296                    if *shared {
297                        write!(f, "&[{}]", param.rust)?;
298                    }
299                    if *shared && *mutable {
300                        write!(f, " and ")?;
301                    }
302                    if *mutable {
303                        write!(f, "&mut [{}]", param.rust)?;
304                    }
305                    Ok(())
306                }
307            }
308        }
309    }
310
311    Description { name, reasons }
312}