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