• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 use crate::syntax::improper::ImproperCtype;
2 use crate::syntax::instantiate::ImplKey;
3 use crate::syntax::map::{OrderedMap, UnorderedMap};
4 use crate::syntax::report::Errors;
5 use crate::syntax::resolve::Resolution;
6 use crate::syntax::set::{OrderedSet, UnorderedSet};
7 use crate::syntax::trivial::{self, TrivialReason};
8 use crate::syntax::visit::{self, Visit};
9 use crate::syntax::{
10     toposort, Api, Atom, Enum, EnumRepr, ExternType, Impl, Lifetimes, Pair, Struct, Type, TypeAlias,
11 };
12 use proc_macro2::Ident;
13 use quote::ToTokens;
14 
15 pub(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 
30 impl<'a> Types<'a> {
collect(cx: &mut Errors, apis: &'a [Api]) -> Self31     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, 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                     match &enm.repr {
92                         EnumRepr::Native { atom: _, repr_type } => {
93                             all.insert(repr_type);
94                         }
95                         #[cfg(feature = "experimental-enum-variants-from-header")]
96                         EnumRepr::Foreign { rust_type: _ } => {}
97                     }
98                     let ident = &enm.name.rust;
99                     if !type_names.insert(ident)
100                         && (!cxx.contains(ident)
101                             || structs.contains_key(ident)
102                             || enums.contains_key(ident))
103                     {
104                         // If already declared as a struct or enum, or if
105                         // colliding with something other than an extern C++
106                         // type, then error.
107                         duplicate_name(cx, enm, ident);
108                     }
109                     enums.insert(ident, enm);
110                     if enm.variants_from_header {
111                         // #![variants_from_header] enums are implicitly extern
112                         // C++ type.
113                         cxx.insert(&enm.name.rust);
114                     }
115                     add_resolution(&enm.name, &enm.generics);
116                 }
117                 Api::CxxType(ety) => {
118                     let ident = &ety.name.rust;
119                     if !type_names.insert(ident)
120                         && (cxx.contains(ident)
121                             || !structs.contains_key(ident) && !enums.contains_key(ident))
122                     {
123                         // If already declared as an extern C++ type, or if
124                         // colliding with something which is neither struct nor
125                         // enum, then error.
126                         duplicate_name(cx, ety, ident);
127                     }
128                     cxx.insert(ident);
129                     if !ety.trusted {
130                         untrusted.insert(ident, ety);
131                     }
132                     add_resolution(&ety.name, &ety.generics);
133                 }
134                 Api::RustType(ety) => {
135                     let ident = &ety.name.rust;
136                     if !type_names.insert(ident) {
137                         duplicate_name(cx, ety, ident);
138                     }
139                     rust.insert(ident);
140                     add_resolution(&ety.name, &ety.generics);
141                 }
142                 Api::CxxFunction(efn) | Api::RustFunction(efn) => {
143                     // Note: duplication of the C++ name is fine because C++ has
144                     // function overloading.
145                     if !function_names.insert((&efn.receiver, &efn.name.rust)) {
146                         duplicate_name(cx, efn, &efn.name.rust);
147                     }
148                     for arg in &efn.args {
149                         visit(&mut all, &arg.ty);
150                     }
151                     if let Some(ret) = &efn.ret {
152                         visit(&mut all, ret);
153                     }
154                 }
155                 Api::TypeAlias(alias) => {
156                     let ident = &alias.name.rust;
157                     if !type_names.insert(ident) {
158                         duplicate_name(cx, alias, ident);
159                     }
160                     cxx.insert(ident);
161                     aliases.insert(ident, alias);
162                     add_resolution(&alias.name, &alias.generics);
163                 }
164                 Api::Impl(imp) => {
165                     visit(&mut all, &imp.ty);
166                     if let Some(key) = imp.ty.impl_key() {
167                         impls.insert(key, Some(imp));
168                     }
169                 }
170             }
171         }
172 
173         for ty in &all {
174             let impl_key = match ty.impl_key() {
175                 Some(impl_key) => impl_key,
176                 None => continue,
177             };
178             let implicit_impl = match impl_key {
179                 ImplKey::RustBox(ident)
180                 | ImplKey::RustVec(ident)
181                 | ImplKey::UniquePtr(ident)
182                 | ImplKey::SharedPtr(ident)
183                 | ImplKey::WeakPtr(ident)
184                 | ImplKey::CxxVector(ident) => {
185                     Atom::from(ident.rust).is_none() && !aliases.contains_key(ident.rust)
186                 }
187             };
188             if implicit_impl && !impls.contains_key(&impl_key) {
189                 impls.insert(impl_key, None);
190             }
191         }
192 
193         // All these APIs may contain types passed by value. We need to ensure
194         // we check that this is permissible. We do this _after_ scanning all
195         // the APIs above, in case some function or struct references a type
196         // which is declared subsequently.
197         let required_trivial =
198             trivial::required_trivial_reasons(apis, &all, &structs, &enums, &cxx);
199 
200         let mut types = Types {
201             all,
202             structs,
203             enums,
204             cxx,
205             rust,
206             aliases,
207             untrusted,
208             required_trivial,
209             impls,
210             resolutions,
211             struct_improper_ctypes,
212             toposorted_structs,
213         };
214 
215         types.toposorted_structs = toposort::sort(cx, apis, &types);
216 
217         let mut unresolved_structs = types.structs.keys();
218         let mut new_information = true;
219         while new_information {
220             new_information = false;
221             unresolved_structs.retain(|ident| {
222                 let mut retain = false;
223                 for var in &types.structs[ident].fields {
224                     if match types.determine_improper_ctype(&var.ty) {
225                         ImproperCtype::Depends(inner) => {
226                             retain = true;
227                             types.struct_improper_ctypes.contains(inner)
228                         }
229                         ImproperCtype::Definite(improper) => improper,
230                     } {
231                         types.struct_improper_ctypes.insert(ident);
232                         new_information = true;
233                         return false;
234                     }
235                 }
236                 // If all fields definite false, remove from unresolved_structs.
237                 retain
238             });
239         }
240 
241         types
242     }
243 
needs_indirect_abi(&self, ty: &Type) -> bool244     pub(crate) fn needs_indirect_abi(&self, ty: &Type) -> bool {
245         match ty {
246             Type::RustBox(_) | Type::UniquePtr(_) => false,
247             Type::Array(_) => true,
248             _ => !self.is_guaranteed_pod(ty),
249         }
250     }
251 
252     // Types that trigger rustc's default #[warn(improper_ctypes)] lint, even if
253     // they may be otherwise unproblematic to mention in an extern signature.
254     // For example in a signature like `extern "C" fn(*const String)`, rustc
255     // refuses to believe that C could know how to supply us with a pointer to a
256     // Rust String, even though C could easily have obtained that pointer
257     // legitimately from a Rust call.
258     #[allow(dead_code)] // only used by cxxbridge-macro, not cxx-build
is_considered_improper_ctype(&self, ty: &Type) -> bool259     pub(crate) fn is_considered_improper_ctype(&self, ty: &Type) -> bool {
260         match self.determine_improper_ctype(ty) {
261             ImproperCtype::Definite(improper) => improper,
262             ImproperCtype::Depends(ident) => self.struct_improper_ctypes.contains(ident),
263         }
264     }
265 
266     // Types which we need to assume could possibly exist by value on the Rust
267     // side.
is_maybe_trivial(&self, ty: &Ident) -> bool268     pub(crate) fn is_maybe_trivial(&self, ty: &Ident) -> bool {
269         self.structs.contains_key(ty)
270             || self.enums.contains_key(ty)
271             || self.aliases.contains_key(ty)
272     }
273 }
274 
275 impl<'t, 'a> IntoIterator for &'t Types<'a> {
276     type Item = &'a Type;
277     type IntoIter = crate::syntax::set::Iter<'t, 'a, Type>;
into_iter(self) -> Self::IntoIter278     fn into_iter(self) -> Self::IntoIter {
279         self.all.into_iter()
280     }
281 }
282 
duplicate_name(cx: &mut Errors, sp: impl ToTokens, ident: &Ident)283 fn duplicate_name(cx: &mut Errors, sp: impl ToTokens, ident: &Ident) {
284     let msg = format!("the name `{}` is defined multiple times", ident);
285     cx.error(sp, msg);
286 }
287