Squashed 'third_party/autocxx/' content from commit 629e8fa53

git-subtree-dir: third_party/autocxx
git-subtree-split: 629e8fa531a633164c0b52e2a3cab536d4cd0849
Signed-off-by: Brian Silverman <bsilver16384@gmail.com>
Change-Id: I62a03b0049f49adf029e0204639cdb5468dde1a1
diff --git a/engine/src/conversion/analysis/pod/mod.rs b/engine/src/conversion/analysis/pod/mod.rs
new file mode 100644
index 0000000..6722c23
--- /dev/null
+++ b/engine/src/conversion/analysis/pod/mod.rs
@@ -0,0 +1,257 @@
+// Copyright 2020 Google LLC
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// https://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+mod byvalue_checker;
+
+use indexmap::map::IndexMap as HashMap;
+use indexmap::set::IndexSet as HashSet;
+
+use autocxx_parser::IncludeCppConfig;
+use byvalue_checker::ByValueChecker;
+use syn::{ItemEnum, ItemStruct, Type, Visibility};
+
+use crate::{
+    conversion::{
+        analysis::type_converter::{self, add_analysis, TypeConversionContext, TypeConverter},
+        api::{AnalysisPhase, Api, ApiName, CppVisibility, NullPhase, StructDetails, TypeKind},
+        apivec::ApiVec,
+        convert_error::{ConvertErrorWithContext, ErrorContext},
+        error_reporter::convert_apis,
+        parse::BindgenSemanticAttributes,
+        ConvertError,
+    },
+    types::{Namespace, QualifiedName},
+};
+
+use super::tdef::{TypedefAnalysis, TypedefPhase};
+
+pub(crate) struct FieldInfo {
+    pub(crate) ty: Type,
+    pub(crate) type_kind: type_converter::TypeKind,
+}
+
+pub(crate) struct PodAnalysis {
+    pub(crate) kind: TypeKind,
+    pub(crate) bases: HashSet<QualifiedName>,
+    /// Base classes for which we should create casts.
+    /// That's just those which are on the allowlist,
+    /// because otherwise we don't know whether they're
+    /// abstract or not.
+    pub(crate) castable_bases: HashSet<QualifiedName>,
+    pub(crate) field_deps: HashSet<QualifiedName>,
+    pub(crate) field_info: Vec<FieldInfo>,
+    pub(crate) is_generic: bool,
+    pub(crate) in_anonymous_namespace: bool,
+}
+
+pub(crate) struct PodPhase;
+
+impl AnalysisPhase for PodPhase {
+    type TypedefAnalysis = TypedefAnalysis;
+    type StructAnalysis = PodAnalysis;
+    type FunAnalysis = ();
+}
+
+/// In our set of APIs, work out which ones are safe to represent
+/// by value in Rust (e.g. they don't have a destructor) and record
+/// as such. Return a set of APIs annotated with extra metadata,
+/// and an object which can be used to query the POD status of any
+/// type whether or not it's one of the [Api]s.
+pub(crate) fn analyze_pod_apis(
+    apis: ApiVec<TypedefPhase>,
+    config: &IncludeCppConfig,
+) -> Result<ApiVec<PodPhase>, ConvertError> {
+    // This next line will return an error if any of the 'generate_pod'
+    // directives from the user can't be met because, for instance,
+    // a type contains a std::string or some other type which can't be
+    // held safely by value in Rust.
+    let byvalue_checker = ByValueChecker::new_from_apis(&apis, config)?;
+    let mut extra_apis = ApiVec::new();
+    let mut type_converter = TypeConverter::new(config, &apis);
+    let mut results = ApiVec::new();
+    convert_apis(
+        apis,
+        &mut results,
+        Api::fun_unchanged,
+        |name, details, _| {
+            analyze_struct(
+                &byvalue_checker,
+                &mut type_converter,
+                &mut extra_apis,
+                name,
+                details,
+                config,
+            )
+        },
+        analyze_enum,
+        Api::typedef_unchanged,
+    );
+    // Conceivably, the process of POD-analysing the first set of APIs could result
+    // in us creating new APIs to concretize generic types.
+    let extra_apis: ApiVec<PodPhase> = extra_apis.into_iter().map(add_analysis).collect();
+    let mut more_extra_apis = ApiVec::new();
+    convert_apis(
+        extra_apis,
+        &mut results,
+        Api::fun_unchanged,
+        |name, details, _| {
+            analyze_struct(
+                &byvalue_checker,
+                &mut type_converter,
+                &mut more_extra_apis,
+                name,
+                details,
+                config,
+            )
+        },
+        analyze_enum,
+        Api::typedef_unchanged,
+    );
+    assert!(more_extra_apis.is_empty());
+    Ok(results)
+}
+
+fn analyze_enum(
+    name: ApiName,
+    mut item: ItemEnum,
+) -> Result<Box<dyn Iterator<Item = Api<PodPhase>>>, ConvertErrorWithContext> {
+    let metadata = BindgenSemanticAttributes::new_retaining_others(&mut item.attrs);
+    metadata.check_for_fatal_attrs(&name.name.get_final_ident())?;
+    Ok(Box::new(std::iter::once(Api::Enum { name, item })))
+}
+
+fn analyze_struct(
+    byvalue_checker: &ByValueChecker,
+    type_converter: &mut TypeConverter,
+    extra_apis: &mut ApiVec<NullPhase>,
+    name: ApiName,
+    mut details: Box<StructDetails>,
+    config: &IncludeCppConfig,
+) -> Result<Box<dyn Iterator<Item = Api<PodPhase>>>, ConvertErrorWithContext> {
+    let id = name.name.get_final_ident();
+    if details.vis != CppVisibility::Public {
+        return Err(ConvertErrorWithContext(
+            ConvertError::NonPublicNestedType,
+            Some(ErrorContext::new_for_item(id)),
+        ));
+    }
+    let metadata = BindgenSemanticAttributes::new_retaining_others(&mut details.item.attrs);
+    metadata.check_for_fatal_attrs(&id)?;
+    let bases = get_bases(&details.item);
+    let mut field_deps = HashSet::new();
+    let mut field_info = Vec::new();
+    let field_conversion_errors = get_struct_field_types(
+        type_converter,
+        name.name.get_namespace(),
+        &details.item,
+        &mut field_deps,
+        &mut field_info,
+        extra_apis,
+    );
+    let type_kind = if byvalue_checker.is_pod(&name.name) {
+        // It's POD so any errors encountered parsing its fields are important.
+        // Let's not allow anything to be POD if it's got rvalue reference fields.
+        if details.has_rvalue_reference_fields {
+            return Err(ConvertErrorWithContext(
+                ConvertError::RValueReferenceField,
+                Some(ErrorContext::new_for_item(id)),
+            ));
+        }
+        if let Some(err) = field_conversion_errors.into_iter().next() {
+            return Err(ConvertErrorWithContext(
+                err,
+                Some(ErrorContext::new_for_item(id)),
+            ));
+        }
+        TypeKind::Pod
+    } else {
+        TypeKind::NonPod
+    };
+    let castable_bases = bases
+        .iter()
+        .filter(|(_, is_public)| **is_public)
+        .map(|(base, _)| base)
+        .filter(|base| config.is_on_allowlist(&base.to_cpp_name()))
+        .cloned()
+        .collect();
+    let is_generic = !details.item.generics.params.is_empty();
+    let in_anonymous_namespace = name
+        .name
+        .ns_segment_iter()
+        .any(|ns| ns.starts_with("_bindgen_mod"));
+    Ok(Box::new(std::iter::once(Api::Struct {
+        name,
+        details,
+        analysis: PodAnalysis {
+            kind: type_kind,
+            bases: bases.into_keys().collect(),
+            castable_bases,
+            field_deps,
+            field_info,
+            is_generic,
+            in_anonymous_namespace,
+        },
+    })))
+}
+
+fn get_struct_field_types(
+    type_converter: &mut TypeConverter,
+    ns: &Namespace,
+    s: &ItemStruct,
+    field_deps: &mut HashSet<QualifiedName>,
+    field_info: &mut Vec<FieldInfo>,
+    extra_apis: &mut ApiVec<NullPhase>,
+) -> Vec<ConvertError> {
+    let mut convert_errors = Vec::new();
+    for f in &s.fields {
+        let annotated =
+            type_converter.convert_type(f.ty.clone(), ns, &TypeConversionContext::WithinReference);
+        match annotated {
+            Ok(mut r) => {
+                extra_apis.append(&mut r.extra_apis);
+                // Skip base classes represented as fields. Anything which wants to include bases can chain
+                // those to the list we're building.
+                if !f
+                    .ident
+                    .as_ref()
+                    .map(|id| {
+                        id.to_string().starts_with("_base")
+                            || id.to_string().starts_with("__bindgen_padding")
+                    })
+                    .unwrap_or(false)
+                {
+                    field_deps.extend(r.types_encountered);
+                    field_info.push(FieldInfo {
+                        ty: r.ty,
+                        type_kind: r.kind,
+                    });
+                }
+            }
+            Err(e) => convert_errors.push(e),
+        };
+    }
+    convert_errors
+}
+
+/// Map to whether the bases are public.
+fn get_bases(item: &ItemStruct) -> HashMap<QualifiedName, bool> {
+    item.fields
+        .iter()
+        .filter_map(|f| {
+            let is_public = matches!(f.vis, Visibility::Public(_));
+            match &f.ty {
+                Type::Path(typ) => f
+                    .ident
+                    .as_ref()
+                    .filter(|id| id.to_string().starts_with("_base"))
+                    .map(|_| (QualifiedName::from_type_path(typ), is_public)),
+                _ => None,
+            }
+        })
+        .collect()
+}