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, ExternType, Impl, Lifetimes, Pair, Struct, Type, TypeAlias,
11 };
12 use proc_macro2::Ident;
13 use quote::ToTokens;
14
15 pub 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 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 all.insert(&enm.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, 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, 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, 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 if !function_names.insert((&efn.receiver, &efn.name.rust)) {
135 duplicate_name(cx, efn, &efn.name.rust);
136 }
137 for arg in &efn.args {
138 visit(&mut all, &arg.ty);
139 }
140 if let Some(ret) = &efn.ret {
141 visit(&mut all, ret);
142 }
143 }
144 Api::TypeAlias(alias) => {
145 let ident = &alias.name.rust;
146 if !type_names.insert(ident) {
147 duplicate_name(cx, alias, ident);
148 }
149 cxx.insert(ident);
150 aliases.insert(ident, alias);
151 add_resolution(&alias.name, &alias.generics);
152 }
153 Api::Impl(imp) => {
154 visit(&mut all, &imp.ty);
155 if let Some(key) = imp.ty.impl_key() {
156 impls.insert(key, Some(imp));
157 }
158 }
159 }
160 }
161
162 for ty in &all {
163 let impl_key = match ty.impl_key() {
164 Some(impl_key) => impl_key,
165 None => continue,
166 };
167 let implicit_impl = match impl_key {
168 ImplKey::RustBox(ident)
169 | ImplKey::RustVec(ident)
170 | ImplKey::UniquePtr(ident)
171 | ImplKey::SharedPtr(ident)
172 | ImplKey::WeakPtr(ident)
173 | ImplKey::CxxVector(ident) => {
174 Atom::from(ident.rust).is_none() && !aliases.contains_key(ident.rust)
175 }
176 };
177 if implicit_impl && !impls.contains_key(&impl_key) {
178 impls.insert(impl_key, None);
179 }
180 }
181
182 // All these APIs may contain types passed by value. We need to ensure
183 // we check that this is permissible. We do this _after_ scanning all
184 // the APIs above, in case some function or struct references a type
185 // which is declared subsequently.
186 let required_trivial =
187 trivial::required_trivial_reasons(apis, &all, &structs, &enums, &cxx);
188
189 let mut types = Types {
190 all,
191 structs,
192 enums,
193 cxx,
194 rust,
195 aliases,
196 untrusted,
197 required_trivial,
198 impls,
199 resolutions,
200 struct_improper_ctypes,
201 toposorted_structs,
202 };
203
204 types.toposorted_structs = toposort::sort(cx, apis, &types);
205
206 let mut unresolved_structs = types.structs.keys();
207 let mut new_information = true;
208 while new_information {
209 new_information = false;
210 unresolved_structs.retain(|ident| {
211 let mut retain = false;
212 for var in &types.structs[ident].fields {
213 if match types.determine_improper_ctype(&var.ty) {
214 ImproperCtype::Depends(inner) => {
215 retain = true;
216 types.struct_improper_ctypes.contains(inner)
217 }
218 ImproperCtype::Definite(improper) => improper,
219 } {
220 types.struct_improper_ctypes.insert(ident);
221 new_information = true;
222 return false;
223 }
224 }
225 // If all fields definite false, remove from unresolved_structs.
226 retain
227 });
228 }
229
230 types
231 }
232
needs_indirect_abi(&self, ty: &Type) -> bool233 pub fn needs_indirect_abi(&self, ty: &Type) -> bool {
234 match ty {
235 Type::RustBox(_) | Type::UniquePtr(_) => false,
236 Type::Array(_) => true,
237 _ => !self.is_guaranteed_pod(ty),
238 }
239 }
240
241 // Types that trigger rustc's default #[warn(improper_ctypes)] lint, even if
242 // they may be otherwise unproblematic to mention in an extern signature.
243 // For example in a signature like `extern "C" fn(*const String)`, rustc
244 // refuses to believe that C could know how to supply us with a pointer to a
245 // Rust String, even though C could easily have obtained that pointer
246 // legitimately from a Rust call.
is_considered_improper_ctype(&self, ty: &Type) -> bool247 pub fn is_considered_improper_ctype(&self, ty: &Type) -> bool {
248 match self.determine_improper_ctype(ty) {
249 ImproperCtype::Definite(improper) => improper,
250 ImproperCtype::Depends(ident) => self.struct_improper_ctypes.contains(ident),
251 }
252 }
253 }
254
255 impl<'t, 'a> IntoIterator for &'t Types<'a> {
256 type Item = &'a Type;
257 type IntoIter = crate::syntax::set::Iter<'t, 'a, Type>;
into_iter(self) -> Self::IntoIter258 fn into_iter(self) -> Self::IntoIter {
259 self.all.into_iter()
260 }
261 }
262
duplicate_name(cx: &mut Errors, sp: impl ToTokens, ident: &Ident)263 fn duplicate_name(cx: &mut Errors, sp: impl ToTokens, ident: &Ident) {
264 let msg = format!("the name `{}` is defined multiple times", ident);
265 cx.error(sp, msg);
266 }
267