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::report::Errors;
7use crate::syntax::resolve::Resolution;
8use crate::syntax::set::UnorderedSet;
9use crate::syntax::trivial::{self, TrivialReason};
10use crate::syntax::unpin::{self, UnpinReason};
11use crate::syntax::visit::{self, Visit};
12use crate::syntax::{
13 toposort, Api, Atom, Enum, ExternType, Impl, Lifetimes, Pair, Struct, Type, TypeAlias,
14};
15use indexmap::map::Entry;
16use proc_macro2::Ident;
17use quote::ToTokens;
18
19pub(crate) struct Types<'a> {
20 pub all: OrderedMap<&'a Type, ComputedCfg<'a>>,
21 pub structs: UnorderedMap<&'a Ident, &'a Struct>,
22 pub enums: UnorderedMap<&'a Ident, &'a Enum>,
23 pub cxx: UnorderedSet<&'a Ident>,
24 pub rust: UnorderedSet<&'a Ident>,
25 pub aliases: UnorderedMap<&'a Ident, &'a TypeAlias>,
26 pub untrusted: UnorderedMap<&'a Ident, &'a ExternType>,
27 pub required_trivial: UnorderedMap<&'a Ident, Vec<TrivialReason<'a>>>,
28 #[allow(dead_code)] pub required_unpin: UnorderedMap<&'a Ident, UnpinReason<'a>>,
30 pub impls: OrderedMap<ImplKey<'a>, ConditionalImpl<'a>>,
31 pub resolutions: UnorderedMap<&'a Ident, Resolution<'a>>,
32 pub struct_improper_ctypes: UnorderedSet<&'a Ident>,
33 pub toposorted_structs: Vec<&'a Struct>,
34}
35
36pub(crate) struct ConditionalImpl<'a> {
37 pub cfg: ComputedCfg<'a>,
38 #[allow(dead_code)] pub explicit_impl: Option<&'a Impl>,
42}
43
44impl<'a> Types<'a> {
45 pub(crate) fn collect(cx: &mut Errors, apis: &'a [Api]) -> Self {
46 let mut all = OrderedMap::new();
47 let mut structs = UnorderedMap::new();
48 let mut enums = UnorderedMap::new();
49 let mut cxx = UnorderedSet::new();
50 let mut rust = UnorderedSet::new();
51 let mut aliases = UnorderedMap::new();
52 let mut untrusted = UnorderedMap::new();
53 let mut impls = OrderedMap::new();
54 let mut resolutions = UnorderedMap::new();
55 let struct_improper_ctypes = UnorderedSet::new();
56 let toposorted_structs = Vec::new();
57
58 fn visit<'a>(
59 all: &mut OrderedMap<&'a Type, ComputedCfg<'a>>,
60 ty: &'a Type,
61 cfg: impl Into<ComputedCfg<'a>>,
62 ) {
63 struct CollectTypes<'s, 'a> {
64 all: &'s mut OrderedMap<&'a Type, ComputedCfg<'a>>,
65 cfg: ComputedCfg<'a>,
66 }
67
68 impl<'s, 'a> Visit<'a> for CollectTypes<'s, 'a> {
69 fn visit_type(&mut self, ty: &'a Type) {
70 match self.all.entry(ty) {
71 Entry::Vacant(entry) => {
72 entry.insert(self.cfg.clone());
73 }
74 Entry::Occupied(mut entry) => entry.get_mut().merge_or(self.cfg.clone()),
75 }
76 visit::visit_type(self, ty);
77 }
78 }
79
80 let mut visitor = CollectTypes {
81 all,
82 cfg: cfg.into(),
83 };
84 visitor.visit_type(ty);
85 }
86
87 let mut add_resolution =
88 |name: &'a Pair, attrs: &'a OtherAttrs, generics: &'a Lifetimes| {
89 resolutions.insert(
90 &name.rust,
91 Resolution {
92 name,
93 attrs,
94 generics,
95 },
96 );
97 };
98
99 let mut type_names = UnorderedSet::new();
100 let mut function_names = UnorderedSet::new();
101 for api in apis {
102 match api {
109 Api::Include(_) => {}
110 Api::Struct(strct) => {
111 let ident = &strct.name.rust;
112 if !type_names.insert(ident)
113 && (!cxx.contains(ident)
114 || structs.contains_key(ident)
115 || enums.contains_key(ident))
116 {
117 duplicate_name(cx, strct, ItemName::Type(ident));
121 }
122 structs.insert(&strct.name.rust, strct);
123 for field in &strct.fields {
124 let cfg = ComputedCfg::all(&strct.cfg, &field.cfg);
125 visit(&mut all, &field.ty, cfg);
126 }
127 add_resolution(&strct.name, &strct.attrs, &strct.generics);
128 }
129 Api::Enum(enm) => {
130 match all.entry(&enm.repr.repr_type) {
131 Entry::Vacant(entry) => {
132 entry.insert(ComputedCfg::Leaf(&enm.cfg));
133 }
134 Entry::Occupied(mut entry) => entry.get_mut().merge_or(&enm.cfg),
135 }
136 let ident = &enm.name.rust;
137 if !type_names.insert(ident)
138 && (!cxx.contains(ident)
139 || structs.contains_key(ident)
140 || enums.contains_key(ident))
141 {
142 duplicate_name(cx, enm, ItemName::Type(ident));
146 }
147 enums.insert(ident, enm);
148 add_resolution(&enm.name, &enm.attrs, &enm.generics);
149 }
150 Api::CxxType(ety) => {
151 let ident = &ety.name.rust;
152 if !type_names.insert(ident)
153 && (cxx.contains(ident)
154 || !structs.contains_key(ident) && !enums.contains_key(ident))
155 {
156 duplicate_name(cx, ety, ItemName::Type(ident));
160 }
161 cxx.insert(ident);
162 if !ety.trusted {
163 untrusted.insert(ident, ety);
164 }
165 add_resolution(&ety.name, &ety.attrs, &ety.generics);
166 }
167 Api::RustType(ety) => {
168 let ident = &ety.name.rust;
169 if !type_names.insert(ident) {
170 duplicate_name(cx, ety, ItemName::Type(ident));
171 }
172 rust.insert(ident);
173 add_resolution(&ety.name, &ety.attrs, &ety.generics);
174 }
175 Api::CxxFunction(efn) | Api::RustFunction(efn) => {
176 let self_type = efn.self_type();
179 if !self_type.is_some_and(|self_type| self_type == "Self")
180 && !function_names.insert((self_type, &efn.name.rust))
181 {
182 duplicate_name(cx, efn, ItemName::Function(self_type, &efn.name.rust));
183 }
184 for arg in &efn.args {
185 visit(&mut all, &arg.ty, &efn.cfg);
186 }
187 if let Some(ret) = &efn.ret {
188 visit(&mut all, ret, &efn.cfg);
189 }
190 }
191 Api::TypeAlias(alias) => {
192 let ident = &alias.name.rust;
193 if !type_names.insert(ident) {
194 duplicate_name(cx, alias, ItemName::Type(ident));
195 }
196 cxx.insert(ident);
197 aliases.insert(ident, alias);
198 add_resolution(&alias.name, &alias.attrs, &alias.generics);
199 }
200 Api::Impl(imp) => {
201 visit(&mut all, &imp.ty, &imp.cfg);
202 if let Some(key) = imp.ty.impl_key() {
203 impls.insert(key, ConditionalImpl::from(imp));
204 }
205 }
206 }
207 }
208
209 for (ty, cfg) in &all {
210 let Some(impl_key) = ty.impl_key() else {
211 continue;
212 };
213 let implicit_impl = match &impl_key {
214 ImplKey::RustBox(ident)
215 | ImplKey::RustVec(ident)
216 | ImplKey::UniquePtr(ident)
217 | ImplKey::SharedPtr(ident)
218 | ImplKey::WeakPtr(ident)
219 | ImplKey::CxxVector(ident) => {
220 Atom::from(ident.rust).is_none() && !aliases.contains_key(ident.rust)
221 }
222 };
223 if implicit_impl {
224 match impls.entry(impl_key) {
225 Entry::Vacant(entry) => {
226 entry.insert(ConditionalImpl::from(cfg.clone()));
227 }
228 Entry::Occupied(mut entry) => entry.get_mut().cfg.merge_or(cfg.clone()),
229 }
230 }
231 }
232
233 let required_trivial =
238 trivial::required_trivial_reasons(apis, &all, &structs, &enums, &cxx, &aliases, &impls);
239
240 let required_unpin =
241 unpin::required_unpin_reasons(apis, &all, &structs, &enums, &cxx, &aliases);
242
243 let mut types = Types {
244 all,
245 structs,
246 enums,
247 cxx,
248 rust,
249 aliases,
250 untrusted,
251 required_trivial,
252 required_unpin,
253 impls,
254 resolutions,
255 struct_improper_ctypes,
256 toposorted_structs,
257 };
258
259 types.toposorted_structs = toposort::sort(cx, apis, &types);
260
261 let mut unresolved_structs = types.structs.keys();
262 let mut new_information = true;
263 while new_information {
264 new_information = false;
265 unresolved_structs.retain(|ident| {
266 let mut retain = false;
267 for var in &types.structs[ident].fields {
268 if match types.determine_improper_ctype(&var.ty) {
269 ImproperCtype::Depends(inner) => {
270 retain = true;
271 types.struct_improper_ctypes.contains(inner)
272 }
273 ImproperCtype::Definite(improper) => improper,
274 } {
275 types.struct_improper_ctypes.insert(ident);
276 new_information = true;
277 return false;
278 }
279 }
280 retain
282 });
283 }
284
285 types
286 }
287
288 pub(crate) fn needs_indirect_abi(&self, ty: &Type) -> bool {
289 match ty {
290 Type::RustBox(_)
291 | Type::UniquePtr(_)
292 | Type::Ref(_)
293 | Type::Ptr(_)
294 | Type::Str(_)
295 | Type::Fn(_)
296 | Type::SliceRef(_) => false,
297 Type::Array(_) => true,
298 _ => !self.is_guaranteed_pod(ty) || self.is_considered_improper_ctype(ty),
299 }
300 }
301
302 #[allow(dead_code)] pub(crate) fn is_considered_improper_ctype(&self, ty: &Type) -> bool {
310 match self.determine_improper_ctype(ty) {
311 ImproperCtype::Definite(improper) => improper,
312 ImproperCtype::Depends(ident) => self.struct_improper_ctypes.contains(ident),
313 }
314 }
315
316 pub(crate) fn is_maybe_trivial(&self, ty: &Ident) -> bool {
319 self.structs.contains_key(ty)
320 || self.enums.contains_key(ty)
321 || self.aliases.contains_key(ty)
322 }
323}
324
325impl<'t, 'a> IntoIterator for &'t Types<'a> {
326 type Item = &'a Type;
327 type IntoIter = std::iter::Copied<indexmap::map::Keys<'t, &'a Type, ComputedCfg<'a>>>;
328 fn into_iter(self) -> Self::IntoIter {
329 self.all.keys().copied()
330 }
331}
332
333impl<'a> From<ComputedCfg<'a>> for ConditionalImpl<'a> {
334 fn from(cfg: ComputedCfg<'a>) -> Self {
335 ConditionalImpl {
336 cfg,
337 explicit_impl: None,
338 }
339 }
340}
341
342impl<'a> From<&'a Impl> for ConditionalImpl<'a> {
343 fn from(imp: &'a Impl) -> Self {
344 ConditionalImpl {
345 cfg: ComputedCfg::Leaf(&imp.cfg),
346 explicit_impl: Some(imp),
347 }
348 }
349}
350
351enum ItemName<'a> {
352 Type(&'a Ident),
353 Function(Option<&'a Ident>, &'a Ident),
354}
355
356fn duplicate_name(cx: &mut Errors, sp: impl ToTokens, name: ItemName) {
357 let description = match name {
358 ItemName::Type(name) => format!("type `{}`", name),
359 ItemName::Function(Some(self_type), name) => {
360 format!("associated function `{}::{}`", self_type, name)
361 }
362 ItemName::Function(None, name) => format!("function `{}`", name),
363 };
364 let msg = format!("the {} is defined multiple times", description);
365 cx.error(sp, msg);
366}