cxx_build/syntax/
trivial.rs

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