diff --git a/Cargo.lock b/Cargo.lock index aa27c7ac6..25e792164 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -154,9 +154,9 @@ dependencies = [ [[package]] name = "autocxx-bindgen" -version = "0.71.1" +version = "0.72.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a7d41cf081e31a74378456586b47a5bae2b75a98e5f7c248c9d9bf433e3637f4" +checksum = "537ef6dffea4311d04a313f645bdd04d2c88b356a6eb7afd4258e5aee70dea8c" dependencies = [ "bitflags 2.6.0", "cexpr", diff --git a/Cargo.toml b/Cargo.toml index dfbbaaf00..05d2aab9b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -41,5 +41,5 @@ exclude = ["examples/s2", "examples/steam-mini", "examples/subclass", "examples/ #[patch.crates-io] #cxx = { path="../cxx" } #cxx-gen = { path="../cxx/gen/lib" } -#autocxx-bindgen = { path="../bindgen" } +#autocxx-bindgen = { path="../bindgen/bindgen" } #moveit = { path="../moveit" } diff --git a/engine/Cargo.toml b/engine/Cargo.toml index 039f3367e..f260b8f79 100644 --- a/engine/Cargo.toml +++ b/engine/Cargo.toml @@ -30,8 +30,8 @@ log = "0.4" proc-macro2 = "1.0.11" quote = "1.0" indoc = "1.0" -autocxx-bindgen = { version = "=0.71.1", default-features = false, features = ["logging", "which-rustfmt"] } -#autocxx-bindgen = { git = "https://github.com/adetaylor/rust-bindgen", branch = "merge-latest-upstream", default-features = false, features = ["logging", "which-rustfmt"] } +autocxx-bindgen = { version = "=0.72.0", default-features = false, features = ["logging", "which-rustfmt"] } +#autocxx-bindgen = { git = "https://github.com/adetaylor/rust-bindgen", branch = "switch-from-attributes-to-callbacks", default-features = false, features = ["logging", "which-rustfmt"] } itertools = "0.10.3" cc = { version = "1.0", optional = true } # Note: Keep the patch-level version of cxx-gen and cxx in sync. diff --git a/engine/src/conversion/analysis/allocators.rs b/engine/src/conversion/analysis/allocators.rs index aab637cd0..1c5243164 100644 --- a/engine/src/conversion/analysis/allocators.rs +++ b/engine/src/conversion/analysis/allocators.rs @@ -12,10 +12,7 @@ use syn::{parse_quote, punctuated::Punctuated, token::Comma, FnArg, ReturnType}; use crate::{ conversion::{ - api::{ - Api, ApiName, CppVisibility, DeletedOrDefaulted, FuncToConvert, Provenance, References, - TraitSynthesis, - }, + api::{Api, ApiName, CppVisibility, FuncToConvert, Provenance, TraitSynthesis}, apivec::ApiVec, }, minisyn::minisynize_punctuated, @@ -80,17 +77,15 @@ fn create_alloc_and_free(ty_name: QualifiedName) -> impl Iterator * #return_mutability #to_typ + -> __bindgen_marker_Reference < * #return_mutability #to_typ > }, vis: parse_quote! { pub }, - virtualness: crate::conversion::api::Virtualness::None, + virtualness: None, cpp_vis: crate::conversion::api::CppVisibility::Public, special_member: None, - unused_template_param: false, - references: References::new_with_this_and_return_as_reference(), original_name: None, self_ty: Some(from.clone()), synthesized_this_type: None, @@ -116,7 +111,7 @@ fn create_cast(from: &QualifiedName, to: &QualifiedName, mutable: CastMutability mutable, }), synthetic_cpp: Some((CppFunctionBody::Cast, CppFunctionKind::Function)), - is_deleted: DeletedOrDefaulted::Neither, + is_deleted: None, provenance: Provenance::SynthesizedOther, variadic: false, }), diff --git a/engine/src/conversion/analysis/fun/implicit_constructors.rs b/engine/src/conversion/analysis/fun/implicit_constructors.rs index a1ebf27fd..19627a57a 100644 --- a/engine/src/conversion/analysis/fun/implicit_constructors.rs +++ b/engine/src/conversion/analysis/fun/implicit_constructors.rs @@ -6,18 +6,19 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +use autocxx_bindgen::callbacks::{Explicitness, SpecialMemberKind, Visibility as CppVisibility}; use indexmap::map::IndexMap as HashMap; use indexmap::{map::Entry, set::IndexSet as HashSet}; -use syn::{Type, TypeArray}; +use syn::{PatType, Type, TypeArray}; -use crate::conversion::api::DeletedOrDefaulted; +use crate::conversion::type_helpers::type_is_reference; use crate::{ conversion::{ analysis::{ depth_first::fields_and_bases_first, pod::PodAnalysis, type_converter::TypeKind, }, - api::{Api, ApiName, CppVisibility, FuncToConvert, SpecialMemberKind}, + api::{Api, ApiName, FuncToConvert}, apivec::ApiVec, convert_error::ConvertErrorWithContext, ConvertErrorFromCpp, @@ -575,7 +576,7 @@ fn find_explicit_items( .entry(ExplicitType { ty, kind }) { Entry::Vacant(entry) => { - entry.insert(if matches!(fun.is_deleted, DeletedOrDefaulted::Deleted) { + entry.insert(if matches!(fun.is_deleted, Some(Explicitness::Deleted)) { ExplicitFound::Deleted } else { ExplicitFound::UserDefined(fun.cpp_vis) @@ -605,7 +606,7 @@ fn find_explicit_items( Some(SpecialMemberKind::AssignmentOperator) ) => { - let is_move_assignment_operator = !fun.references.rvalue_ref_params.is_empty(); + let is_move_assignment_operator = !any_input_is_rvalue_reference(&fun.inputs); merge_fun( impl_for.clone(), if is_move_assignment_operator { @@ -694,6 +695,15 @@ fn find_explicit_items( (result, unknown_types) } +fn any_input_is_rvalue_reference( + inputs: &syn::punctuated::Punctuated, +) -> bool { + inputs.iter().any(|input| match &input.0 { + syn::FnArg::Receiver(_) => false, + syn::FnArg::Typed(PatType { ty, .. }, ..) => type_is_reference(ty.as_ref(), true), + }) +} + /// Returns the information for a given known type. fn known_type_items_found(constructor_details: KnownTypeConstructorDetails) -> ItemsFound { let exists_public = SpecialMemberFound::Explicit(CppVisibility::Public); diff --git a/engine/src/conversion/analysis/fun/mod.rs b/engine/src/conversion/analysis/fun/mod.rs index 02d96af06..c4e7fe5cd 100644 --- a/engine/src/conversion/analysis/fun/mod.rs +++ b/engine/src/conversion/analysis/fun/mod.rs @@ -19,19 +19,21 @@ use crate::{ type_converter::{self, add_analysis, TypeConversionContext, TypeConverter}, }, api::{ - ApiName, CastMutability, CppVisibility, DeletedOrDefaulted, FuncToConvert, NullPhase, - Provenance, References, SpecialMemberKind, SubclassName, TraitImplSignature, - TraitSynthesis, UnsafetyNeeded, Virtualness, + ApiName, CastMutability, FuncToConvert, NullPhase, Provenance, SubclassName, + TraitImplSignature, TraitSynthesis, UnsafetyNeeded, }, apivec::ApiVec, convert_error::{ConvertErrorWithContext, ErrorContext, ErrorContextType}, error_reporter::{convert_apis, report_any_error}, + type_helpers::{type_has_unused_template_param, type_is_reference, unwrap_has_opaque}, CppEffectiveName, CppOriginalName, }, known_types::known_types, minisyn::{minisynize_punctuated, FnArg}, types::validate_ident_ok_for_rust, }; +use autocxx_bindgen::callbacks::Visibility as CppVisibility; +use autocxx_bindgen::callbacks::{Explicitness, SpecialMemberKind, Virtualness}; use indexmap::map::IndexMap as HashMap; use indexmap::set::IndexSet as HashSet; @@ -69,7 +71,7 @@ use super::{ doc_label::make_doc_attrs, pod::{PodAnalysis, PodPhase}, tdef::TypedefAnalysis, - type_converter::{Annotated, PointerTreatment}, + type_converter::Annotated, }; #[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)] @@ -437,9 +439,8 @@ impl<'a> FnAnalyzer<'a> { &mut self, ty: Box, ns: &Namespace, - pointer_treatment: PointerTreatment, ) -> Result>, ConvertErrorFromCpp> { - let ctx = TypeConversionContext::OuterType { pointer_treatment }; + let ctx = TypeConversionContext::OuterType {}; let mut annotated = self.type_converter.convert_boxed_type(ty, ns, &ctx)?; self.extra_apis.append(&mut annotated.extra_apis); Ok(annotated) @@ -524,7 +525,7 @@ impl<'a> FnAnalyzer<'a> { **fun, FuncToConvert { special_member: Some(SpecialMemberKind::Destructor), - is_deleted: DeletedOrDefaulted::Neither | DeletedOrDefaulted::Defaulted, + is_deleted: None | Some(Explicitness::Defaulted), cpp_vis: CppVisibility::Public, .. } @@ -750,7 +751,6 @@ impl<'a> FnAnalyzer<'a> { ns, &diagnostic_name, &fun.synthesized_this_type, - &fun.references, true, false, None, @@ -967,9 +967,11 @@ impl<'a> FnAnalyzer<'a> { let receiver_mutability = receiver_mutability.expect("Failed to find receiver details"); match fun.virtualness { - Virtualness::None => MethodKind::Normal, - Virtualness::Virtual => MethodKind::Virtual(receiver_mutability), - Virtualness::PureVirtual => MethodKind::PureVirtual(receiver_mutability), + None => MethodKind::Normal, + Some(Virtualness::Virtual) => MethodKind::Virtual(receiver_mutability), + Some(Virtualness::PureVirtual) => { + MethodKind::PureVirtual(receiver_mutability) + } } }; // Disambiguate overloads. @@ -1133,7 +1135,7 @@ impl<'a> FnAnalyzer<'a> { Ok(_) => panic!("No error in the error"), Err(problem) => set_ignore_reason(problem), } - } else if fun.unused_template_param { + } else if any_type_has_unused_template_param(&fun.inputs, &fun.output) { // This indicates that bindgen essentially flaked out because templates // were too complex. set_ignore_reason(ConvertErrorFromCpp::UnusedTemplateParam) @@ -1145,9 +1147,9 @@ impl<'a> FnAnalyzer<'a> { // treat it as an assignment operator, but anything below we still consider when // deciding which other C++ special member functions are implicitly defined. set_ignore_reason(ConvertErrorFromCpp::AssignmentOperator) - } else if fun.references.rvalue_ref_return { + } else if return_type_is_reference(&fun.output) { set_ignore_reason(ConvertErrorFromCpp::RValueReturn) - } else if matches!(fun.is_deleted, DeletedOrDefaulted::Deleted) { + } else if matches!(fun.is_deleted, Some(Explicitness::Deleted)) { set_ignore_reason(ConvertErrorFromCpp::Deleted) } else { match kind { @@ -1210,13 +1212,7 @@ impl<'a> FnAnalyzer<'a> { // Analyze the return type, just as we previously did for the // parameters. let mut return_analysis = self - .convert_return_type( - &fun.output, - ns, - &diagnostic_name, - &fun.references, - sophistication, - ) + .convert_return_type(&fun.output, ns, &diagnostic_name, sophistication) .unwrap_or_else(|err| { set_ignore_reason(err); ReturnTypeAnalysis::default() @@ -1510,7 +1506,6 @@ impl<'a> FnAnalyzer<'a> { ns, diagnostic_name, &fun.synthesized_this_type, - &fun.references, false, is_move_constructor, force_rust_conversion, @@ -1666,7 +1661,6 @@ impl<'a> FnAnalyzer<'a> { ns: &Namespace, diagnostic_name: &QualifiedName, virtual_this: &Option, - references: &References, treat_this_as_reference: bool, is_move_constructor: bool, force_rust_conversion: Option, @@ -1678,15 +1672,20 @@ impl<'a> FnAnalyzer<'a> { let mut pt = pt.clone(); let mut self_type = None; let old_pat = *pt.pat; - let mut pointer_treatment = PointerTreatment::Pointer; let mut is_placement_return_destination = false; - let new_pat = match old_pat { + let (new_pat, ty_to_convert) = match old_pat { syn::Pat::Ident(mut pp) if pp.ident == "this" => { let this_type = match pt.ty.as_ref() { Type::Ptr(TypePtr { elem, mutability, .. }) => match elem.as_ref() { Type::Path(typ) => { + let typ = unwrap_has_opaque(typ) + .and_then(|ty| match ty { + Type::Path(typ) => Some(typ), + _ => None, + }) + .unwrap_or(typ); let receiver_mutability = if mutability.is_some() { ReceiverMutability::Mutable } else { @@ -1721,17 +1720,23 @@ impl<'a> FnAnalyzer<'a> { is_placement_return_destination = construct_into_self; if treat_this_as_reference { pp.ident = Ident::new("self", pp.ident.span()); - pointer_treatment = PointerTreatment::Reference; + let pt_ty = pt.ty.as_ref(); + ( + syn::Pat::Ident(pp), + Box::new( + syn::parse_quote! { __bindgen_marker_Reference < #pt_ty >}, + ), + ) + } else { + (syn::Pat::Ident(pp), pt.ty) } - syn::Pat::Ident(pp) } syn::Pat::Ident(pp) => { validate_ident_ok_for_cxx(&pp.ident.to_string()) .map_err(ConvertErrorFromCpp::InvalidIdent)?; - pointer_treatment = references.param_treatment(&pp.ident.clone().into()); - syn::Pat::Ident(pp) + (syn::Pat::Ident(pp), pt.ty) } - _ => old_pat, + _ => (old_pat, pt.ty), }; let is_placement_return_destination = is_placement_return_destination @@ -1739,7 +1744,7 @@ impl<'a> FnAnalyzer<'a> { force_rust_conversion, Some(RustConversionType::FromPlacementParamToNewReturn) ); - let annotated_type = self.convert_boxed_type(pt.ty, ns, pointer_treatment)?; + let annotated_type = self.convert_boxed_type(ty_to_convert, ns)?; let conversion = self.argument_conversion_details( &annotated_type, is_move_constructor, @@ -1943,14 +1948,12 @@ impl<'a> FnAnalyzer<'a> { rt: &ReturnType, ns: &Namespace, diagnostic_name: &QualifiedName, - references: &References, sophistication: TypeConversionSophistication, ) -> Result { Ok(match rt { ReturnType::Default => ReturnTypeAnalysis::default(), ReturnType::Type(rarrow, boxed_type) => { - let annotated_type = - self.convert_boxed_type(boxed_type.clone(), ns, references.return_treatment())?; + let annotated_type = self.convert_boxed_type(boxed_type.clone(), ns)?; let boxed_type = annotated_type.ty; let ty: &Type = boxed_type.as_ref(); match ty { @@ -1976,7 +1979,6 @@ impl<'a> FnAnalyzer<'a> { ns, diagnostic_name, &None, - &References::default(), false, false, Some(RustConversionType::FromPlacementParamToNewReturn), @@ -2076,7 +2078,6 @@ impl<'a> FnAnalyzer<'a> { &mut apis, SpecialMemberKind::DefaultConstructor, parse_quote! { this: *mut #path }, - References::default(), ); } if items_found.implicit_move_constructor_needed() { @@ -2085,11 +2086,7 @@ impl<'a> FnAnalyzer<'a> { "move_ctor", &mut apis, SpecialMemberKind::MoveConstructor, - parse_quote! { this: *mut #path, other: *mut #path }, - References { - rvalue_ref_params: [make_ident("other")].into_iter().collect(), - ..Default::default() - }, + parse_quote! { this: *mut #path, other: __bindgen_marker_RValueReference < *mut #path > }, ) } if items_found.implicit_copy_constructor_needed() { @@ -2098,11 +2095,7 @@ impl<'a> FnAnalyzer<'a> { "const_copy_ctor", &mut apis, SpecialMemberKind::CopyConstructor, - parse_quote! { this: *mut #path, other: *const #path }, - References { - ref_params: [make_ident("other")].into_iter().collect(), - ..Default::default() - }, + parse_quote! { this: *mut #path, other: __bindgen_marker_Reference < *const #path > }, ) } if items_found.implicit_destructor_needed() { @@ -2112,7 +2105,6 @@ impl<'a> FnAnalyzer<'a> { &mut apis, SpecialMemberKind::Destructor, parse_quote! { this: *mut #path }, - References::default(), ); } } @@ -2152,7 +2144,6 @@ impl<'a> FnAnalyzer<'a> { apis: &mut ApiVec, special_member: SpecialMemberKind, inputs: Punctuated, - references: References, ) { let self_ty = items_found.name.as_ref().unwrap(); let ident = make_ident(self.config.uniquify_name_per_mod(&format!( @@ -2178,26 +2169,25 @@ impl<'a> FnAnalyzer<'a> { let mut any_errors = ApiVec::new(); apis.extend( report_any_error(&ns, &mut any_errors, || { + let special_member_desc = special_member_to_string(special_member); self.analyze_foreign_fn_and_subclasses( fake_api_name, Box::new(FuncToConvert { self_ty: Some(self_ty.clone()), ident, - doc_attrs: make_doc_attrs(format!("Synthesized {special_member}.")) + doc_attrs: make_doc_attrs(format!("Synthesized {special_member_desc}.")) .into_iter() .map(Into::into) .collect(), inputs: minisynize_punctuated(inputs), output: ReturnType::Default.into(), vis: parse_quote! { pub }, - virtualness: Virtualness::None, + virtualness: None, cpp_vis: CppVisibility::Public, special_member: Some(special_member), - unused_template_param: false, - references, original_name: None, synthesized_this_type: None, - is_deleted: DeletedOrDefaulted::Neither, + is_deleted: None, add_to_trait: None, synthetic_cpp: None, provenance: Provenance::SynthesizedOther, @@ -2212,6 +2202,31 @@ impl<'a> FnAnalyzer<'a> { } } +fn any_type_has_unused_template_param( + inputs: &Punctuated, + output: &crate::minisyn::ReturnType, +) -> bool { + if let ReturnType::Type(_, ty) = &output.0 { + if type_has_unused_template_param(ty.as_ref()) { + return true; + } + } + inputs.iter().any(|input| match &input.0 { + syn::FnArg::Receiver(_) => false, + syn::FnArg::Typed(PatType { ty, .. }) => type_has_unused_template_param(ty.as_ref()), + }) +} + +fn special_member_to_string(special_member: SpecialMemberKind) -> &'static str { + match special_member { + SpecialMemberKind::DefaultConstructor => "default constructor", + SpecialMemberKind::CopyConstructor => "copy constructor", + SpecialMemberKind::MoveConstructor => "move constructor", + SpecialMemberKind::Destructor => "destructor", + SpecialMemberKind::AssignmentOperator => "assignment operator", + } +} + /// Attempts to determine whether this function name is a constructor, and if so, /// returns the suffix. fn constructor_with_suffix<'a>(rust_name: &'a str, nested_type_ident: &str) -> Option<&'a str> { @@ -2299,6 +2314,14 @@ impl Api { } } +fn return_type_is_reference(output: &crate::minisyn::ReturnType) -> bool { + if let ReturnType::Type(_, ty) = &output.0 { + type_is_reference(ty.as_ref(), true) + } else { + false + } +} + fn extract_type_from_pinned_mut_ref(ty: &TypePath) -> Type { match ty .path diff --git a/engine/src/conversion/analysis/fun/subclass.rs b/engine/src/conversion/analysis/fun/subclass.rs index a680852d6..7fd325204 100644 --- a/engine/src/conversion/analysis/fun/subclass.rs +++ b/engine/src/conversion/analysis/fun/subclass.rs @@ -16,12 +16,12 @@ use crate::conversion::analysis::fun::{FnKind, MethodKind, ReceiverMutability, U use crate::conversion::analysis::pod::PodPhase; use crate::conversion::api::{ CppVisibility, FuncToConvert, Provenance, RustSubclassFnDetails, SubclassConstructorDetails, - SubclassName, SuperclassMethod, UnsafetyNeeded, Virtualness, + SubclassName, SuperclassMethod, UnsafetyNeeded, }; use crate::conversion::apivec::ApiVec; -use crate::conversion::parse::CppOriginalName; use crate::conversion::CppEffectiveName; use crate::minisyn::minisynize_punctuated; +use crate::parse_callbacks::CppOriginalName; use crate::{ conversion::{ analysis::fun::function_wrapper::{ @@ -64,12 +64,10 @@ pub(super) fn create_subclass_fn_wrapper( inputs: fun.inputs.clone(), output: fun.output.clone(), vis: fun.vis.clone(), - virtualness: Virtualness::None, + virtualness: None, cpp_vis: CppVisibility::Public, special_member: None, - unused_template_param: fun.unused_template_param, original_name: None, - references: fun.references.clone(), add_to_trait: fun.add_to_trait.clone(), is_deleted: fun.is_deleted, synthetic_cpp: None, @@ -251,12 +249,10 @@ pub(super) fn create_subclass_constructor( inputs, output: fun.output.clone(), vis: fun.vis.clone(), - virtualness: Virtualness::None, + virtualness: None, cpp_vis: CppVisibility::Public, - special_member: fun.special_member.clone(), + special_member: fun.special_member, original_name: None, - unused_template_param: fun.unused_template_param, - references: fun.references.clone(), synthesized_this_type: Some(cpp.clone()), self_ty: Some(cpp), add_to_trait: None, diff --git a/engine/src/conversion/analysis/mod.rs b/engine/src/conversion/analysis/mod.rs index d73301161..67462ef5d 100644 --- a/engine/src/conversion/analysis/mod.rs +++ b/engine/src/conversion/analysis/mod.rs @@ -25,4 +25,3 @@ mod type_converter; pub(crate) use name_check::check_names; pub(crate) use replace_hopeless_typedef_targets::replace_hopeless_typedef_targets; -pub(crate) use type_converter::PointerTreatment; diff --git a/engine/src/conversion/analysis/pod/mod.rs b/engine/src/conversion/analysis/pod/mod.rs index 2fafc9564..a836966d8 100644 --- a/engine/src/conversion/analysis/pod/mod.rs +++ b/engine/src/conversion/analysis/pod/mod.rs @@ -20,12 +20,13 @@ use crate::{ analysis::type_converter::{self, add_analysis, TypeConversionContext, TypeConverter}, api::{AnalysisPhase, Api, ApiName, NullPhase, StructDetails, TypeKind}, apivec::ApiVec, + check_for_fatal_attrs, convert_error::{ConvertErrorWithContext, ErrorContext}, error_reporter::convert_apis, - parse::BindgenSemanticAttributes, ConvertErrorFromCpp, }, types::{Namespace, QualifiedName}, + ParseCallbackResults, }; use super::tdef::{TypedefAnalysis, TypedefPhase}; @@ -74,6 +75,7 @@ impl AnalysisPhase for PodPhase { pub(crate) fn analyze_pod_apis( apis: ApiVec, config: &IncludeCppConfig, + parse_callback_results: &ParseCallbackResults, ) -> Result, ConvertErrorFromCpp> { // This next line will return an error if any of the 'generate_pod' // directives from the user can't be met because, for instance, @@ -95,9 +97,10 @@ pub(crate) fn analyze_pod_apis( name, details, config, + parse_callback_results, ) }, - analyze_enum, + |name, item| analyze_enum(name, item, parse_callback_results), Api::typedef_unchanged, ); // Conceivably, the process of POD-analysing the first set of APIs could result @@ -116,9 +119,10 @@ pub(crate) fn analyze_pod_apis( name, details, config, + parse_callback_results, ) }, - analyze_enum, + |name, item| analyze_enum(name, item, parse_callback_results), Api::typedef_unchanged, ); assert!(more_extra_apis.is_empty()); @@ -127,10 +131,10 @@ pub(crate) fn analyze_pod_apis( fn analyze_enum( name: ApiName, - mut item: crate::minisyn::ItemEnum, + item: crate::minisyn::ItemEnum, + parse_callback_results: &ParseCallbackResults, ) -> Result>>, ConvertErrorWithContext> { - let metadata = BindgenSemanticAttributes::new_retaining_others(&mut item.attrs); - metadata.check_for_fatal_attrs(&name.name.get_final_ident())?; + check_for_fatal_attrs(parse_callback_results, &name.name)?; Ok(Box::new(std::iter::once(Api::Enum { name, item }))) } @@ -139,12 +143,12 @@ fn analyze_struct( type_converter: &mut TypeConverter, extra_apis: &mut ApiVec, name: ApiName, - mut details: Box, + details: Box, config: &IncludeCppConfig, + parse_callback_results: &ParseCallbackResults, ) -> Result>>, ConvertErrorWithContext> { let id = name.name.get_final_ident(); - let metadata = BindgenSemanticAttributes::new_retaining_others(&mut details.item.attrs); - metadata.check_for_fatal_attrs(&id)?; + check_for_fatal_attrs(parse_callback_results, &name.name)?; let bases = get_bases(&details.item); let mut field_deps = HashSet::new(); let mut field_definition_deps = HashSet::new(); diff --git a/engine/src/conversion/analysis/tdef.rs b/engine/src/conversion/analysis/tdef.rs index 1c414e8c4..b639263b3 100644 --- a/engine/src/conversion/analysis/tdef.rs +++ b/engine/src/conversion/analysis/tdef.rs @@ -16,12 +16,13 @@ use crate::{ analysis::type_converter::{add_analysis, Annotated, TypeConversionContext, TypeConverter}, api::{AnalysisPhase, Api, ApiName, NullPhase, TypedefKind}, apivec::ApiVec, + check_for_fatal_attrs, convert_error::{ConvertErrorWithContext, ErrorContext}, error_reporter::convert_apis, - parse::BindgenSemanticAttributes, ConvertErrorFromCpp, }, types::QualifiedName, + ParseCallbackResults, }; #[derive(std::fmt::Debug)] @@ -45,6 +46,7 @@ impl AnalysisPhase for TypedefPhase { pub(crate) fn convert_typedef_targets( config: &IncludeCppConfig, apis: ApiVec, + parse_callback_results: &ParseCallbackResults, ) -> ApiVec { let mut type_converter = TypeConverter::new(config, &apis); let mut extra_apis = ApiVec::new(); @@ -63,6 +65,7 @@ pub(crate) fn convert_typedef_targets( old_tyname, &mut type_converter, &mut extra_apis, + parse_callback_results, )?, TypedefKind::Use { .. } => Api::Typedef { name, @@ -86,6 +89,7 @@ fn get_replacement_typedef( old_tyname: Option, type_converter: &mut TypeConverter, extra_apis: &mut ApiVec, + parse_callback_results: &ParseCallbackResults, ) -> Result, ConvertErrorWithContext> { if !ity.generics.params.is_empty() { return Err(ConvertErrorWithContext( @@ -94,8 +98,7 @@ fn get_replacement_typedef( )); } let mut converted_type = ity.clone(); - let metadata = BindgenSemanticAttributes::new_retaining_others(&mut converted_type.attrs); - metadata.check_for_fatal_attrs(&ity.ident)?; + check_for_fatal_attrs(parse_callback_results, &name.name)?; let type_conversion_results = type_converter.convert_type( (*ity.ty).clone(), name.name.get_namespace(), diff --git a/engine/src/conversion/analysis/type_converter.rs b/engine/src/conversion/analysis/type_converter.rs index 214b021ce..2bdf87cc7 100644 --- a/engine/src/conversion/analysis/type_converter.rs +++ b/engine/src/conversion/analysis/type_converter.rs @@ -11,6 +11,7 @@ use crate::{ api::{AnalysisPhase, Api, ApiName, NullPhase, TypedefKind, UnanalyzedApi}, apivec::ApiVec, codegen_cpp::type_to_cpp::CppNameMap, + type_helpers::{unwrap_has_opaque, unwrap_has_unused_template_param, unwrap_reference}, ConvertErrorFromCpp, }, known_types::{known_types, CxxGenericType}, @@ -42,6 +43,7 @@ pub(crate) enum TypeKind { /// Results of some type conversion, annotated with a list of every type encountered, /// and optionally any extra APIs we need in order to use this type. +#[derive(Debug)] pub(crate) struct Annotated { pub(crate) ty: T, pub(crate) types_encountered: HashSet, @@ -74,14 +76,6 @@ impl Annotated { } } -/// How to interpret a pointer which we encounter during type conversion. -#[derive(Clone, Copy)] -pub(crate) enum PointerTreatment { - Pointer, - Reference, - RValueReference, -} - /// Options when converting a type. /// It's possible we could add more policies here in future. /// For example, Rust in general allows type names containing @@ -93,18 +87,10 @@ pub(crate) enum TypeConversionContext { WithinReference, WithinStructField { struct_type_params: HashSet }, WithinContainer, - OuterType { pointer_treatment: PointerTreatment }, + OuterType, } impl TypeConversionContext { - fn pointer_treatment(&self) -> PointerTreatment { - match self { - Self::WithinReference | Self::WithinContainer | Self::WithinStructField { .. } => { - PointerTreatment::Pointer - } - Self::OuterType { pointer_treatment } => *pointer_treatment, - } - } fn allow_instantiation_of_forward_declaration(&self) -> bool { matches!(self, Self::WithinReference) } @@ -165,35 +151,7 @@ impl<'a> TypeConverter<'a> { ctx: &TypeConversionContext, ) -> Result, ConvertErrorFromCpp> { let result = match ty { - Type::Path(p) => { - let newp = self.convert_type_path(p, ns, ctx)?; - if let Type::Path(newpp) = &newp.ty { - let qn = QualifiedName::from_type_path(newpp); - if !ctx.allow_instantiation_of_forward_declaration() - && self.forward_declarations.contains(&qn) - { - return Err(ConvertErrorFromCpp::TypeContainingForwardDeclaration(qn)); - } - // Special handling because rust_Str (as emitted by bindgen) - // doesn't simply get renamed to a different type _identifier_. - // This plain type-by-value (as far as bindgen is concerned) - // is actually a &str. - if known_types().should_dereference_in_cpp(&qn) { - Annotated::new( - Type::Reference(parse_quote! { - &str - }), - newp.types_encountered, - newp.extra_apis, - TypeKind::Reference, - ) - } else { - newp - } - } else { - newp - } - } + Type::Path(p) => self.convert_type_path(p, ns, ctx)?, Type::Reference(mut r) => { let innerty = self.convert_boxed_type(r.elem, ns, &TypeConversionContext::WithinReference)?; @@ -216,7 +174,7 @@ impl<'a> TypeConverter<'a> { TypeKind::Regular, ) } - Type::Ptr(ptr) => self.convert_ptr(ptr, ns, ctx.pointer_treatment())?, + Type::Ptr(ptr) => self.convert_ptr(ptr, ns)?, _ => { return Err(ConvertErrorFromCpp::UnknownType( ty.to_token_stream().to_string(), @@ -227,6 +185,92 @@ impl<'a> TypeConverter<'a> { } fn convert_type_path( + &mut self, + typ: TypePath, + ns: &Namespace, + ctx: &TypeConversionContext, + ) -> Result, ConvertErrorFromCpp> { + // First we try to spot if these are the special marker paths that + // bindgen uses to denote references or other things. + if let Some(ty) = unwrap_has_unused_template_param(&typ) { + self.convert_type(ty.clone(), ns, ctx) + } else if let Some(ty) = unwrap_has_opaque(&typ) { + self.convert_type(ty.clone(), ns, ctx) + } else if let Some(ptr) = unwrap_reference(&typ, false) { + // LValue reference + let mutability = ptr.mutability; + let elem = self.convert_boxed_type( + ptr.elem.clone(), + ns, + &TypeConversionContext::WithinReference, + )?; + // TODO - in the future, we should check if this is a rust::Str and throw + // a wobbler if not. rust::Str should only be seen _by value_ in C++ + // headers; it manifests as &str in Rust but on the C++ side it must + // be a plain value. We should detect and abort. + let mut outer = elem.map(|elem| match mutability { + Some(_) => Type::Path(parse_quote! { + ::core::pin::Pin < & #mutability #elem > + }), + None => Type::Reference(parse_quote! { + & #elem + }), + }); + outer.kind = if mutability.is_some() { + TypeKind::MutableReference + } else { + TypeKind::Reference + }; + Ok(outer) + } else if let Some(ptr) = unwrap_reference(&typ, true) { + // RValue reference + Self::ensure_pointee_is_valid(ptr)?; + let innerty = self.convert_boxed_type( + ptr.elem.clone(), + ns, + &TypeConversionContext::WithinReference, + )?; + let mut ptr = ptr.clone(); + ptr.elem = innerty.ty; + Ok(Annotated::new( + Type::Ptr(ptr), + innerty.types_encountered, + innerty.extra_apis, + TypeKind::RValueReference, + )) + } else { + // An actual path + let newp = self.convert_type_path_which_is_not_a_reference(typ, ns, ctx)?; + if let Type::Path(newpp) = &newp.ty { + let qn = QualifiedName::from_type_path(newpp); + if !ctx.allow_instantiation_of_forward_declaration() + && self.forward_declarations.contains(&qn) + { + return Err(ConvertErrorFromCpp::TypeContainingForwardDeclaration(qn)); + } + // Special handling because rust_Str (as emitted by bindgen) + // doesn't simply get renamed to a different type _identifier_. + // This plain type-by-value (as far as bindgen is concerned) + // is actually a &str. + if known_types().should_dereference_in_cpp(&qn) { + Ok(Annotated::new( + Type::Reference(parse_quote! { + &str + }), + newp.types_encountered, + newp.extra_apis, + TypeKind::Reference, + )) + } else { + Ok(newp) + } + } else { + Ok(newp) + } + } + } + + fn convert_type_path_which_is_not_a_reference( &mut self, mut typ: TypePath, ns: &Namespace, @@ -458,57 +502,17 @@ impl<'a> TypeConverter<'a> { &mut self, mut ptr: TypePtr, ns: &Namespace, - pointer_treatment: PointerTreatment, ) -> Result, ConvertErrorFromCpp> { - match pointer_treatment { - PointerTreatment::Pointer => { - Self::ensure_pointee_is_valid(&ptr)?; - let innerty = - self.convert_boxed_type(ptr.elem, ns, &TypeConversionContext::WithinReference)?; - ptr.elem = innerty.ty; - Ok(Annotated::new( - Type::Ptr(ptr), - innerty.types_encountered, - innerty.extra_apis, - TypeKind::Pointer, - )) - } - PointerTreatment::Reference => { - let mutability = ptr.mutability; - let elem = - self.convert_boxed_type(ptr.elem, ns, &TypeConversionContext::WithinReference)?; - // TODO - in the future, we should check if this is a rust::Str and throw - // a wobbler if not. rust::Str should only be seen _by value_ in C++ - // headers; it manifests as &str in Rust but on the C++ side it must - // be a plain value. We should detect and abort. - let mut outer = elem.map(|elem| match mutability { - Some(_) => Type::Path(parse_quote! { - ::core::pin::Pin < & #mutability #elem > - }), - None => Type::Reference(parse_quote! { - & #elem - }), - }); - outer.kind = if mutability.is_some() { - TypeKind::MutableReference - } else { - TypeKind::Reference - }; - Ok(outer) - } - PointerTreatment::RValueReference => { - Self::ensure_pointee_is_valid(&ptr)?; - let innerty = - self.convert_boxed_type(ptr.elem, ns, &TypeConversionContext::WithinReference)?; - ptr.elem = innerty.ty; - Ok(Annotated::new( - Type::Ptr(ptr), - innerty.types_encountered, - innerty.extra_apis, - TypeKind::RValueReference, - )) - } - } + Self::ensure_pointee_is_valid(&ptr)?; + let innerty = + self.convert_boxed_type(ptr.elem, ns, &TypeConversionContext::WithinReference)?; + ptr.elem = innerty.ty; + Ok(Annotated::new( + Type::Ptr(ptr), + innerty.types_encountered, + innerty.extra_apis, + TypeKind::Pointer, + )) } fn ensure_pointee_is_valid(ptr: &TypePtr) -> Result<(), ConvertErrorFromCpp> { diff --git a/engine/src/conversion/api.rs b/engine/src/conversion/api.rs index 16b0319ea..05d947f2a 100644 --- a/engine/src/conversion/api.rs +++ b/engine/src/conversion/api.rs @@ -6,34 +6,33 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use indexmap::set::IndexSet as HashSet; -use std::fmt::Display; +use autocxx_bindgen::callbacks::{Explicitness, Layout, SpecialMemberKind, Virtualness}; use syn::{ - parse::Parse, punctuated::Punctuated, token::{Comma, Unsafe}, }; -use crate::minisyn::{ - Attribute, FnArg, Ident, ItemConst, ItemEnum, ItemStruct, ItemType, ItemUse, LitBool, LitInt, - Pat, ReturnType, Type, Visibility, -}; use crate::types::{make_ident, Namespace, QualifiedName}; +use crate::{ + minisyn::{ + Attribute, FnArg, Ident, ItemConst, ItemEnum, ItemStruct, ItemType, ItemUse, Pat, + ReturnType, Type, Visibility, + }, + parse_callbacks::CppOriginalName, +}; use autocxx_parser::{ExternCppType, RustFun, RustPath}; use itertools::Itertools; use quote::ToTokens; +pub(crate) use autocxx_bindgen::callbacks::Visibility as CppVisibility; + use super::{ - analysis::{ - fun::{ - function_wrapper::{CppFunction, CppFunctionBody, CppFunctionKind}, - ReceiverMutability, - }, - PointerTreatment, + analysis::fun::{ + function_wrapper::{CppFunction, CppFunctionBody, CppFunctionKind}, + ReceiverMutability, }, convert_error::{ConvertErrorWithContext, ErrorContext}, - parse::CppOriginalName, ConvertErrorFromCpp, CppEffectiveName, }; @@ -48,14 +47,6 @@ pub(crate) enum TypeKind { // in which case we'll err on the side of caution. } -/// C++ visibility. -#[derive(Debug, Clone, PartialEq, Eq, Copy)] -pub(crate) enum CppVisibility { - Public, - Protected, - Private, -} - /// Details about a C++ struct. #[derive(Debug)] pub(crate) struct StructDetails { @@ -64,39 +55,6 @@ pub(crate) struct StructDetails { pub(crate) has_rvalue_reference_fields: bool, } -/// Layout of a type, equivalent to the same type in ir/layout.rs in bindgen -#[derive(Clone, Debug)] -pub(crate) struct Layout { - /// The size (in bytes) of this layout. - pub(crate) size: usize, - /// The alignment (in bytes) of this layout. - pub(crate) align: usize, - /// Whether this layout's members are packed or not. - pub(crate) packed: bool, -} - -impl Parse for Layout { - fn parse(input: syn::parse::ParseStream) -> syn::Result { - let size: LitInt = input.parse()?; - input.parse::()?; - let align: LitInt = input.parse()?; - input.parse::()?; - let packed: LitBool = input.parse()?; - Ok(Layout { - size: size.base10_parse().unwrap(), - align: align.base10_parse().unwrap(), - packed: packed.value(), - }) - } -} - -#[derive(Clone, Debug)] -pub(crate) enum Virtualness { - None, - Virtual, - PureVirtual, -} - #[derive(Clone, Copy, Debug)] pub(crate) enum CastMutability { ConstToConst, @@ -141,45 +99,6 @@ pub(crate) struct SuperclassMethod { pub(crate) is_pure_virtual: bool, } -/// Information about references (as opposed to pointers) to be found -/// within the function signature. This is derived from bindgen annotations -/// which is why it's not within `FuncToConvert::inputs` -#[derive(Default, Clone, Debug)] -pub(crate) struct References { - pub(crate) rvalue_ref_params: HashSet, - pub(crate) ref_params: HashSet, - pub(crate) ref_return: bool, - pub(crate) rvalue_ref_return: bool, -} - -impl References { - pub(crate) fn new_with_this_and_return_as_reference() -> Self { - Self { - ref_return: true, - ref_params: [make_ident("this")].into_iter().collect(), - ..Default::default() - } - } - pub(crate) fn param_treatment(&self, param: &Ident) -> PointerTreatment { - if self.rvalue_ref_params.contains(param) { - PointerTreatment::RValueReference - } else if self.ref_params.contains(param) { - PointerTreatment::Reference - } else { - PointerTreatment::Pointer - } - } - pub(crate) fn return_treatment(&self) -> PointerTreatment { - if self.rvalue_ref_return { - PointerTreatment::RValueReference - } else if self.ref_return { - PointerTreatment::Reference - } else { - PointerTreatment::Pointer - } - } -} - #[derive(Clone, Debug)] pub(crate) struct TraitImplSignature { pub(crate) ty: Type, @@ -219,31 +138,6 @@ impl std::hash::Hash for TraitImplSignature { } } -#[derive(Clone, Debug)] -pub(crate) enum SpecialMemberKind { - DefaultConstructor, - CopyConstructor, - MoveConstructor, - Destructor, - AssignmentOperator, -} - -impl Display for SpecialMemberKind { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - write!( - f, - "{}", - match self { - SpecialMemberKind::DefaultConstructor => "default constructor", - SpecialMemberKind::CopyConstructor => "copy constructor", - SpecialMemberKind::MoveConstructor => "move constructor", - SpecialMemberKind::Destructor => "destructor", - SpecialMemberKind::AssignmentOperator => "assignment operator", - } - ) - } -} - #[derive(Clone, Debug)] pub(crate) enum Provenance { Bindgen, @@ -251,14 +145,6 @@ pub(crate) enum Provenance { SynthesizedSubclassConstructor(Box), } -/// Whether a function has =delete or =default -#[derive(Clone, Copy, Debug)] -pub(crate) enum DeletedOrDefaulted { - Neither, - Deleted, - Defaulted, -} - /// A C++ function for which we need to generate bindings, but haven't /// yet analyzed in depth. This is little more than a `ForeignItemFn` /// broken down into its constituent parts, plus some metadata from the @@ -278,11 +164,9 @@ pub(crate) struct FuncToConvert { pub(crate) variadic: bool, pub(crate) output: ReturnType, pub(crate) vis: Visibility, - pub(crate) virtualness: Virtualness, + pub(crate) virtualness: Option, pub(crate) cpp_vis: CppVisibility, pub(crate) special_member: Option, - pub(crate) unused_template_param: bool, - pub(crate) references: References, pub(crate) original_name: Option, /// Used for static functions only. For all other functons, /// this is figured out from the receiver type in the inputs. @@ -296,8 +180,8 @@ pub(crate) struct FuncToConvert { /// If Some, this function didn't really exist in the original /// C++ and instead we're synthesizing it. pub(crate) synthetic_cpp: Option<(CppFunctionBody, CppFunctionKind)>, - /// =delete - pub(crate) is_deleted: DeletedOrDefaulted, + /// =delete or =default + pub(crate) is_deleted: Option, } /// Layers of analysis which may be applied to decorate each API. diff --git a/engine/src/conversion/apivec.rs b/engine/src/conversion/apivec.rs index 24e70ed7a..e1bb87500 100644 --- a/engine/src/conversion/apivec.rs +++ b/engine/src/conversion/apivec.rs @@ -42,6 +42,7 @@ use super::api::{AnalysisPhase, Api}; /// of APIs within an existing `ApiVec`. But it's extremely important that /// the naming-uniqueness-invariant remains, so any such mutation should /// allow mutation only of other fields, not the name. +#[derive(Debug)] pub(crate) struct ApiVec { apis: Vec>, names: HashSet, diff --git a/engine/src/conversion/codegen_cpp/type_to_cpp.rs b/engine/src/conversion/codegen_cpp/type_to_cpp.rs index 361d54933..a5ff046e0 100644 --- a/engine/src/conversion/codegen_cpp/type_to_cpp.rs +++ b/engine/src/conversion/codegen_cpp/type_to_cpp.rs @@ -7,7 +7,8 @@ // except according to those terms. use crate::{ - conversion::{apivec::ApiVec, AnalysisPhase, ConvertErrorFromCpp, CppOriginalName}, + conversion::{apivec::ApiVec, AnalysisPhase, ConvertErrorFromCpp}, + parse_callbacks::CppOriginalName, types::QualifiedName, }; use indexmap::map::IndexMap as HashMap; diff --git a/engine/src/conversion/codegen_rs/mod.rs b/engine/src/conversion/codegen_rs/mod.rs index eb2ea2a1f..f9437625b 100644 --- a/engine/src/conversion/codegen_rs/mod.rs +++ b/engine/src/conversion/codegen_rs/mod.rs @@ -14,6 +14,7 @@ mod namespace_organizer; mod non_pod_struct; pub(crate) mod unqualify; +use autocxx_bindgen::callbacks::Layout; use indexmap::map::IndexMap as HashMap; use indexmap::set::IndexSet as HashSet; @@ -51,7 +52,7 @@ use super::{ doc_attr::get_doc_attrs, }; use super::{ - api::{Layout, Provenance, RustSubclassFnDetails, SuperclassMethod, TraitImplSignature}, + api::{Provenance, RustSubclassFnDetails, SuperclassMethod, TraitImplSignature}, apivec::ApiVec, codegen_cpp::type_to_cpp::CppNameMap, }; @@ -508,7 +509,7 @@ impl<'a> RsCodeGenerator<'a> { .. } => { let doc_attrs = get_doc_attrs(&details.item.attrs); - let layout = details.layout.clone(); + let layout = details.layout; self.generate_type( &name, id, diff --git a/engine/src/conversion/codegen_rs/non_pod_struct.rs b/engine/src/conversion/codegen_rs/non_pod_struct.rs index 25b4861a5..0a179890e 100644 --- a/engine/src/conversion/codegen_rs/non_pod_struct.rs +++ b/engine/src/conversion/codegen_rs/non_pod_struct.rs @@ -6,8 +6,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use crate::conversion::api::Layout; use crate::types::make_ident; +use autocxx_bindgen::callbacks::Layout; use proc_macro2::{Ident, Span}; use quote::quote; use syn::parse::Parser; diff --git a/engine/src/conversion/conversion_tests.rs b/engine/src/conversion/conversion_tests.rs index e74888e8e..6fa3751a7 100644 --- a/engine/src/conversion/conversion_tests.rs +++ b/engine/src/conversion/conversion_tests.rs @@ -11,7 +11,7 @@ use autocxx_parser::UnsafePolicy; use syn::parse_quote; use syn::ItemMod; -use crate::CodegenOptions; +use crate::{CodegenOptions, ParseCallbackResults}; use super::BridgeConverter; @@ -29,8 +29,10 @@ fn do_test(input: ItemMod) { let tc = parse_quote! {}; let bc = BridgeConverter::new(&[], &tc); let inclusions = "".into(); + let parse_callback_results = ParseCallbackResults::default(); bc.convert( input, + parse_callback_results, UnsafePolicy::AllFunctionsSafe, inclusions, &CodegenOptions::default(), diff --git a/engine/src/conversion/mod.rs b/engine/src/conversion/mod.rs index 61308290a..9428e93cd 100644 --- a/engine/src/conversion/mod.rs +++ b/engine/src/conversion/mod.rs @@ -20,16 +20,19 @@ mod parse; mod type_helpers; mod utilities; -pub(crate) use crate::conversion::parse::CppOriginalName; +pub(crate) use super::parse_callbacks::CppOriginalName; use analysis::fun::FnAnalyzer; +use autocxx_bindgen::callbacks::Visibility as CppVisibility; use autocxx_parser::IncludeCppConfig; pub(crate) use codegen_cpp::CppCodeGenerator; pub(crate) use convert_error::ConvertError; -use convert_error::ConvertErrorFromCpp; +use convert_error::{ConvertErrorFromCpp, ConvertErrorWithContext, ErrorContext}; use itertools::Itertools; use syn::{Item, ItemMod}; -use crate::{types::QualifiedName, CodegenOptions, CppFilePair, UnsafePolicy}; +use crate::{ + types::QualifiedName, CodegenOptions, CppFilePair, ParseCallbackResults, UnsafePolicy, +}; use self::{ analysis::{ @@ -106,6 +109,7 @@ impl<'a> BridgeConverter<'a> { pub(crate) fn convert( &self, mut bindgen_mod: ItemMod, + parse_callback_results: ParseCallbackResults, unsafe_policy: UnsafePolicy, inclusions: String, codegen_options: &CodegenOptions, @@ -116,7 +120,7 @@ impl<'a> BridgeConverter<'a> { Some((_, items)) => { // Parse the bindgen mod. let items_to_process = std::mem::take(items); - let parser = ParseBindgen::new(self.config); + let parser = ParseBindgen::new(self.config, &parse_callback_results); let apis = parser.parse_items(items_to_process, source_file_contents)?; Self::dump_apis("parsing", &apis); // Inside parse_results, we now have a list of APIs. @@ -124,7 +128,7 @@ impl<'a> BridgeConverter<'a> { // First, convert any typedefs. // "Convert" means replacing bindgen-style type targets // (e.g. root::std::unique_ptr) with cxx-style targets (e.g. UniquePtr). - let apis = convert_typedef_targets(self.config, apis); + let apis = convert_typedef_targets(self.config, apis, &parse_callback_results); Self::dump_apis("typedefs", &apis); // Now analyze which of them can be POD (i.e. trivial, movable, pass-by-value // versus which need to be opaque). @@ -132,8 +136,8 @@ impl<'a> BridgeConverter<'a> { // POD really are POD, and duly mark any dependent types. // This returns a new list of `Api`s, which will be parameterized with // the analysis results. - let analyzed_apis = - analyze_pod_apis(apis, self.config).map_err(ConvertError::Cpp)?; + let analyzed_apis = analyze_pod_apis(apis, self.config, &parse_callback_results) + .map_err(ConvertError::Cpp)?; Self::dump_apis("pod analysis", &analyzed_apis); let analyzed_apis = replace_hopeless_typedef_targets(self.config, analyzed_apis); let analyzed_apis = add_casts(analyzed_apis); @@ -225,7 +229,7 @@ impl<'a> BridgeConverter<'a> { /// remove them, or make them safe by doing name validation at the point /// of conversion. #[derive(PartialEq, PartialOrd, Eq, Hash, Clone, Debug)] -pub struct CppEffectiveName(String); +pub struct CppEffectiveName(pub(crate) String); impl CppEffectiveName { /// FIXME: document what we're doing here, just as soon as I've figured /// it out @@ -273,3 +277,26 @@ impl CppEffectiveName { self.0.contains("::") } } + +/// Some attributes indicate we can never handle a given item. Check for those. +fn check_for_fatal_attrs( + callback_results: &ParseCallbackResults, + name: &QualifiedName, +) -> Result<(), ConvertErrorWithContext> { + if callback_results.discards_template_param(name) { + Err(ConvertErrorWithContext( + ConvertErrorFromCpp::UnusedTemplateParam, + Some(ErrorContext::new_for_item(name.get_final_ident())), + )) + } else if !matches!( + callback_results.get_cpp_visibility(name), + CppVisibility::Public + ) { + Err(ConvertErrorWithContext( + ConvertErrorFromCpp::NonPublicNestedType, + Some(ErrorContext::new_for_item(name.get_final_ident())), + )) + } else { + Ok(()) + } +} diff --git a/engine/src/conversion/parse/bindgen_semantic_attributes.rs b/engine/src/conversion/parse/bindgen_semantic_attributes.rs deleted file mode 100644 index bf41eefd8..000000000 --- a/engine/src/conversion/parse/bindgen_semantic_attributes.rs +++ /dev/null @@ -1,301 +0,0 @@ -// Copyright 2022 Google LLC -// -// Licensed under the Apache License, Version 2.0 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use std::fmt::Display; - -use proc_macro2::{Ident, TokenStream}; -use quote::quote; -use syn::{ - parenthesized, - parse::{Parse, Parser}, - Attribute, LitStr, -}; - -use crate::{ - conversion::{ - api::{ - CppVisibility, DeletedOrDefaulted, Layout, References, SpecialMemberKind, Virtualness, - }, - convert_error::{ConvertErrorWithContext, ErrorContext}, - ConvertErrorFromCpp, CppEffectiveName, - }, - types::QualifiedName, -}; - -/// Newtype wrapper for a C++ "original name"; that is, an annotation -/// derived from bindgen that this is the original name of the C++ item. -/// -/// At present these various newtype wrappers for kinds of names -/// (Rust, C++, cxx::bridge) have various conversions between them that -/// are probably not safe. They're marked with FIXMEs. Over time we should -/// remove them, or make them safe by doing name validation at the point -/// of conversion. -#[derive(PartialEq, PartialOrd, Eq, Hash, Clone, Debug)] -pub struct CppOriginalName(String); - -impl Display for CppOriginalName { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.write_str(&self.0) - } -} - -impl CppOriginalName { - pub(crate) fn is_nested(&self) -> bool { - self.0.contains("::") - } - - pub(crate) fn from_final_item_of_pre_existing_qualified_name(name: &QualifiedName) -> Self { - Self(name.get_final_item().to_string()) - } - - pub(crate) fn to_qualified_name(&self) -> QualifiedName { - QualifiedName::new_from_cpp_name(&self.0) - } - - pub(crate) fn to_effective_name(&self) -> CppEffectiveName { - CppEffectiveName(self.0.clone()) - } - - /// This is the main output of this type; it's fed into a mapping - /// from to - /// ; this contributes "inner". - pub(crate) fn for_original_name_map(&self) -> &str { - &self.0 - } - - /// Used to give the final part of the name which can be used - /// to figure out the name for constructors, destructors etc. - pub(crate) fn get_final_segment_for_special_members(&self) -> Option<&str> { - self.0.rsplit_once("::").map(|(_, suffix)| suffix) - } - - pub(crate) fn from_type_name_for_constructor(name: String) -> Self { - Self(name) - } - - /// Work out what to call a Rust-side API given a C++-side name. - pub(crate) fn to_string_for_rust_name(&self) -> String { - self.0.clone() - } - - /// Return the string inside for validation purposes. - pub(crate) fn for_validation(&self) -> &str { - &self.0 - } - - /// Used for diagnostics early in function analysis before we establish - /// the correct naming. - pub(crate) fn diagnostic_display_name(&self) -> &String { - &self.0 - } - - // FIXME - remove - pub(crate) fn from_rust_name(string: String) -> Self { - Self(string) - } - - /// Determines whether we need to generate a cxxbridge::name attribute - pub(crate) fn does_not_match_cxxbridge_name( - &self, - cxxbridge_name: &crate::minisyn::Ident, - ) -> bool { - cxxbridge_name.0 != self.0 - } - - pub(crate) fn generate_cxxbridge_name_attribute(&self) -> proc_macro2::TokenStream { - let cpp_call_name = &self.to_string_for_rust_name(); - quote!( - #[cxx_name = #cpp_call_name] - ) - } -} - -/// The set of all annotations that autocxx_bindgen has added -/// for our benefit. -#[derive(Debug)] -pub(crate) struct BindgenSemanticAttributes(Vec); - -impl BindgenSemanticAttributes { - // Remove `bindgen_` attributes. They don't have a corresponding macro defined anywhere, - // so they will cause compilation errors if we leave them in. - // We may return an error if one of the bindgen attributes shows that the - // item can't be processed. - pub(crate) fn new_retaining_others(attrs: &mut Vec) -> Self { - let metadata = Self::new(attrs); - attrs.retain(|a| a.path().segments.last().unwrap().ident != "cpp_semantics"); - metadata - } - - pub(crate) fn new(attrs: &[Attribute]) -> Self { - Self( - attrs - .iter() - .filter_map(|attr| { - if attr.path().segments.last().unwrap().ident == "cpp_semantics" { - let r: Result = attr.parse_args(); - r.ok() - } else { - None - } - }) - .collect(), - ) - } - - /// Some attributes indicate we can never handle a given item. Check for those. - pub(crate) fn check_for_fatal_attrs( - &self, - id_for_context: &Ident, - ) -> Result<(), ConvertErrorWithContext> { - if self.has_attr("unused_template_param") { - Err(ConvertErrorWithContext( - ConvertErrorFromCpp::UnusedTemplateParam, - Some(ErrorContext::new_for_item(id_for_context.clone().into())), - )) - } else if self.get_cpp_visibility() != CppVisibility::Public { - Err(ConvertErrorWithContext( - ConvertErrorFromCpp::NonPublicNestedType, - Some(ErrorContext::new_for_item(id_for_context.clone().into())), - )) - } else { - Ok(()) - } - } - - /// Whether the given attribute is present. - pub(super) fn has_attr(&self, attr_name: &str) -> bool { - self.0.iter().any(|a| a.is_ident(attr_name)) - } - - /// The C++ visibility of the item. - pub(super) fn get_cpp_visibility(&self) -> CppVisibility { - if self.has_attr("visibility_private") { - CppVisibility::Private - } else if self.has_attr("visibility_protected") { - CppVisibility::Protected - } else { - CppVisibility::Public - } - } - - /// Whether the item is virtual. - pub(super) fn get_virtualness(&self) -> Virtualness { - if self.has_attr("pure_virtual") { - Virtualness::PureVirtual - } else if self.has_attr("bindgen_virtual") { - Virtualness::Virtual - } else { - Virtualness::None - } - } - - pub(super) fn get_deleted_or_defaulted(&self) -> DeletedOrDefaulted { - if self.has_attr("deleted") { - DeletedOrDefaulted::Deleted - } else if self.has_attr("defaulted") { - DeletedOrDefaulted::Defaulted - } else { - DeletedOrDefaulted::Neither - } - } - - fn parse_if_present(&self, annotation: &str) -> Option { - self.0 - .iter() - .find(|a| a.is_ident(annotation)) - .map(|a| a.parse_args().unwrap()) - } - - fn string_if_present(&self, annotation: &str) -> Option { - let ls: Option = self.parse_if_present(annotation); - ls.map(|ls| ls.value()) - } - - /// The in-memory layout of the item. - pub(super) fn get_layout(&self) -> Option { - self.parse_if_present("layout") - } - - /// The original C++ name, which bindgen may have changed. - pub(super) fn get_original_name(&self) -> Option { - self.string_if_present("original_name").map(CppOriginalName) - } - - /// Whether this is a move constructor or other special member. - pub(super) fn special_member_kind(&self) -> Option { - self.string_if_present("special_member") - .map(|kind| match kind.as_str() { - "default_ctor" => SpecialMemberKind::DefaultConstructor, - "copy_ctor" => SpecialMemberKind::CopyConstructor, - "move_ctor" => SpecialMemberKind::MoveConstructor, - "dtor" => SpecialMemberKind::Destructor, - "assignment_operator" => SpecialMemberKind::AssignmentOperator, - _ => panic!("unexpected special_member_kind"), - }) - } - - /// Any reference parameters or return values. - pub(super) fn get_reference_parameters_and_return(&self) -> References { - let mut results = References::default(); - for a in &self.0 { - if a.is_ident("ret_type_reference") { - results.ref_return = true; - } else if a.is_ident("ret_type_rvalue_reference") { - results.rvalue_ref_return = true; - } else if a.is_ident("arg_type_reference") { - let r: Result = a.parse_args(); - if let Ok(ls) = r { - results.ref_params.insert(ls.into()); - } - } else if a.is_ident("arg_type_rvalue_reference") { - let r: Result = a.parse_args(); - if let Ok(ls) = r { - results.rvalue_ref_params.insert(ls.into()); - } - } - } - results - } -} - -#[derive(Debug)] -struct BindgenSemanticAttribute { - annotation_name: Ident, - body: Option, -} - -impl BindgenSemanticAttribute { - fn is_ident(&self, name: &str) -> bool { - self.annotation_name == name - } - - fn parse_args(&self) -> Result { - T::parse.parse2(self.body.as_ref().unwrap().clone()) - } -} - -impl Parse for BindgenSemanticAttribute { - fn parse(input: syn::parse::ParseStream) -> syn::Result { - let annotation_name: Ident = input.parse()?; - if input.peek(syn::token::Paren) { - let body_contents; - parenthesized!(body_contents in input); - Ok(Self { - annotation_name, - body: Some(body_contents.parse()?), - }) - } else if !input.is_empty() { - Err(input.error("expected nothing")) - } else { - Ok(Self { - annotation_name, - body: None, - }) - } - } -} diff --git a/engine/src/conversion/parse/mod.rs b/engine/src/conversion/parse/mod.rs index 85da963f6..b12eef839 100644 --- a/engine/src/conversion/parse/mod.rs +++ b/engine/src/conversion/parse/mod.rs @@ -6,10 +6,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -mod bindgen_semantic_attributes; mod extern_fun_signatures; mod parse_bindgen; mod parse_foreign_mod; -pub(crate) use bindgen_semantic_attributes::{BindgenSemanticAttributes, CppOriginalName}; pub(crate) use parse_bindgen::ParseBindgen; diff --git a/engine/src/conversion/parse/parse_bindgen.rs b/engine/src/conversion/parse/parse_bindgen.rs index ade92bb86..93fc60502 100644 --- a/engine/src/conversion/parse/parse_bindgen.rs +++ b/engine/src/conversion/parse/parse_bindgen.rs @@ -13,11 +13,15 @@ use crate::{ conversion::{ api::{Api, ApiName, NullPhase, StructDetails, SubclassName, TypedefKind, UnanalyzedApi}, apivec::ApiVec, + check_for_fatal_attrs, convert_error::LocatedConvertErrorFromRust, + type_helpers::type_is_reference, + utilities::generate_utilities, ConvertError, ConvertErrorFromCpp, }, - types::Namespace, - types::QualifiedName, + minisyn, + types::{Namespace, QualifiedName}, + ParseCallbackResults, }; use crate::{ conversion::{ @@ -29,26 +33,25 @@ use crate::{ use autocxx_parser::{IncludeCppConfig, RustPath}; use syn::{parse_quote, Fields, Ident, Item, Type, TypePath, UseTree}; -use super::{ - super::utilities::generate_utilities, bindgen_semantic_attributes::BindgenSemanticAttributes, -}; - use super::parse_foreign_mod::ParseForeignMod; /// Parses a bindgen mod in order to understand the APIs within it. pub(crate) struct ParseBindgen<'a> { config: &'a IncludeCppConfig, apis: ApiVec, + parse_callback_results: &'a ParseCallbackResults, } -fn api_name(ns: &Namespace, id: Ident, attrs: &BindgenSemanticAttributes) -> ApiName { - ApiName::new_with_cpp_name(ns, id.into(), attrs.get_original_name()) +fn api_name(ns: &Namespace, id: Ident, callback_results: &ParseCallbackResults) -> ApiName { + let qn = QualifiedName::new(ns, minisyn::Ident(id.clone())); + // TODO FIXME squash reduncancy + ApiName::new_with_cpp_name(ns, id.into(), callback_results.get_original_name(&qn)) } pub(crate) fn api_name_qualified( ns: &Namespace, id: Ident, - attrs: &BindgenSemanticAttributes, + callback_results: &ParseCallbackResults, ) -> Result { match validate_ident_ok_for_cxx(&id.to_string()) { Err(e) => { @@ -58,15 +61,19 @@ pub(crate) fn api_name_qualified( Some(ctx), )) } - Ok(..) => Ok(api_name(ns, id, attrs)), + Ok(..) => Ok(api_name(ns, id, callback_results)), } } impl<'a> ParseBindgen<'a> { - pub(crate) fn new(config: &'a IncludeCppConfig) -> Self { + pub(crate) fn new( + config: &'a IncludeCppConfig, + parse_callback_results: &'a ParseCallbackResults, + ) -> Self { ParseBindgen { config, apis: ApiVec::new(), + parse_callback_results, } } @@ -188,7 +195,9 @@ impl<'a> ParseBindgen<'a> { fn parse_mod_items(&mut self, items: Vec, ns: Namespace) { // This object maintains some state specific to this namespace, i.e. // this particular mod. - let mut mod_converter = ParseForeignMod::new(ns.clone()); + // TODO - ideally we'd get rid of this clone + let parse_callback_results = self.parse_callback_results.clone(); + let mut mod_converter = ParseForeignMod::new(ns.clone(), &parse_callback_results); let mut more_apis = ApiVec::new(); for item in items { report_any_error(&ns, &mut more_apis, || { @@ -214,11 +223,10 @@ impl<'a> ParseBindgen<'a> { if s.ident.to_string().ends_with("__bindgen_vtable") { return Ok(()); } - let annotations = BindgenSemanticAttributes::new(&s.attrs); // cxx::bridge can't cope with type aliases to generic // types at the moment. - let name = api_name_qualified(ns, s.ident.clone(), &annotations)?; - let mut err = annotations.check_for_fatal_attrs(&s.ident).err(); + let name = api_name_qualified(ns, s.ident.clone(), self.parse_callback_results)?; + let mut err = check_for_fatal_attrs(self.parse_callback_results, &name.name).err(); let api = if ns.is_empty() && self.config.is_rust_type(&s.ident) { None } else if Self::spot_forward_declaration(&s.fields) @@ -241,13 +249,12 @@ impl<'a> ParseBindgen<'a> { } Some(UnanalyzedApi::ForwardDeclaration { name, err }) } else { - let has_rvalue_reference_fields = s.fields.iter().any(|f| { - BindgenSemanticAttributes::new(&f.attrs).has_attr("rvalue_reference") - }); + let has_rvalue_reference_fields = Self::spot_rvalue_reference_fields(&s.fields); + let layout = self.parse_callback_results.get_layout(&name.name); Some(UnanalyzedApi::Struct { name, details: Box::new(StructDetails { - layout: annotations.get_layout(), + layout, item: s.into(), has_rvalue_reference_fields, }), @@ -262,9 +269,8 @@ impl<'a> ParseBindgen<'a> { Ok(()) } Item::Enum(e) => { - let annotations = BindgenSemanticAttributes::new(&e.attrs); let api = UnanalyzedApi::Enum { - name: api_name_qualified(ns, e.ident.clone(), &annotations)?, + name: api_name_qualified(ns, e.ident.clone(), self.parse_callback_results)?, item: e.into(), }; if !self.config.is_on_blocklist(&api.name().to_cpp_name()) { @@ -324,9 +330,8 @@ impl<'a> ParseBindgen<'a> { Some(ErrorContext::new_for_item(new_id.clone().into())), )); } - let annotations = BindgenSemanticAttributes::new(&use_item.attrs); self.apis.push(UnanalyzedApi::Typedef { - name: api_name(ns, new_id.clone(), &annotations), + name: api_name(ns, new_id.clone(), self.parse_callback_results), item: TypedefKind::Use( parse_quote! { pub use #old_path as #new_id; @@ -351,7 +356,6 @@ impl<'a> ParseBindgen<'a> { Ok(()) } Item::Const(const_item) => { - let annotations = BindgenSemanticAttributes::new(&const_item.attrs); // Bindgen generates const expressions for nested unnamed enums, // but autcxx will refuse to expand those enums, making these consts // invalid. @@ -365,18 +369,17 @@ impl<'a> ParseBindgen<'a> { } if enum_type_name_valid { self.apis.push(UnanalyzedApi::Const { - name: api_name(ns, const_item.ident.clone(), &annotations), + name: api_name(ns, const_item.ident.clone(), self.parse_callback_results), const_item: const_item.into(), }); } Ok(()) } Item::Type(ity) => { - let annotations = BindgenSemanticAttributes::new(&ity.attrs); // It's known that sometimes bindgen will give us duplicate typedefs with the // same name - see test_issue_264. self.apis.push(UnanalyzedApi::Typedef { - name: api_name(ns, ity.ident.clone(), &annotations), + name: api_name(ns, ity.ident.clone(), self.parse_callback_results), item: TypedefKind::Type(ity.into()), old_tyname: None, analysis: (), @@ -404,6 +407,10 @@ impl<'a> ParseBindgen<'a> { .any(|id| id == desired_id) } + fn spot_rvalue_reference_fields(s: &Fields) -> bool { + s.iter().any(|f| type_is_reference(&f.ty, true)) + } + fn confirm_all_generate_directives_obeyed(&self) -> Result<(), ConvertErrorFromCpp> { let api_names: HashSet<_> = self .apis diff --git a/engine/src/conversion/parse/parse_foreign_mod.rs b/engine/src/conversion/parse/parse_foreign_mod.rs index ea82ff8f5..839534eb4 100644 --- a/engine/src/conversion/parse/parse_foreign_mod.rs +++ b/engine/src/conversion/parse/parse_foreign_mod.rs @@ -16,6 +16,7 @@ use crate::conversion::{ convert_error::ErrorContext, }; use crate::minisyn::{minisynize_punctuated, minisynize_vec}; +use crate::ParseCallbackResults; use crate::{ conversion::ConvertErrorFromCpp, types::{Namespace, QualifiedName}, @@ -23,12 +24,10 @@ use crate::{ use std::collections::HashMap; use syn::{Block, Expr, ExprCall, ForeignItem, Ident, ImplItem, ItemImpl, Stmt, Type}; -use super::bindgen_semantic_attributes::BindgenSemanticAttributes; - /// Parses a given bindgen-generated 'mod' into suitable /// [Api]s. In bindgen output, a given mod concerns /// a specific C++ namespace. -pub(crate) struct ParseForeignMod { +pub(crate) struct ParseForeignMod<'a> { ns: Namespace, // We mostly act upon the functions we see within the 'extern "C"' // block of bindgen output, but we can't actually do this until @@ -41,15 +40,17 @@ pub(crate) struct ParseForeignMod { // function name to type name. method_receivers: HashMap, ignored_apis: ApiVec, + parse_callback_results: &'a ParseCallbackResults, } -impl ParseForeignMod { - pub(crate) fn new(ns: Namespace) -> Self { +impl<'a> ParseForeignMod<'a> { + pub(crate) fn new(ns: Namespace, parse_callback_results: &'a ParseCallbackResults) -> Self { Self { ns, funcs_to_convert: Vec::new(), method_receivers: HashMap::new(), ignored_apis: ApiVec::new(), + parse_callback_results, } } @@ -68,8 +69,8 @@ impl ParseForeignMod { fn parse_foreign_item(&mut self, i: ForeignItem) -> Result<(), ConvertErrorWithContext> { match i { ForeignItem::Fn(item) => { - let annotations = BindgenSemanticAttributes::new(&item.attrs); let doc_attrs = get_doc_attrs(&item.attrs); + let qn = QualifiedName::new(&self.ns, item.sig.ident.clone().into()); self.funcs_to_convert.push(FuncToConvert { provenance: Provenance::Bindgen, self_ty: None, @@ -78,16 +79,13 @@ impl ParseForeignMod { inputs: minisynize_punctuated(item.sig.inputs), output: item.sig.output.into(), vis: item.vis.into(), - virtualness: annotations.get_virtualness(), - cpp_vis: annotations.get_cpp_visibility(), - special_member: annotations.special_member_kind(), - unused_template_param: annotations - .has_attr("incomprehensible_param_in_arg_or_return"), - references: annotations.get_reference_parameters_and_return(), - original_name: annotations.get_original_name(), + virtualness: self.parse_callback_results.get_virtualness(&qn), + cpp_vis: self.parse_callback_results.get_cpp_visibility(&qn), + special_member: self.parse_callback_results.special_member_kind(&qn), + original_name: self.parse_callback_results.get_fn_original_name(&qn), synthesized_this_type: None, add_to_trait: None, - is_deleted: annotations.get_deleted_or_defaulted(), + is_deleted: self.parse_callback_results.get_deleted_or_defaulted(&qn), synthetic_cpp: None, variadic: item.sig.variadic.is_some(), }); diff --git a/engine/src/conversion/type_helpers.rs b/engine/src/conversion/type_helpers.rs index 8eb3a3a23..7f253a722 100644 --- a/engine/src/conversion/type_helpers.rs +++ b/engine/src/conversion/type_helpers.rs @@ -7,8 +7,8 @@ // except according to those terms. use syn::{ - AngleBracketedGenericArguments, GenericArgument, PathArguments, PathSegment, Type, TypePath, - TypeReference, + AngleBracketedGenericArguments, GenericArgument, Path, PathArguments, PathSegment, Type, + TypePath, TypeReference, }; /// Looks in a `core::pin::Pin<&mut Something>` and returns the `Something` @@ -56,3 +56,59 @@ fn is_pin(tp: &TypePath) -> bool { } true } + +fn marker_for_reference(search_for_rvalue: bool) -> &'static str { + if search_for_rvalue { + "__bindgen_marker_RValueReference" + } else { + "__bindgen_marker_Reference" + } +} + +pub(crate) fn type_is_reference(ty: &syn::Type, search_for_rvalue: bool) -> bool { + matches_bindgen_marker(ty, marker_for_reference(search_for_rvalue)) +} + +fn matches_bindgen_marker(ty: &syn::Type, marker_name: &str) -> bool { + matches!(&ty, Type::Path(TypePath { + path: Path { segments, .. },.. + }) if segments.first().map(|seg| seg.ident == marker_name).unwrap_or_default()) +} + +fn unwrap_bindgen_marker<'a>(ty: &'a TypePath, marker_name: &str) -> Option<&'a syn::Type> { + ty.path + .segments + .first() + .filter(|seg| seg.ident == marker_name) + .and_then(|seg| match seg.arguments { + PathArguments::AngleBracketed(ref angle_bracketed_args) => { + angle_bracketed_args.args.first() + } + _ => None, + }) + .and_then(|generic_argument| match generic_argument { + GenericArgument::Type(ty) => Some(ty), + _ => None, + }) +} + +pub(crate) fn unwrap_reference(ty: &TypePath, search_for_rvalue: bool) -> Option<&syn::TypePtr> { + match unwrap_bindgen_marker(ty, marker_for_reference(search_for_rvalue)) { + // Our behavior here if we see __bindgen_marker_Reference + // is to ignore the type. This should never happen. + Some(Type::Ptr(typ)) => Some(typ), + _ => None, + } +} + +pub(crate) fn type_has_unused_template_param(ty: &syn::Type) -> bool { + matches_bindgen_marker(ty, "__bindgen_marker_UnusedTemplateParam") +} + +pub(crate) fn unwrap_has_unused_template_param(ty: &TypePath) -> Option<&syn::Type> { + unwrap_bindgen_marker(ty, "__bindgen_marker_UnusedTemplateParam") +} + +pub(crate) fn unwrap_has_opaque(ty: &TypePath) -> Option<&syn::Type> { + unwrap_bindgen_marker(ty, "__bindgen_marker_Opaque") +} diff --git a/engine/src/lib.rs b/engine/src/lib.rs index d67aa4ea8..a54d26dd2 100644 --- a/engine/src/lib.rs +++ b/engine/src/lib.rs @@ -31,7 +31,7 @@ use autocxx_bindgen::BindgenError; use autocxx_parser::{IncludeCppConfig, UnsafePolicy}; use conversion::BridgeConverter; use miette::{SourceOffset, SourceSpan}; -use parse_callbacks::AutocxxParseCallbacks; +use parse_callbacks::{AutocxxParseCallbacks, ParseCallbackResults}; use parse_file::CppBuildable; use proc_macro2::TokenStream as TokenStream2; use regex::Regex; @@ -347,9 +347,14 @@ impl IncludeCppEngine { .generate_inline_functions(true) .respect_cxx_access_specs(true) .use_specific_virtual_function_receiver(true) - .cpp_semantic_attributes(true) + .use_opaque_newtype_wrapper(true) + .use_reference_newtype_wrapper(true) + .use_unused_template_param_newtype_wrapper(true) .represent_cxx_operators(true) .use_distinct_char16_t(true) + .generate_deleted_functions(true) + .generate_pure_virtuals(true) + .generate_private_functions(true) .layout_tests(false); // TODO revisit later for item in known_types().get_initial_blocklist() { builder = builder.blocklist_item(item); @@ -442,11 +447,14 @@ impl IncludeCppEngine { return Err(Error::WrappedReferencesButNoArbitrarySelfTypes); } + let parse_callback_results = Rc::new(RefCell::new(ParseCallbackResults::default())); let mod_name = self.config.get_mod_name(); - let mut builder = self.make_bindgen_builder(&inc_dirs, extra_clang_args); - if let Some(dep_recorder) = dep_recorder { - builder = builder.parse_callbacks(Box::new(AutocxxParseCallbacks(dep_recorder))); - } + let mut builder = self + .make_bindgen_builder(&inc_dirs, extra_clang_args) + .parse_callbacks(Box::new(AutocxxParseCallbacks::new( + dep_recorder, + parse_callback_results.clone(), + ))); let header_contents = self.build_header(); self.dump_header_if_so_configured(&header_contents, &inc_dirs, extra_clang_args); let header_and_prelude = format!("{}\n\n{}", known_types().get_prelude(), header_contents); @@ -455,6 +463,8 @@ impl IncludeCppEngine { let bindings = builder.generate().map_err(Error::Bindgen)?; let bindings = self.parse_bindings(bindings)?; + let parse_callback_results = parse_callback_results.take(); + log::info!("Parse callback results: {:?}", parse_callback_results); // Source code contents just used for diagnostics - if we don't have it, // use a blank string and miette will not attempt to annotate it nicely. @@ -469,6 +479,7 @@ impl IncludeCppEngine { let conversion = converter .convert( bindings, + parse_callback_results, self.config.unsafe_policy.clone(), header_contents, codegen_options, diff --git a/engine/src/parse_callbacks.rs b/engine/src/parse_callbacks.rs index 61098c0fe..83287603b 100644 --- a/engine/src/parse_callbacks.rs +++ b/engine/src/parse_callbacks.rs @@ -6,18 +6,340 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use std::panic::UnwindSafe; +use std::{cell::RefCell, fmt::Display, panic::UnwindSafe, rc::Rc}; -use crate::RebuildDependencyRecorder; +use crate::types::Namespace; +use crate::{conversion::CppEffectiveName, types::QualifiedName, RebuildDependencyRecorder}; use autocxx_bindgen::callbacks::ParseCallbacks; +use autocxx_bindgen::callbacks::Virtualness; +use autocxx_bindgen::callbacks::{ + DiscoveredItem, DiscoveredItemId, Explicitness, Layout, SpecialMemberKind, Visibility, +}; +use indexmap::IndexMap as HashMap; +use indexmap::IndexSet as HashSet; +use quote::quote; + +/// Newtype wrapper for a C++ "original name"; that is, an annotation +/// derived from bindgen that this is the original name of the C++ item. +/// +/// At present these various newtype wrappers for kinds of names +/// (Rust, C++, cxx::bridge) have various conversions between them that +/// are probably not safe. They're marked with FIXMEs. Over time we should +/// remove them, or make them safe by doing name validation at the point +/// of conversion. +#[derive(PartialEq, PartialOrd, Eq, Hash, Clone, Debug)] +pub struct CppOriginalName(String); + +impl Display for CppOriginalName { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str(&self.0) + } +} + +impl CppOriginalName { + pub(crate) fn is_nested(&self) -> bool { + self.0.contains("::") + } + + pub(crate) fn from_final_item_of_pre_existing_qualified_name(name: &QualifiedName) -> Self { + Self(name.get_final_item().to_string()) + } + + pub(crate) fn to_qualified_name(&self) -> QualifiedName { + QualifiedName::new_from_cpp_name(&self.0) + } + + pub(crate) fn to_effective_name(&self) -> CppEffectiveName { + CppEffectiveName(self.0.clone()) + } + + /// This is the main output of this type; it's fed into a mapping + /// from to + /// ; this contributes "inner". + pub(crate) fn for_original_name_map(&self) -> &str { + &self.0 + } + + /// Used to give the final part of the name which can be used + /// to figure out the name for constructors, destructors etc. + pub(crate) fn get_final_segment_for_special_members(&self) -> Option<&str> { + self.0.rsplit_once("::").map(|(_, suffix)| suffix) + } + + pub(crate) fn from_type_name_for_constructor(name: String) -> Self { + Self(name) + } + + /// Work out what to call a Rust-side API given a C++-side name. + pub(crate) fn to_string_for_rust_name(&self) -> String { + self.0.clone() + } + + /// Return the string inside for validation purposes. + pub(crate) fn for_validation(&self) -> &str { + &self.0 + } + + /// Used for diagnostics early in function analysis before we establish + /// the correct naming. + pub(crate) fn diagnostic_display_name(&self) -> &String { + &self.0 + } + + // FIXME - remove + pub(crate) fn from_rust_name(string: String) -> Self { + Self(string) + } + + /// Determines whether we need to generate a cxxbridge::name attribute + pub(crate) fn does_not_match_cxxbridge_name( + &self, + cxxbridge_name: &crate::minisyn::Ident, + ) -> bool { + cxxbridge_name.0 != self.0 + } + + pub(crate) fn generate_cxxbridge_name_attribute(&self) -> proc_macro2::TokenStream { + let cpp_call_name = &self.to_string_for_rust_name(); + quote!( + #[cxx_name = #cpp_call_name] + ) + } +} + +#[derive(Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)] +struct NameAndParent { + parent: DiscoveredItemId, + name: String, +} + +#[derive(Debug, Default, Clone)] +/// Information communicated to us from bindgen using its `ParseCallbacks` +/// mechanism. +/// +/// The various accessor methods here return `None` if a +/// given `QualifiedName` can't be found, because bindgen only tells us +/// information when it actually has it. +pub(crate) struct ParseCallbackResults { + original_names: HashMap, + virtuals: HashMap, + root_mod: Option, + layouts: HashMap, + visibility: HashMap, + special_member_kinds: HashMap, + explicitness: HashMap, + discards_template_param: HashSet, + names: HashMap, + mods_for_items: HashMap, +} + +impl ParseCallbackResults { + pub(crate) fn get_fn_original_name(&self, name: &QualifiedName) -> Option { + self.id_by_name(name) + .and_then(|id| self.original_names.get(&id).cloned()) + } + + pub(crate) fn get_original_name(&self, name: &QualifiedName) -> Option { + self.id_by_name(name) + .and_then(|id| self.original_names.get(&id).cloned()) + } + + pub(crate) fn get_virtualness(&self, name: &QualifiedName) -> Option { + self.id_by_name(name) + .and_then(|id| self.virtuals.get(&id).cloned()) + } + + fn id_by_name(&self, name: &QualifiedName) -> Option { + self.mod_id_by_namespace(name.get_namespace()) + .and_then(|parent| { + let search_key = NameAndParent { + parent, + name: name.get_final_item().to_string(), + }; + self.get_item_by_parentage(&search_key) + }) + } + + fn get_root_mod(&self) -> DiscoveredItemId { + self.root_mod.expect("Root mod not yet reported by bindgen") + } + + fn mod_id_by_namespace(&self, namespace: &Namespace) -> Option { + self.mod_id_by_inner_namespace(self.get_root_mod(), namespace.iter()) + } + + fn mod_id_by_inner_namespace<'a>( + &self, + parent: DiscoveredItemId, + mut ns_iter: impl Iterator, + ) -> Option { + match ns_iter.next() { + Some(child_mod_name) => { + let search_key = NameAndParent { + parent, + name: child_mod_name.to_string(), + }; + self.get_item_by_parentage(&search_key) + .and_then(|child_mod_id| self.mod_id_by_inner_namespace(child_mod_id, ns_iter)) + } + None => Some(parent), + } + } + + fn get_item_by_parentage(&self, search_key: &NameAndParent) -> Option { + // This is O(n), and since it will be called at least once for each item, that means autocxx + // overall is O(n^2). We probably want to build a map here. + eprintln!("Searching for {search_key:?}"); + self.mods_for_items + .iter() + .filter(|(_item, parent)| **parent == search_key.parent) + .map(|(item, _parent)| item) + .find(|item| { + self.names + .get(*item) + .map(|n| n == &search_key.name) + .unwrap_or_default() + }) + .cloned() + } + + pub(crate) fn get_layout(&self, name: &QualifiedName) -> Option { + self.id_by_name(name) + .and_then(|id| self.layouts.get(&id).cloned()) + } + + pub(crate) fn get_cpp_visibility(&self, name: &QualifiedName) -> Visibility { + self.id_by_name(name) + .and_then(|id| self.visibility.get(&id).cloned()) + .unwrap_or(Visibility::Public) + } + + pub(crate) fn special_member_kind(&self, name: &QualifiedName) -> Option { + self.id_by_name(name) + .and_then(|id| self.special_member_kinds.get(&id).cloned()) + } + + pub(crate) fn get_deleted_or_defaulted(&self, name: &QualifiedName) -> Option { + self.id_by_name(name) + .and_then(|id| self.explicitness.get(&id).cloned()) + } + + pub(crate) fn discards_template_param(&self, name: &QualifiedName) -> bool { + eprintln!("Asking if {:?} discards template param", name); + let id = self.id_by_name(name); + eprintln!("id is {:?}", id); + let r = self + .id_by_name(name) + .map(|id| self.discards_template_param.contains(&id)) + .unwrap_or_default(); + eprintln!("r = {:?}", r); + r + } +} #[derive(Debug)] -pub(crate) struct AutocxxParseCallbacks(pub(crate) Box); +pub(crate) struct AutocxxParseCallbacks { + pub(crate) rebuild_dependency_recorder: Option>, + pub(crate) results: Rc>, +} + +impl AutocxxParseCallbacks { + pub(crate) fn new( + rebuild_dependency_recorder: Option>, + results: Rc>, + ) -> Self { + Self { + rebuild_dependency_recorder, + results, + } + } +} impl UnwindSafe for AutocxxParseCallbacks {} impl ParseCallbacks for AutocxxParseCallbacks { fn include_file(&self, filename: &str) { - self.0.record_header_file_dependency(filename); + if let Some(rebuild_dependency_recorder) = &self.rebuild_dependency_recorder { + rebuild_dependency_recorder.record_header_file_dependency(filename); + } + } + + fn denote_cpp_name( + &self, + id: DiscoveredItemId, + original_name: Option<&str>, + namespace_mod: Option, + ) { + let mut results = self.results.borrow_mut(); + if let Some(original_name) = original_name { + results + .original_names + .insert(id, CppOriginalName(original_name.to_string())); + } + if let Some(namespace_mod) = namespace_mod { + results.mods_for_items.insert(id, namespace_mod); + } + } + + fn denote_virtualness(&self, id: DiscoveredItemId, virtualness: Virtualness) { + self.results.borrow_mut().virtuals.insert(id, virtualness); + } + + fn new_item_found(&self, id: DiscoveredItemId, item: DiscoveredItem) { + match item { + DiscoveredItem::Struct { final_name, .. } + | DiscoveredItem::Enum { final_name, .. } + | DiscoveredItem::Union { final_name, .. } + | DiscoveredItem::Alias { + alias_name: final_name, + .. + } + | DiscoveredItem::Function { final_name } => { + eprintln!("Informed about {id:?} {final_name:?}"); + self.results.borrow_mut().names.insert(id, final_name); + } + DiscoveredItem::Mod { + final_name, + parent_id, + } => { + let mut results = self.results.borrow_mut(); + results.names.insert(id, final_name); + if let Some(parent_id) = parent_id { + results.mods_for_items.insert(id, parent_id); + } else { + results.root_mod.replace(id); + } + } + _ => {} + } + } + + fn denote_layout(&self, id: DiscoveredItemId, layout: &Layout) { + self.results.borrow_mut().layouts.insert(id, *layout); + } + + fn denote_visibility(&self, id: DiscoveredItemId, visibility: Visibility) { + if !matches!(visibility, Visibility::Public) { + // Public is the default; no need to record + self.results.borrow_mut().visibility.insert(id, visibility); + } + } + + fn denote_special_member(&self, id: DiscoveredItemId, kind: SpecialMemberKind) { + self.results + .borrow_mut() + .special_member_kinds + .insert(id, kind); + } + + fn denote_explicit(&self, id: DiscoveredItemId, explicitness: Explicitness) { + self.results + .borrow_mut() + .explicitness + .insert(id, explicitness); + } + + fn denote_discards_template_param(&self, id: DiscoveredItemId) { + self.results.borrow_mut().discards_template_param.insert(id); } } diff --git a/engine/src/types.rs b/engine/src/types.rs index 3b2155684..e14150fa6 100644 --- a/engine/src/types.rs +++ b/engine/src/types.rs @@ -7,6 +7,7 @@ // except according to those terms. use crate::minisyn::Ident; +use crate::parse_callbacks::CppOriginalName; use itertools::Itertools; use proc_macro2::Span; use quote::ToTokens; @@ -17,8 +18,6 @@ use thiserror::Error; use crate::known_types::known_types; -use crate::conversion::CppOriginalName; - pub(crate) fn make_ident>(id: S) -> Ident { Ident::new(id.as_ref(), Span::call_site()) } @@ -265,7 +264,7 @@ pub fn validate_ident_ok_for_cxx(id: &str) -> Result<(), InvalidIdentError> { Err(InvalidIdentError::Bitfield) } else if id.starts_with("__BindgenUnionField") { Err(InvalidIdentError::Union) - } else if id.contains("__") { + } else if id.contains("__") && !id.starts_with("__bindgen_marker") { Err(InvalidIdentError::TooManyUnderscores) } else if id.starts_with("_bindgen_ty_") { Err(InvalidIdentError::BindgenTy)