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