cxx_gen/syntax/
trivial.rs

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