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