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, ExternFn, 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 #[cfg_attr(not(proc_macro), expect(dead_code))]
34 pub associated_fn: UnorderedMap<&'a Ident, Vec<&'a ExternFn>>,
35 pub struct_improper_ctypes: UnorderedSet<&'a Ident>,
36 pub toposorted_structs: Vec<&'a Struct>,
37}
38
39pub(crate) struct ConditionalImpl<'a> {
40 pub cfg: ComputedCfg<'a>,
41 #[cfg_attr(not(proc_macro), expect(dead_code))]
44 pub explicit_impl: Option<&'a Impl>,
45}
46
47impl<'a> Types<'a> {
48 pub(crate) fn collect(cx: &mut Errors, apis: &'a [Api]) -> Self {
49 let mut all = OrderedMap::new();
50 let mut structs = UnorderedMap::new();
51 let mut enums = UnorderedMap::new();
52 let mut cxx = UnorderedSet::new();
53 let mut rust = UnorderedSet::new();
54 let mut aliases = UnorderedMap::new();
55 let mut untrusted = UnorderedMap::new();
56 let mut impls = OrderedMap::new();
57 let mut resolutions = UnorderedMap::new();
58 let mut associated_fn = UnorderedMap::new();
59 let struct_improper_ctypes = UnorderedSet::new();
60 let toposorted_structs = Vec::new();
61
62 fn visit<'a>(
63 all: &mut OrderedMap<&'a Type, ComputedCfg<'a>>,
64 ty: &'a Type,
65 cfg: impl Into<ComputedCfg<'a>>,
66 ) {
67 struct CollectTypes<'s, 'a> {
68 all: &'s mut OrderedMap<&'a Type, ComputedCfg<'a>>,
69 cfg: ComputedCfg<'a>,
70 }
71
72 impl<'s, 'a> Visit<'a> for CollectTypes<'s, 'a> {
73 fn visit_type(&mut self, ty: &'a Type) {
74 match self.all.entry(ty) {
75 Entry::Vacant(entry) => {
76 entry.insert(self.cfg.clone());
77 }
78 Entry::Occupied(mut entry) => entry.get_mut().merge_or(self.cfg.clone()),
79 }
80 visit::visit_type(self, ty);
81 }
82 }
83
84 let mut visitor = CollectTypes {
85 all,
86 cfg: cfg.into(),
87 };
88 visitor.visit_type(ty);
89 }
90
91 let mut add_resolution =
92 |name: &'a Pair, attrs: &'a OtherAttrs, generics: &'a Lifetimes| {
93 resolutions.insert(
94 &name.rust,
95 Resolution {
96 name,
97 attrs,
98 generics,
99 },
100 );
101 };
102
103 let mut type_names = UnorderedSet::new();
104 let mut function_names = UnorderedSet::new();
105 for api in apis {
106 match api {
113 Api::Include(_) => {}
114 Api::Struct(strct) => {
115 let ident = &strct.name.rust;
116 if !type_names.insert(ident)
117 && (!cxx.contains(ident)
118 || structs.contains_key(ident)
119 || enums.contains_key(ident))
120 {
121 duplicate_name(cx, strct, ItemName::Type(ident));
125 }
126 structs.insert(&strct.name.rust, strct);
127 for field in &strct.fields {
128 let cfg = ComputedCfg::all(&strct.cfg, &field.cfg);
129 visit(&mut all, &field.ty, cfg);
130 }
131 add_resolution(&strct.name, &strct.attrs, &strct.generics);
132 }
133 Api::Enum(enm) => {
134 match all.entry(&enm.repr.repr_type) {
135 Entry::Vacant(entry) => {
136 entry.insert(ComputedCfg::Leaf(&enm.cfg));
137 }
138 Entry::Occupied(mut entry) => entry.get_mut().merge_or(&enm.cfg),
139 }
140 let ident = &enm.name.rust;
141 if !type_names.insert(ident)
142 && (!cxx.contains(ident)
143 || structs.contains_key(ident)
144 || enums.contains_key(ident))
145 {
146 duplicate_name(cx, enm, ItemName::Type(ident));
150 }
151 enums.insert(ident, enm);
152 add_resolution(&enm.name, &enm.attrs, &enm.generics);
153 }
154 Api::CxxType(ety) => {
155 let ident = &ety.name.rust;
156 if !type_names.insert(ident)
157 && (cxx.contains(ident)
158 || !structs.contains_key(ident) && !enums.contains_key(ident))
159 {
160 duplicate_name(cx, ety, ItemName::Type(ident));
164 }
165 cxx.insert(ident);
166 if !ety.trusted {
167 untrusted.insert(ident, ety);
168 }
169 add_resolution(&ety.name, &ety.attrs, &ety.generics);
170 }
171 Api::RustType(ety) => {
172 let ident = &ety.name.rust;
173 if !type_names.insert(ident) {
174 duplicate_name(cx, ety, ItemName::Type(ident));
175 }
176 rust.insert(ident);
177 add_resolution(&ety.name, &ety.attrs, &ety.generics);
178 }
179 Api::CxxFunction(efn) | Api::RustFunction(efn) => {
180 let self_type = efn.self_type();
183 if let Some(self_type) = self_type {
184 associated_fn
185 .entry(self_type)
186 .or_insert_with(Vec::new)
187 .push(efn);
188 }
189 if !self_type.is_some_and(|self_type| self_type == "Self")
190 && !function_names.insert((self_type, &efn.name.rust))
191 {
192 duplicate_name(cx, efn, ItemName::Function(self_type, &efn.name.rust));
193 }
194 for arg in &efn.args {
195 visit(&mut all, &arg.ty, &efn.cfg);
196 }
197 if let Some(ret) = &efn.ret {
198 visit(&mut all, ret, &efn.cfg);
199 }
200 }
201 Api::TypeAlias(alias) => {
202 let ident = &alias.name.rust;
203 if !type_names.insert(ident) {
204 duplicate_name(cx, alias, ItemName::Type(ident));
205 }
206 cxx.insert(ident);
207 aliases.insert(ident, alias);
208 add_resolution(&alias.name, &alias.attrs, &alias.generics);
209 }
210 Api::Impl(imp) => {
211 visit(&mut all, &imp.ty, &imp.cfg);
212 }
213 }
214 }
215
216 for api in apis {
217 if let Api::Impl(imp) = api {
218 if let Some(key) = imp.ty.impl_key(&resolutions) {
219 impls.insert(key, ConditionalImpl::from(imp));
220 }
221 }
222 }
223
224 let required_trivial = trivial::required_trivial_reasons(
229 apis,
230 &all,
231 &structs,
232 &enums,
233 &cxx,
234 &aliases,
235 &impls,
236 &resolutions,
237 );
238
239 let required_unpin =
240 unpin::required_unpin_reasons(apis, &all, &structs, &enums, &cxx, &aliases);
241
242 let mut types = Types {
243 all,
244 structs,
245 enums,
246 cxx,
247 rust,
248 aliases,
249 untrusted,
250 required_trivial,
251 required_unpin,
252 impls,
253 resolutions,
254 associated_fn,
255 struct_improper_ctypes,
256 toposorted_structs,
257 };
258
259 types.toposorted_structs = toposort::sort(cx, apis, &types);
260
261 for (ty, cfg) in &types.all {
262 let Some(impl_key) = ty.impl_key(&types.resolutions) else {
263 continue;
264 };
265 if impl_key.is_implicit_impl_ok(&types) {
266 match types.impls.entry(impl_key) {
267 Entry::Vacant(entry) => {
268 entry.insert(ConditionalImpl::from(cfg.clone()));
269 }
270 Entry::Occupied(mut entry) => entry.get_mut().cfg.merge_or(cfg.clone()),
271 }
272 }
273 }
274
275 let mut unresolved_structs = types.structs.keys();
276 let mut new_information = true;
277 while new_information {
278 new_information = false;
279 unresolved_structs.retain(|ident| {
280 let mut retain = false;
281 for var in &types.structs[ident].fields {
282 if match types.determine_improper_ctype(&var.ty) {
283 ImproperCtype::Depends(inner) => {
284 retain = true;
285 types.struct_improper_ctypes.contains(inner)
286 }
287 ImproperCtype::Definite(improper) => improper,
288 } {
289 types.struct_improper_ctypes.insert(ident);
290 new_information = true;
291 return false;
292 }
293 }
294 retain
296 });
297 }
298
299 types
300 }
301
302 pub(crate) fn needs_indirect_abi(&self, ty: impl Into<TypeQuery<'a>>) -> bool {
303 let ty = ty.into();
304 match ty {
305 TypeQuery::RustBox
306 | TypeQuery::UniquePtr
307 | TypeQuery::Ref(_)
308 | TypeQuery::Ptr(_)
309 | TypeQuery::Str
310 | TypeQuery::Fn
311 | TypeQuery::SliceRef => false,
312 TypeQuery::Array(_) => true,
313 _ => !self.is_guaranteed_pod(ty) || self.is_considered_improper_ctype(ty),
314 }
315 }
316
317 pub(crate) fn is_considered_improper_ctype(&self, ty: impl Into<TypeQuery<'a>>) -> bool {
324 match self.determine_improper_ctype(ty) {
325 ImproperCtype::Definite(improper) => improper,
326 ImproperCtype::Depends(ident) => self.struct_improper_ctypes.contains(ident),
327 }
328 }
329
330 pub(crate) fn is_maybe_trivial(&self, ty: &Type) -> bool {
333 match ty {
334 Type::Ident(named_type) => {
335 let ident = &named_type.rust;
336 self.structs.contains_key(ident)
337 || self.enums.contains_key(ident)
338 || self.aliases.contains_key(ident)
339 }
340 Type::CxxVector(_) => false,
341 _ => {
::core::panicking::panic_fmt(format_args!("internal error: entered unreachable code: {0}",
format_args!("syntax/check.rs should reject other types")));
}unreachable!("syntax/check.rs should reject other types"),
342 }
343 }
344
345 pub(crate) fn contains_elided_lifetime(&self, ty: &Type) -> bool {
346 match ty {
347 Type::Ident(ty) => {
348 Atom::from(&ty.rust).is_none()
349 && ty.generics.lifetimes.len()
350 != self.resolve(&ty.rust).generics.lifetimes.len()
351 }
352 Type::RustBox(ty)
353 | Type::RustVec(ty)
354 | Type::UniquePtr(ty)
355 | Type::SharedPtr(ty)
356 | Type::WeakPtr(ty)
357 | Type::CxxVector(ty) => self.contains_elided_lifetime(&ty.inner),
358 Type::Ref(ty) => ty.lifetime.is_none() || self.contains_elided_lifetime(&ty.inner),
359 Type::Ptr(ty) => self.contains_elided_lifetime(&ty.inner),
360 Type::Str(ty) => ty.lifetime.is_none(),
361 Type::SliceRef(ty) => ty.lifetime.is_none() || self.contains_elided_lifetime(&ty.inner),
362 Type::Array(ty) => self.contains_elided_lifetime(&ty.inner),
363 Type::Fn(_) | Type::Void(_) => false,
364 }
365 }
366
367 pub(crate) fn is_local(&self, ty: &Type) -> bool {
370 match ty {
371 Type::Ident(ident) => {
372 Atom::from(&ident.rust).is_none() && !self.aliases.contains_key(&ident.rust)
373 }
374 Type::RustBox(_) => {
375 false
378 }
379 Type::Array(_)
380 | Type::CxxVector(_)
381 | Type::Fn(_)
382 | Type::Void(_)
383 | Type::RustVec(_)
384 | Type::UniquePtr(_)
385 | Type::SharedPtr(_)
386 | Type::WeakPtr(_)
387 | Type::Ref(_)
388 | Type::Ptr(_)
389 | Type::Str(_)
390 | Type::SliceRef(_) => false,
391 }
392 }
393}
394
395impl<'t, 'a> IntoIterator for &'t Types<'a> {
396 type Item = &'a Type;
397 type IntoIter = std::iter::Copied<indexmap::map::Keys<'t, &'a Type, ComputedCfg<'a>>>;
398 fn into_iter(self) -> Self::IntoIter {
399 self.all.keys().copied()
400 }
401}
402
403impl<'a> From<ComputedCfg<'a>> for ConditionalImpl<'a> {
404 fn from(cfg: ComputedCfg<'a>) -> Self {
405 ConditionalImpl {
406 cfg,
407 explicit_impl: None,
408 }
409 }
410}
411
412impl<'a> From<&'a Impl> for ConditionalImpl<'a> {
413 fn from(imp: &'a Impl) -> Self {
414 ConditionalImpl {
415 cfg: ComputedCfg::Leaf(&imp.cfg),
416 explicit_impl: Some(imp),
417 }
418 }
419}
420
421enum ItemName<'a> {
422 Type(&'a Ident),
423 Function(Option<&'a Ident>, &'a Ident),
424}
425
426fn duplicate_name(cx: &mut Errors, sp: impl ToTokens, name: ItemName) {
427 let description = match name {
428 ItemName::Type(name) => ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("type `{0}`", name))
})format!("type `{}`", name),
429 ItemName::Function(Some(self_type), name) => {
430 ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("associated function `{0}::{1}`",
self_type, name))
})format!("associated function `{}::{}`", self_type, name)
431 }
432 ItemName::Function(None, name) => ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("function `{0}`", name))
})format!("function `{}`", name),
433 };
434 let msg = ::alloc::__export::must_use({
::alloc::fmt::format(format_args!("the {0} is defined multiple times",
description))
})format!("the {} is defined multiple times", description);
435 cx.error(sp, msg);
436}