blob: eeb505196a940359e521116aa3f1307704ffc90f [file] [log] [blame]
Brian Silverman4e662aa2022-05-11 23:10:19 -07001// Copyright 2020 Google LLC
2//
3// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
4// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
5// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
6// option. This file may not be copied, modified, or distributed
7// except according to those terms.
8
9mod byvalue_checker;
10
11use indexmap::map::IndexMap as HashMap;
12use indexmap::set::IndexSet as HashSet;
13
14use autocxx_parser::IncludeCppConfig;
15use byvalue_checker::ByValueChecker;
16use syn::{ItemEnum, ItemStruct, Type, Visibility};
17
18use crate::{
19 conversion::{
20 analysis::type_converter::{self, add_analysis, TypeConversionContext, TypeConverter},
Brian Silvermanf3ec38b2022-07-06 20:43:36 -070021 api::{AnalysisPhase, Api, ApiName, NullPhase, StructDetails, TypeKind},
Brian Silverman4e662aa2022-05-11 23:10:19 -070022 apivec::ApiVec,
23 convert_error::{ConvertErrorWithContext, ErrorContext},
24 error_reporter::convert_apis,
25 parse::BindgenSemanticAttributes,
26 ConvertError,
27 },
28 types::{Namespace, QualifiedName},
29};
30
31use super::tdef::{TypedefAnalysis, TypedefPhase};
32
33pub(crate) struct FieldInfo {
34 pub(crate) ty: Type,
35 pub(crate) type_kind: type_converter::TypeKind,
36}
37
38pub(crate) struct PodAnalysis {
39 pub(crate) kind: TypeKind,
40 pub(crate) bases: HashSet<QualifiedName>,
41 /// Base classes for which we should create casts.
42 /// That's just those which are on the allowlist,
43 /// because otherwise we don't know whether they're
44 /// abstract or not.
45 pub(crate) castable_bases: HashSet<QualifiedName>,
46 pub(crate) field_deps: HashSet<QualifiedName>,
47 pub(crate) field_info: Vec<FieldInfo>,
48 pub(crate) is_generic: bool,
49 pub(crate) in_anonymous_namespace: bool,
50}
51
52pub(crate) struct PodPhase;
53
54impl AnalysisPhase for PodPhase {
55 type TypedefAnalysis = TypedefAnalysis;
56 type StructAnalysis = PodAnalysis;
57 type FunAnalysis = ();
58}
59
60/// In our set of APIs, work out which ones are safe to represent
61/// by value in Rust (e.g. they don't have a destructor) and record
62/// as such. Return a set of APIs annotated with extra metadata,
63/// and an object which can be used to query the POD status of any
64/// type whether or not it's one of the [Api]s.
65pub(crate) fn analyze_pod_apis(
66 apis: ApiVec<TypedefPhase>,
67 config: &IncludeCppConfig,
68) -> Result<ApiVec<PodPhase>, ConvertError> {
69 // This next line will return an error if any of the 'generate_pod'
70 // directives from the user can't be met because, for instance,
71 // a type contains a std::string or some other type which can't be
72 // held safely by value in Rust.
73 let byvalue_checker = ByValueChecker::new_from_apis(&apis, config)?;
74 let mut extra_apis = ApiVec::new();
75 let mut type_converter = TypeConverter::new(config, &apis);
76 let mut results = ApiVec::new();
77 convert_apis(
78 apis,
79 &mut results,
80 Api::fun_unchanged,
81 |name, details, _| {
82 analyze_struct(
83 &byvalue_checker,
84 &mut type_converter,
85 &mut extra_apis,
86 name,
87 details,
88 config,
89 )
90 },
91 analyze_enum,
92 Api::typedef_unchanged,
93 );
94 // Conceivably, the process of POD-analysing the first set of APIs could result
95 // in us creating new APIs to concretize generic types.
96 let extra_apis: ApiVec<PodPhase> = extra_apis.into_iter().map(add_analysis).collect();
97 let mut more_extra_apis = ApiVec::new();
98 convert_apis(
99 extra_apis,
100 &mut results,
101 Api::fun_unchanged,
102 |name, details, _| {
103 analyze_struct(
104 &byvalue_checker,
105 &mut type_converter,
106 &mut more_extra_apis,
107 name,
108 details,
109 config,
110 )
111 },
112 analyze_enum,
113 Api::typedef_unchanged,
114 );
115 assert!(more_extra_apis.is_empty());
116 Ok(results)
117}
118
119fn analyze_enum(
120 name: ApiName,
121 mut item: ItemEnum,
122) -> Result<Box<dyn Iterator<Item = Api<PodPhase>>>, ConvertErrorWithContext> {
123 let metadata = BindgenSemanticAttributes::new_retaining_others(&mut item.attrs);
124 metadata.check_for_fatal_attrs(&name.name.get_final_ident())?;
125 Ok(Box::new(std::iter::once(Api::Enum { name, item })))
126}
127
128fn analyze_struct(
129 byvalue_checker: &ByValueChecker,
130 type_converter: &mut TypeConverter,
131 extra_apis: &mut ApiVec<NullPhase>,
132 name: ApiName,
133 mut details: Box<StructDetails>,
134 config: &IncludeCppConfig,
135) -> Result<Box<dyn Iterator<Item = Api<PodPhase>>>, ConvertErrorWithContext> {
136 let id = name.name.get_final_ident();
Brian Silverman4e662aa2022-05-11 23:10:19 -0700137 let metadata = BindgenSemanticAttributes::new_retaining_others(&mut details.item.attrs);
138 metadata.check_for_fatal_attrs(&id)?;
139 let bases = get_bases(&details.item);
140 let mut field_deps = HashSet::new();
141 let mut field_info = Vec::new();
142 let field_conversion_errors = get_struct_field_types(
143 type_converter,
144 name.name.get_namespace(),
145 &details.item,
146 &mut field_deps,
147 &mut field_info,
148 extra_apis,
149 );
150 let type_kind = if byvalue_checker.is_pod(&name.name) {
151 // It's POD so any errors encountered parsing its fields are important.
152 // Let's not allow anything to be POD if it's got rvalue reference fields.
153 if details.has_rvalue_reference_fields {
154 return Err(ConvertErrorWithContext(
155 ConvertError::RValueReferenceField,
156 Some(ErrorContext::new_for_item(id)),
157 ));
158 }
159 if let Some(err) = field_conversion_errors.into_iter().next() {
160 return Err(ConvertErrorWithContext(
161 err,
162 Some(ErrorContext::new_for_item(id)),
163 ));
164 }
165 TypeKind::Pod
166 } else {
167 TypeKind::NonPod
168 };
169 let castable_bases = bases
170 .iter()
171 .filter(|(_, is_public)| **is_public)
172 .map(|(base, _)| base)
173 .filter(|base| config.is_on_allowlist(&base.to_cpp_name()))
174 .cloned()
175 .collect();
176 let is_generic = !details.item.generics.params.is_empty();
177 let in_anonymous_namespace = name
178 .name
179 .ns_segment_iter()
180 .any(|ns| ns.starts_with("_bindgen_mod"));
181 Ok(Box::new(std::iter::once(Api::Struct {
182 name,
183 details,
184 analysis: PodAnalysis {
185 kind: type_kind,
186 bases: bases.into_keys().collect(),
187 castable_bases,
188 field_deps,
189 field_info,
190 is_generic,
191 in_anonymous_namespace,
192 },
193 })))
194}
195
196fn get_struct_field_types(
197 type_converter: &mut TypeConverter,
198 ns: &Namespace,
199 s: &ItemStruct,
200 field_deps: &mut HashSet<QualifiedName>,
201 field_info: &mut Vec<FieldInfo>,
202 extra_apis: &mut ApiVec<NullPhase>,
203) -> Vec<ConvertError> {
204 let mut convert_errors = Vec::new();
Brian Silvermanf3ec38b2022-07-06 20:43:36 -0700205 let struct_type_params = s
206 .generics
207 .type_params()
208 .map(|tp| tp.ident.clone())
209 .collect();
210 let type_conversion_context = TypeConversionContext::WithinStructField { struct_type_params };
Brian Silverman4e662aa2022-05-11 23:10:19 -0700211 for f in &s.fields {
Brian Silvermanf3ec38b2022-07-06 20:43:36 -0700212 let annotated = type_converter.convert_type(f.ty.clone(), ns, &type_conversion_context);
Brian Silverman4e662aa2022-05-11 23:10:19 -0700213 match annotated {
214 Ok(mut r) => {
215 extra_apis.append(&mut r.extra_apis);
216 // Skip base classes represented as fields. Anything which wants to include bases can chain
217 // those to the list we're building.
218 if !f
219 .ident
220 .as_ref()
221 .map(|id| {
222 id.to_string().starts_with("_base")
223 || id.to_string().starts_with("__bindgen_padding")
224 })
225 .unwrap_or(false)
226 {
227 field_deps.extend(r.types_encountered);
228 field_info.push(FieldInfo {
229 ty: r.ty,
230 type_kind: r.kind,
231 });
232 }
233 }
234 Err(e) => convert_errors.push(e),
235 };
236 }
237 convert_errors
238}
239
240/// Map to whether the bases are public.
241fn get_bases(item: &ItemStruct) -> HashMap<QualifiedName, bool> {
242 item.fields
243 .iter()
244 .filter_map(|f| {
245 let is_public = matches!(f.vis, Visibility::Public(_));
246 match &f.ty {
247 Type::Path(typ) => f
248 .ident
249 .as_ref()
250 .filter(|id| id.to_string().starts_with("_base"))
251 .map(|_| (QualifiedName::from_type_path(typ), is_public)),
252 _ => None,
253 }
254 })
255 .collect()
256}