Brian Silverman | 4e662aa | 2022-05-11 23:10:19 -0700 | [diff] [blame] | 1 | // 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 | |
| 9 | mod byvalue_checker; |
| 10 | |
| 11 | use indexmap::map::IndexMap as HashMap; |
| 12 | use indexmap::set::IndexSet as HashSet; |
| 13 | |
| 14 | use autocxx_parser::IncludeCppConfig; |
| 15 | use byvalue_checker::ByValueChecker; |
Austin Schuh | 6ea9bfa | 2023-08-06 19:05:10 -0700 | [diff] [blame^] | 16 | use syn::{ItemStruct, Type, Visibility}; |
Brian Silverman | 4e662aa | 2022-05-11 23:10:19 -0700 | [diff] [blame] | 17 | |
| 18 | use crate::{ |
| 19 | conversion::{ |
| 20 | analysis::type_converter::{self, add_analysis, TypeConversionContext, TypeConverter}, |
Brian Silverman | f3ec38b | 2022-07-06 20:43:36 -0700 | [diff] [blame] | 21 | api::{AnalysisPhase, Api, ApiName, NullPhase, StructDetails, TypeKind}, |
Brian Silverman | 4e662aa | 2022-05-11 23:10:19 -0700 | [diff] [blame] | 22 | apivec::ApiVec, |
| 23 | convert_error::{ConvertErrorWithContext, ErrorContext}, |
| 24 | error_reporter::convert_apis, |
| 25 | parse::BindgenSemanticAttributes, |
Austin Schuh | 6ea9bfa | 2023-08-06 19:05:10 -0700 | [diff] [blame^] | 26 | ConvertErrorFromCpp, |
Brian Silverman | 4e662aa | 2022-05-11 23:10:19 -0700 | [diff] [blame] | 27 | }, |
| 28 | types::{Namespace, QualifiedName}, |
| 29 | }; |
| 30 | |
| 31 | use super::tdef::{TypedefAnalysis, TypedefPhase}; |
| 32 | |
Austin Schuh | 6ea9bfa | 2023-08-06 19:05:10 -0700 | [diff] [blame^] | 33 | #[derive(std::fmt::Debug)] |
| 34 | |
Brian Silverman | 4e662aa | 2022-05-11 23:10:19 -0700 | [diff] [blame] | 35 | pub(crate) struct FieldInfo { |
| 36 | pub(crate) ty: Type, |
| 37 | pub(crate) type_kind: type_converter::TypeKind, |
| 38 | } |
| 39 | |
Austin Schuh | 6ea9bfa | 2023-08-06 19:05:10 -0700 | [diff] [blame^] | 40 | #[derive(std::fmt::Debug)] |
Brian Silverman | 4e662aa | 2022-05-11 23:10:19 -0700 | [diff] [blame] | 41 | pub(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 Schuh | 6ea9bfa | 2023-08-06 19:05:10 -0700 | [diff] [blame^] | 49 | /// All field types. e.g. for std::unique_ptr<A>, this would include |
| 50 | /// both std::unique_ptr and A |
Brian Silverman | 4e662aa | 2022-05-11 23:10:19 -0700 | [diff] [blame] | 51 | pub(crate) field_deps: HashSet<QualifiedName>, |
Austin Schuh | 6ea9bfa | 2023-08-06 19:05:10 -0700 | [diff] [blame^] | 52 | /// 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 Silverman | 4e662aa | 2022-05-11 23:10:19 -0700 | [diff] [blame] | 55 | pub(crate) field_info: Vec<FieldInfo>, |
| 56 | pub(crate) is_generic: bool, |
| 57 | pub(crate) in_anonymous_namespace: bool, |
| 58 | } |
| 59 | |
Austin Schuh | 6ea9bfa | 2023-08-06 19:05:10 -0700 | [diff] [blame^] | 60 | #[derive(std::fmt::Debug)] |
Brian Silverman | 4e662aa | 2022-05-11 23:10:19 -0700 | [diff] [blame] | 61 | pub(crate) struct PodPhase; |
| 62 | |
| 63 | impl 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. |
| 74 | pub(crate) fn analyze_pod_apis( |
| 75 | apis: ApiVec<TypedefPhase>, |
| 76 | config: &IncludeCppConfig, |
Austin Schuh | 6ea9bfa | 2023-08-06 19:05:10 -0700 | [diff] [blame^] | 77 | ) -> Result<ApiVec<PodPhase>, ConvertErrorFromCpp> { |
Brian Silverman | 4e662aa | 2022-05-11 23:10:19 -0700 | [diff] [blame] | 78 | // 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 | |
| 128 | fn analyze_enum( |
| 129 | name: ApiName, |
Austin Schuh | 6ea9bfa | 2023-08-06 19:05:10 -0700 | [diff] [blame^] | 130 | mut item: crate::minisyn::ItemEnum, |
Brian Silverman | 4e662aa | 2022-05-11 23:10:19 -0700 | [diff] [blame] | 131 | ) -> 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 | |
| 137 | fn 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 Silverman | 4e662aa | 2022-05-11 23:10:19 -0700 | [diff] [blame] | 146 | 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 Schuh | 6ea9bfa | 2023-08-06 19:05:10 -0700 | [diff] [blame^] | 150 | let mut field_definition_deps = HashSet::new(); |
Brian Silverman | 4e662aa | 2022-05-11 23:10:19 -0700 | [diff] [blame] | 151 | 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 Schuh | 6ea9bfa | 2023-08-06 19:05:10 -0700 | [diff] [blame^] | 157 | &mut field_definition_deps, |
Brian Silverman | 4e662aa | 2022-05-11 23:10:19 -0700 | [diff] [blame] | 158 | &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 Schuh | 6ea9bfa | 2023-08-06 19:05:10 -0700 | [diff] [blame^] | 166 | ConvertErrorFromCpp::RValueReferenceField, |
Brian Silverman | 4e662aa | 2022-05-11 23:10:19 -0700 | [diff] [blame] | 167 | 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 Schuh | 6ea9bfa | 2023-08-06 19:05:10 -0700 | [diff] [blame^] | 200 | field_definition_deps, |
Brian Silverman | 4e662aa | 2022-05-11 23:10:19 -0700 | [diff] [blame] | 201 | field_info, |
| 202 | is_generic, |
| 203 | in_anonymous_namespace, |
| 204 | }, |
| 205 | }))) |
| 206 | } |
| 207 | |
| 208 | fn get_struct_field_types( |
| 209 | type_converter: &mut TypeConverter, |
| 210 | ns: &Namespace, |
| 211 | s: &ItemStruct, |
| 212 | field_deps: &mut HashSet<QualifiedName>, |
Austin Schuh | 6ea9bfa | 2023-08-06 19:05:10 -0700 | [diff] [blame^] | 213 | field_definition_deps: &mut HashSet<QualifiedName>, |
Brian Silverman | 4e662aa | 2022-05-11 23:10:19 -0700 | [diff] [blame] | 214 | field_info: &mut Vec<FieldInfo>, |
| 215 | extra_apis: &mut ApiVec<NullPhase>, |
Austin Schuh | 6ea9bfa | 2023-08-06 19:05:10 -0700 | [diff] [blame^] | 216 | ) -> Vec<ConvertErrorFromCpp> { |
Brian Silverman | 4e662aa | 2022-05-11 23:10:19 -0700 | [diff] [blame] | 217 | let mut convert_errors = Vec::new(); |
Brian Silverman | f3ec38b | 2022-07-06 20:43:36 -0700 | [diff] [blame] | 218 | 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 Silverman | 4e662aa | 2022-05-11 23:10:19 -0700 | [diff] [blame] | 224 | for f in &s.fields { |
Brian Silverman | f3ec38b | 2022-07-06 20:43:36 -0700 | [diff] [blame] | 225 | let annotated = type_converter.convert_type(f.ty.clone(), ns, &type_conversion_context); |
Brian Silverman | 4e662aa | 2022-05-11 23:10:19 -0700 | [diff] [blame] | 226 | 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 Schuh | 6ea9bfa | 2023-08-06 19:05:10 -0700 | [diff] [blame^] | 241 | 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 Silverman | 4e662aa | 2022-05-11 23:10:19 -0700 | [diff] [blame] | 249 | 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. |
| 262 | fn 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 | } |