cxx_build/syntax/
types.rs

1use crate::syntax::attrs::OtherAttrs;
2use crate::syntax::cfg::ComputedCfg;
3use crate::syntax::improper::ImproperCtype;
4use crate::syntax::instantiate::ImplKey;
5use crate::syntax::map::{OrderedMap, UnorderedMap};
6use crate::syntax::report::Errors;
7use crate::syntax::resolve::Resolution;
8use crate::syntax::set::UnorderedSet;
9use crate::syntax::trivial::{self, TrivialReason};
10use crate::syntax::unpin::{self, UnpinReason};
11use crate::syntax::visit::{self, Visit};
12use crate::syntax::{
13    toposort, Api, Atom, Enum, ExternType, Impl, Lifetimes, Pair, Struct, Type, TypeAlias,
14};
15use indexmap::map::Entry;
16use proc_macro2::Ident;
17use quote::ToTokens;
18
19pub(crate) struct Types<'a> {
20    pub all: OrderedMap<&'a Type, ComputedCfg<'a>>,
21    pub structs: UnorderedMap<&'a Ident, &'a Struct>,
22    pub enums: UnorderedMap<&'a Ident, &'a Enum>,
23    pub cxx: UnorderedSet<&'a Ident>,
24    pub rust: UnorderedSet<&'a Ident>,
25    pub aliases: UnorderedMap<&'a Ident, &'a TypeAlias>,
26    pub untrusted: UnorderedMap<&'a Ident, &'a ExternType>,
27    pub required_trivial: UnorderedMap<&'a Ident, Vec<TrivialReason<'a>>>,
28    #[allow(dead_code)] // only used by cxxbridge-macro, not cxx-build
29    pub required_unpin: UnorderedMap<&'a Ident, UnpinReason<'a>>,
30    pub impls: OrderedMap<ImplKey<'a>, ConditionalImpl<'a>>,
31    pub resolutions: UnorderedMap<&'a Ident, Resolution<'a>>,
32    pub struct_improper_ctypes: UnorderedSet<&'a Ident>,
33    pub toposorted_structs: Vec<&'a Struct>,
34}
35
36pub(crate) struct ConditionalImpl<'a> {
37    pub cfg: ComputedCfg<'a>,
38    // None for implicit impls, which arise from using a generic type
39    // instantiation in a struct field or function signature.
40    #[allow(dead_code)] // only used by cxxbridge-macro, not cxx-build
41    pub explicit_impl: Option<&'a Impl>,
42}
43
44impl<'a> Types<'a> {
45    pub(crate) fn collect(cx: &mut Errors, apis: &'a [Api]) -> Self {
46        let mut all = OrderedMap::new();
47        let mut structs = UnorderedMap::new();
48        let mut enums = UnorderedMap::new();
49        let mut cxx = UnorderedSet::new();
50        let mut rust = UnorderedSet::new();
51        let mut aliases = UnorderedMap::new();
52        let mut untrusted = UnorderedMap::new();
53        let mut impls = OrderedMap::new();
54        let mut resolutions = UnorderedMap::new();
55        let struct_improper_ctypes = UnorderedSet::new();
56        let toposorted_structs = Vec::new();
57
58        fn visit<'a>(
59            all: &mut OrderedMap<&'a Type, ComputedCfg<'a>>,
60            ty: &'a Type,
61            cfg: impl Into<ComputedCfg<'a>>,
62        ) {
63            struct CollectTypes<'s, 'a> {
64                all: &'s mut OrderedMap<&'a Type, ComputedCfg<'a>>,
65                cfg: ComputedCfg<'a>,
66            }
67
68            impl<'s, 'a> Visit<'a> for CollectTypes<'s, 'a> {
69                fn visit_type(&mut self, ty: &'a Type) {
70                    match self.all.entry(ty) {
71                        Entry::Vacant(entry) => {
72                            entry.insert(self.cfg.clone());
73                        }
74                        Entry::Occupied(mut entry) => entry.get_mut().merge_or(self.cfg.clone()),
75                    }
76                    visit::visit_type(self, ty);
77                }
78            }
79
80            let mut visitor = CollectTypes {
81                all,
82                cfg: cfg.into(),
83            };
84            visitor.visit_type(ty);
85        }
86
87        let mut add_resolution =
88            |name: &'a Pair, attrs: &'a OtherAttrs, generics: &'a Lifetimes| {
89                resolutions.insert(
90                    &name.rust,
91                    Resolution {
92                        name,
93                        attrs,
94                        generics,
95                    },
96                );
97            };
98
99        let mut type_names = UnorderedSet::new();
100        let mut function_names = UnorderedSet::new();
101        for api in apis {
102            // The same identifier is permitted to be declared as both a shared
103            // enum and extern C++ type, or shared struct and extern C++ type.
104            // That indicates to not emit the C++ enum/struct definition because
105            // it's defined by the included headers already.
106            //
107            // All other cases of duplicate identifiers are reported as an error.
108            match api {
109                Api::Include(_) => {}
110                Api::Struct(strct) => {
111                    let ident = &strct.name.rust;
112                    if !type_names.insert(ident)
113                        && (!cxx.contains(ident)
114                            || structs.contains_key(ident)
115                            || enums.contains_key(ident))
116                    {
117                        // If already declared as a struct or enum, or if
118                        // colliding with something other than an extern C++
119                        // type, then error.
120                        duplicate_name(cx, strct, ItemName::Type(ident));
121                    }
122                    structs.insert(&strct.name.rust, strct);
123                    for field in &strct.fields {
124                        let cfg = ComputedCfg::all(&strct.cfg, &field.cfg);
125                        visit(&mut all, &field.ty, cfg);
126                    }
127                    add_resolution(&strct.name, &strct.attrs, &strct.generics);
128                }
129                Api::Enum(enm) => {
130                    match all.entry(&enm.repr.repr_type) {
131                        Entry::Vacant(entry) => {
132                            entry.insert(ComputedCfg::Leaf(&enm.cfg));
133                        }
134                        Entry::Occupied(mut entry) => entry.get_mut().merge_or(&enm.cfg),
135                    }
136                    let ident = &enm.name.rust;
137                    if !type_names.insert(ident)
138                        && (!cxx.contains(ident)
139                            || structs.contains_key(ident)
140                            || enums.contains_key(ident))
141                    {
142                        // If already declared as a struct or enum, or if
143                        // colliding with something other than an extern C++
144                        // type, then error.
145                        duplicate_name(cx, enm, ItemName::Type(ident));
146                    }
147                    enums.insert(ident, enm);
148                    add_resolution(&enm.name, &enm.attrs, &enm.generics);
149                }
150                Api::CxxType(ety) => {
151                    let ident = &ety.name.rust;
152                    if !type_names.insert(ident)
153                        && (cxx.contains(ident)
154                            || !structs.contains_key(ident) && !enums.contains_key(ident))
155                    {
156                        // If already declared as an extern C++ type, or if
157                        // colliding with something which is neither struct nor
158                        // enum, then error.
159                        duplicate_name(cx, ety, ItemName::Type(ident));
160                    }
161                    cxx.insert(ident);
162                    if !ety.trusted {
163                        untrusted.insert(ident, ety);
164                    }
165                    add_resolution(&ety.name, &ety.attrs, &ety.generics);
166                }
167                Api::RustType(ety) => {
168                    let ident = &ety.name.rust;
169                    if !type_names.insert(ident) {
170                        duplicate_name(cx, ety, ItemName::Type(ident));
171                    }
172                    rust.insert(ident);
173                    add_resolution(&ety.name, &ety.attrs, &ety.generics);
174                }
175                Api::CxxFunction(efn) | Api::RustFunction(efn) => {
176                    // Note: duplication of the C++ name is fine because C++ has
177                    // function overloading.
178                    let self_type = efn.self_type();
179                    if !self_type.is_some_and(|self_type| self_type == "Self")
180                        && !function_names.insert((self_type, &efn.name.rust))
181                    {
182                        duplicate_name(cx, efn, ItemName::Function(self_type, &efn.name.rust));
183                    }
184                    for arg in &efn.args {
185                        visit(&mut all, &arg.ty, &efn.cfg);
186                    }
187                    if let Some(ret) = &efn.ret {
188                        visit(&mut all, ret, &efn.cfg);
189                    }
190                }
191                Api::TypeAlias(alias) => {
192                    let ident = &alias.name.rust;
193                    if !type_names.insert(ident) {
194                        duplicate_name(cx, alias, ItemName::Type(ident));
195                    }
196                    cxx.insert(ident);
197                    aliases.insert(ident, alias);
198                    add_resolution(&alias.name, &alias.attrs, &alias.generics);
199                }
200                Api::Impl(imp) => {
201                    visit(&mut all, &imp.ty, &imp.cfg);
202                    if let Some(key) = imp.ty.impl_key() {
203                        impls.insert(key, ConditionalImpl::from(imp));
204                    }
205                }
206            }
207        }
208
209        for (ty, cfg) in &all {
210            let Some(impl_key) = ty.impl_key() else {
211                continue;
212            };
213            let implicit_impl = match &impl_key {
214                ImplKey::RustBox(ident)
215                | ImplKey::RustVec(ident)
216                | ImplKey::UniquePtr(ident)
217                | ImplKey::SharedPtr(ident)
218                | ImplKey::WeakPtr(ident)
219                | ImplKey::CxxVector(ident) => {
220                    Atom::from(ident.rust).is_none() && !aliases.contains_key(ident.rust)
221                }
222            };
223            if implicit_impl {
224                match impls.entry(impl_key) {
225                    Entry::Vacant(entry) => {
226                        entry.insert(ConditionalImpl::from(cfg.clone()));
227                    }
228                    Entry::Occupied(mut entry) => entry.get_mut().cfg.merge_or(cfg.clone()),
229                }
230            }
231        }
232
233        // All these APIs may contain types passed by value. We need to ensure
234        // we check that this is permissible. We do this _after_ scanning all
235        // the APIs above, in case some function or struct references a type
236        // which is declared subsequently.
237        let required_trivial =
238            trivial::required_trivial_reasons(apis, &all, &structs, &enums, &cxx, &aliases, &impls);
239
240        let required_unpin =
241            unpin::required_unpin_reasons(apis, &all, &structs, &enums, &cxx, &aliases);
242
243        let mut types = Types {
244            all,
245            structs,
246            enums,
247            cxx,
248            rust,
249            aliases,
250            untrusted,
251            required_trivial,
252            required_unpin,
253            impls,
254            resolutions,
255            struct_improper_ctypes,
256            toposorted_structs,
257        };
258
259        types.toposorted_structs = toposort::sort(cx, apis, &types);
260
261        let mut unresolved_structs = types.structs.keys();
262        let mut new_information = true;
263        while new_information {
264            new_information = false;
265            unresolved_structs.retain(|ident| {
266                let mut retain = false;
267                for var in &types.structs[ident].fields {
268                    if match types.determine_improper_ctype(&var.ty) {
269                        ImproperCtype::Depends(inner) => {
270                            retain = true;
271                            types.struct_improper_ctypes.contains(inner)
272                        }
273                        ImproperCtype::Definite(improper) => improper,
274                    } {
275                        types.struct_improper_ctypes.insert(ident);
276                        new_information = true;
277                        return false;
278                    }
279                }
280                // If all fields definite false, remove from unresolved_structs.
281                retain
282            });
283        }
284
285        types
286    }
287
288    pub(crate) fn needs_indirect_abi(&self, ty: &Type) -> bool {
289        match ty {
290            Type::RustBox(_)
291            | Type::UniquePtr(_)
292            | Type::Ref(_)
293            | Type::Ptr(_)
294            | Type::Str(_)
295            | Type::Fn(_)
296            | Type::SliceRef(_) => false,
297            Type::Array(_) => true,
298            _ => !self.is_guaranteed_pod(ty) || self.is_considered_improper_ctype(ty),
299        }
300    }
301
302    // Types that trigger rustc's default #[warn(improper_ctypes)] lint, even if
303    // they may be otherwise unproblematic to mention in an extern signature.
304    // For example in a signature like `extern "C" fn(*const String)`, rustc
305    // refuses to believe that C could know how to supply us with a pointer to a
306    // Rust String, even though C could easily have obtained that pointer
307    // legitimately from a Rust call.
308    #[allow(dead_code)] // only used by cxxbridge-macro, not cxx-build
309    pub(crate) fn is_considered_improper_ctype(&self, ty: &Type) -> bool {
310        match self.determine_improper_ctype(ty) {
311            ImproperCtype::Definite(improper) => improper,
312            ImproperCtype::Depends(ident) => self.struct_improper_ctypes.contains(ident),
313        }
314    }
315
316    // Types which we need to assume could possibly exist by value on the Rust
317    // side.
318    pub(crate) fn is_maybe_trivial(&self, ty: &Ident) -> bool {
319        self.structs.contains_key(ty)
320            || self.enums.contains_key(ty)
321            || self.aliases.contains_key(ty)
322    }
323}
324
325impl<'t, 'a> IntoIterator for &'t Types<'a> {
326    type Item = &'a Type;
327    type IntoIter = std::iter::Copied<indexmap::map::Keys<'t, &'a Type, ComputedCfg<'a>>>;
328    fn into_iter(self) -> Self::IntoIter {
329        self.all.keys().copied()
330    }
331}
332
333impl<'a> From<ComputedCfg<'a>> for ConditionalImpl<'a> {
334    fn from(cfg: ComputedCfg<'a>) -> Self {
335        ConditionalImpl {
336            cfg,
337            explicit_impl: None,
338        }
339    }
340}
341
342impl<'a> From<&'a Impl> for ConditionalImpl<'a> {
343    fn from(imp: &'a Impl) -> Self {
344        ConditionalImpl {
345            cfg: ComputedCfg::Leaf(&imp.cfg),
346            explicit_impl: Some(imp),
347        }
348    }
349}
350
351enum ItemName<'a> {
352    Type(&'a Ident),
353    Function(Option<&'a Ident>, &'a Ident),
354}
355
356fn duplicate_name(cx: &mut Errors, sp: impl ToTokens, name: ItemName) {
357    let description = match name {
358        ItemName::Type(name) => format!("type `{}`", name),
359        ItemName::Function(Some(self_type), name) => {
360            format!("associated function `{}::{}`", self_type, name)
361        }
362        ItemName::Function(None, name) => format!("function `{}`", name),
363    };
364    let msg = format!("the {} is defined multiple times", description);
365    cx.error(sp, msg);
366}