blob: 6722c237f7582967d91a3cf60619b161ba329d3f [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},
21 api::{AnalysisPhase, Api, ApiName, CppVisibility, NullPhase, StructDetails, TypeKind},
22 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();
137 if details.vis != CppVisibility::Public {
138 return Err(ConvertErrorWithContext(
139 ConvertError::NonPublicNestedType,
140 Some(ErrorContext::new_for_item(id)),
141 ));
142 }
143 let metadata = BindgenSemanticAttributes::new_retaining_others(&mut details.item.attrs);
144 metadata.check_for_fatal_attrs(&id)?;
145 let bases = get_bases(&details.item);
146 let mut field_deps = HashSet::new();
147 let mut field_info = Vec::new();
148 let field_conversion_errors = get_struct_field_types(
149 type_converter,
150 name.name.get_namespace(),
151 &details.item,
152 &mut field_deps,
153 &mut field_info,
154 extra_apis,
155 );
156 let type_kind = if byvalue_checker.is_pod(&name.name) {
157 // It's POD so any errors encountered parsing its fields are important.
158 // Let's not allow anything to be POD if it's got rvalue reference fields.
159 if details.has_rvalue_reference_fields {
160 return Err(ConvertErrorWithContext(
161 ConvertError::RValueReferenceField,
162 Some(ErrorContext::new_for_item(id)),
163 ));
164 }
165 if let Some(err) = field_conversion_errors.into_iter().next() {
166 return Err(ConvertErrorWithContext(
167 err,
168 Some(ErrorContext::new_for_item(id)),
169 ));
170 }
171 TypeKind::Pod
172 } else {
173 TypeKind::NonPod
174 };
175 let castable_bases = bases
176 .iter()
177 .filter(|(_, is_public)| **is_public)
178 .map(|(base, _)| base)
179 .filter(|base| config.is_on_allowlist(&base.to_cpp_name()))
180 .cloned()
181 .collect();
182 let is_generic = !details.item.generics.params.is_empty();
183 let in_anonymous_namespace = name
184 .name
185 .ns_segment_iter()
186 .any(|ns| ns.starts_with("_bindgen_mod"));
187 Ok(Box::new(std::iter::once(Api::Struct {
188 name,
189 details,
190 analysis: PodAnalysis {
191 kind: type_kind,
192 bases: bases.into_keys().collect(),
193 castable_bases,
194 field_deps,
195 field_info,
196 is_generic,
197 in_anonymous_namespace,
198 },
199 })))
200}
201
202fn get_struct_field_types(
203 type_converter: &mut TypeConverter,
204 ns: &Namespace,
205 s: &ItemStruct,
206 field_deps: &mut HashSet<QualifiedName>,
207 field_info: &mut Vec<FieldInfo>,
208 extra_apis: &mut ApiVec<NullPhase>,
209) -> Vec<ConvertError> {
210 let mut convert_errors = Vec::new();
211 for f in &s.fields {
212 let annotated =
213 type_converter.convert_type(f.ty.clone(), ns, &TypeConversionContext::WithinReference);
214 match annotated {
215 Ok(mut r) => {
216 extra_apis.append(&mut r.extra_apis);
217 // Skip base classes represented as fields. Anything which wants to include bases can chain
218 // those to the list we're building.
219 if !f
220 .ident
221 .as_ref()
222 .map(|id| {
223 id.to_string().starts_with("_base")
224 || id.to_string().starts_with("__bindgen_padding")
225 })
226 .unwrap_or(false)
227 {
228 field_deps.extend(r.types_encountered);
229 field_info.push(FieldInfo {
230 ty: r.ty,
231 type_kind: r.kind,
232 });
233 }
234 }
235 Err(e) => convert_errors.push(e),
236 };
237 }
238 convert_errors
239}
240
241/// Map to whether the bases are public.
242fn get_bases(item: &ItemStruct) -> HashMap<QualifiedName, bool> {
243 item.fields
244 .iter()
245 .filter_map(|f| {
246 let is_public = matches!(f.vis, Visibility::Public(_));
247 match &f.ty {
248 Type::Path(typ) => f
249 .ident
250 .as_ref()
251 .filter(|id| id.to_string().starts_with("_base"))
252 .map(|_| (QualifiedName::from_type_path(typ), is_public)),
253 _ => None,
254 }
255 })
256 .collect()
257}