cxx_build/syntax/
types.rs

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