blob: 2fafc95646d77c43b2d84f1a06a5a502b6bd6c2d [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;
Austin Schuh6ea9bfa2023-08-06 19:05:10 -070016use syn::{ItemStruct, Type, Visibility};
Brian Silverman4e662aa2022-05-11 23:10:19 -070017
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,
Austin Schuh6ea9bfa2023-08-06 19:05:10 -070026 ConvertErrorFromCpp,
Brian Silverman4e662aa2022-05-11 23:10:19 -070027 },
28 types::{Namespace, QualifiedName},
29};
30
31use super::tdef::{TypedefAnalysis, TypedefPhase};
32
Austin Schuh6ea9bfa2023-08-06 19:05:10 -070033#[derive(std::fmt::Debug)]
34
Brian Silverman4e662aa2022-05-11 23:10:19 -070035pub(crate) struct FieldInfo {
36 pub(crate) ty: Type,
37 pub(crate) type_kind: type_converter::TypeKind,
38}
39
Austin Schuh6ea9bfa2023-08-06 19:05:10 -070040#[derive(std::fmt::Debug)]
Brian Silverman4e662aa2022-05-11 23:10:19 -070041pub(crate) struct PodAnalysis {
42 pub(crate) kind: TypeKind,
43 pub(crate) bases: HashSet<QualifiedName>,
44 /// Base classes for which we should create casts.
45 /// That's just those which are on the allowlist,
46 /// because otherwise we don't know whether they're
47 /// abstract or not.
48 pub(crate) castable_bases: HashSet<QualifiedName>,
Austin Schuh6ea9bfa2023-08-06 19:05:10 -070049 /// All field types. e.g. for std::unique_ptr<A>, this would include
50 /// both std::unique_ptr and A
Brian Silverman4e662aa2022-05-11 23:10:19 -070051 pub(crate) field_deps: HashSet<QualifiedName>,
Austin Schuh6ea9bfa2023-08-06 19:05:10 -070052 /// Types within fields where we need a definition, e.g. for
53 /// std::unique_ptr<A> it would just be std::unique_ptr.
54 pub(crate) field_definition_deps: HashSet<QualifiedName>,
Brian Silverman4e662aa2022-05-11 23:10:19 -070055 pub(crate) field_info: Vec<FieldInfo>,
56 pub(crate) is_generic: bool,
57 pub(crate) in_anonymous_namespace: bool,
58}
59
Austin Schuh6ea9bfa2023-08-06 19:05:10 -070060#[derive(std::fmt::Debug)]
Brian Silverman4e662aa2022-05-11 23:10:19 -070061pub(crate) struct PodPhase;
62
63impl AnalysisPhase for PodPhase {
64 type TypedefAnalysis = TypedefAnalysis;
65 type StructAnalysis = PodAnalysis;
66 type FunAnalysis = ();
67}
68
69/// In our set of APIs, work out which ones are safe to represent
70/// by value in Rust (e.g. they don't have a destructor) and record
71/// as such. Return a set of APIs annotated with extra metadata,
72/// and an object which can be used to query the POD status of any
73/// type whether or not it's one of the [Api]s.
74pub(crate) fn analyze_pod_apis(
75 apis: ApiVec<TypedefPhase>,
76 config: &IncludeCppConfig,
Austin Schuh6ea9bfa2023-08-06 19:05:10 -070077) -> Result<ApiVec<PodPhase>, ConvertErrorFromCpp> {
Brian Silverman4e662aa2022-05-11 23:10:19 -070078 // This next line will return an error if any of the 'generate_pod'
79 // directives from the user can't be met because, for instance,
80 // a type contains a std::string or some other type which can't be
81 // held safely by value in Rust.
82 let byvalue_checker = ByValueChecker::new_from_apis(&apis, config)?;
83 let mut extra_apis = ApiVec::new();
84 let mut type_converter = TypeConverter::new(config, &apis);
85 let mut results = ApiVec::new();
86 convert_apis(
87 apis,
88 &mut results,
89 Api::fun_unchanged,
90 |name, details, _| {
91 analyze_struct(
92 &byvalue_checker,
93 &mut type_converter,
94 &mut extra_apis,
95 name,
96 details,
97 config,
98 )
99 },
100 analyze_enum,
101 Api::typedef_unchanged,
102 );
103 // Conceivably, the process of POD-analysing the first set of APIs could result
104 // in us creating new APIs to concretize generic types.
105 let extra_apis: ApiVec<PodPhase> = extra_apis.into_iter().map(add_analysis).collect();
106 let mut more_extra_apis = ApiVec::new();
107 convert_apis(
108 extra_apis,
109 &mut results,
110 Api::fun_unchanged,
111 |name, details, _| {
112 analyze_struct(
113 &byvalue_checker,
114 &mut type_converter,
115 &mut more_extra_apis,
116 name,
117 details,
118 config,
119 )
120 },
121 analyze_enum,
122 Api::typedef_unchanged,
123 );
124 assert!(more_extra_apis.is_empty());
125 Ok(results)
126}
127
128fn analyze_enum(
129 name: ApiName,
Austin Schuh6ea9bfa2023-08-06 19:05:10 -0700130 mut item: crate::minisyn::ItemEnum,
Brian Silverman4e662aa2022-05-11 23:10:19 -0700131) -> Result<Box<dyn Iterator<Item = Api<PodPhase>>>, ConvertErrorWithContext> {
132 let metadata = BindgenSemanticAttributes::new_retaining_others(&mut item.attrs);
133 metadata.check_for_fatal_attrs(&name.name.get_final_ident())?;
134 Ok(Box::new(std::iter::once(Api::Enum { name, item })))
135}
136
137fn analyze_struct(
138 byvalue_checker: &ByValueChecker,
139 type_converter: &mut TypeConverter,
140 extra_apis: &mut ApiVec<NullPhase>,
141 name: ApiName,
142 mut details: Box<StructDetails>,
143 config: &IncludeCppConfig,
144) -> Result<Box<dyn Iterator<Item = Api<PodPhase>>>, ConvertErrorWithContext> {
145 let id = name.name.get_final_ident();
Brian Silverman4e662aa2022-05-11 23:10:19 -0700146 let metadata = BindgenSemanticAttributes::new_retaining_others(&mut details.item.attrs);
147 metadata.check_for_fatal_attrs(&id)?;
148 let bases = get_bases(&details.item);
149 let mut field_deps = HashSet::new();
Austin Schuh6ea9bfa2023-08-06 19:05:10 -0700150 let mut field_definition_deps = HashSet::new();
Brian Silverman4e662aa2022-05-11 23:10:19 -0700151 let mut field_info = Vec::new();
152 let field_conversion_errors = get_struct_field_types(
153 type_converter,
154 name.name.get_namespace(),
155 &details.item,
156 &mut field_deps,
Austin Schuh6ea9bfa2023-08-06 19:05:10 -0700157 &mut field_definition_deps,
Brian Silverman4e662aa2022-05-11 23:10:19 -0700158 &mut field_info,
159 extra_apis,
160 );
161 let type_kind = if byvalue_checker.is_pod(&name.name) {
162 // It's POD so any errors encountered parsing its fields are important.
163 // Let's not allow anything to be POD if it's got rvalue reference fields.
164 if details.has_rvalue_reference_fields {
165 return Err(ConvertErrorWithContext(
Austin Schuh6ea9bfa2023-08-06 19:05:10 -0700166 ConvertErrorFromCpp::RValueReferenceField,
Brian Silverman4e662aa2022-05-11 23:10:19 -0700167 Some(ErrorContext::new_for_item(id)),
168 ));
169 }
170 if let Some(err) = field_conversion_errors.into_iter().next() {
171 return Err(ConvertErrorWithContext(
172 err,
173 Some(ErrorContext::new_for_item(id)),
174 ));
175 }
176 TypeKind::Pod
177 } else {
178 TypeKind::NonPod
179 };
180 let castable_bases = bases
181 .iter()
182 .filter(|(_, is_public)| **is_public)
183 .map(|(base, _)| base)
184 .filter(|base| config.is_on_allowlist(&base.to_cpp_name()))
185 .cloned()
186 .collect();
187 let is_generic = !details.item.generics.params.is_empty();
188 let in_anonymous_namespace = name
189 .name
190 .ns_segment_iter()
191 .any(|ns| ns.starts_with("_bindgen_mod"));
192 Ok(Box::new(std::iter::once(Api::Struct {
193 name,
194 details,
195 analysis: PodAnalysis {
196 kind: type_kind,
197 bases: bases.into_keys().collect(),
198 castable_bases,
199 field_deps,
Austin Schuh6ea9bfa2023-08-06 19:05:10 -0700200 field_definition_deps,
Brian Silverman4e662aa2022-05-11 23:10:19 -0700201 field_info,
202 is_generic,
203 in_anonymous_namespace,
204 },
205 })))
206}
207
208fn get_struct_field_types(
209 type_converter: &mut TypeConverter,
210 ns: &Namespace,
211 s: &ItemStruct,
212 field_deps: &mut HashSet<QualifiedName>,
Austin Schuh6ea9bfa2023-08-06 19:05:10 -0700213 field_definition_deps: &mut HashSet<QualifiedName>,
Brian Silverman4e662aa2022-05-11 23:10:19 -0700214 field_info: &mut Vec<FieldInfo>,
215 extra_apis: &mut ApiVec<NullPhase>,
Austin Schuh6ea9bfa2023-08-06 19:05:10 -0700216) -> Vec<ConvertErrorFromCpp> {
Brian Silverman4e662aa2022-05-11 23:10:19 -0700217 let mut convert_errors = Vec::new();
Brian Silvermanf3ec38b2022-07-06 20:43:36 -0700218 let struct_type_params = s
219 .generics
220 .type_params()
221 .map(|tp| tp.ident.clone())
222 .collect();
223 let type_conversion_context = TypeConversionContext::WithinStructField { struct_type_params };
Brian Silverman4e662aa2022-05-11 23:10:19 -0700224 for f in &s.fields {
Brian Silvermanf3ec38b2022-07-06 20:43:36 -0700225 let annotated = type_converter.convert_type(f.ty.clone(), ns, &type_conversion_context);
Brian Silverman4e662aa2022-05-11 23:10:19 -0700226 match annotated {
227 Ok(mut r) => {
228 extra_apis.append(&mut r.extra_apis);
229 // Skip base classes represented as fields. Anything which wants to include bases can chain
230 // those to the list we're building.
231 if !f
232 .ident
233 .as_ref()
234 .map(|id| {
235 id.to_string().starts_with("_base")
236 || id.to_string().starts_with("__bindgen_padding")
237 })
238 .unwrap_or(false)
239 {
240 field_deps.extend(r.types_encountered);
Austin Schuh6ea9bfa2023-08-06 19:05:10 -0700241 if let Type::Path(typ) = &r.ty {
242 // Later analyses need to know about the field
243 // types where we need full definitions, as opposed
244 // to just declarations. That means just the outermost
245 // type path.
246 // TODO: consider arrays.
247 field_definition_deps.insert(QualifiedName::from_type_path(typ));
248 }
Brian Silverman4e662aa2022-05-11 23:10:19 -0700249 field_info.push(FieldInfo {
250 ty: r.ty,
251 type_kind: r.kind,
252 });
253 }
254 }
255 Err(e) => convert_errors.push(e),
256 };
257 }
258 convert_errors
259}
260
261/// Map to whether the bases are public.
262fn get_bases(item: &ItemStruct) -> HashMap<QualifiedName, bool> {
263 item.fields
264 .iter()
265 .filter_map(|f| {
266 let is_public = matches!(f.vis, Visibility::Public(_));
267 match &f.ty {
268 Type::Path(typ) => f
269 .ident
270 .as_ref()
271 .filter(|id| id.to_string().starts_with("_base"))
272 .map(|_| (QualifiedName::from_type_path(typ), is_public)),
273 _ => None,
274 }
275 })
276 .collect()
277}