| // Copyright 2022 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. |
| |
| use itertools::Itertools; |
| use quote::quote; |
| use syn::{parse_quote, FnArg}; |
| |
| use crate::{ |
| conversion::{ |
| api::{Api, ApiName, CastMutability, Provenance, References, TraitSynthesis}, |
| apivec::ApiVec, |
| }, |
| types::{make_ident, QualifiedName}, |
| }; |
| |
| /// If A is a base of B, we might want to be able to cast from |
| /// &B to &A, or from Pin<&mut A> to &B, or from Pin<&mut A> to &B. |
| /// The first is OK; the others turn out to be hard due to all |
| /// the Pin stuff. For now therefore, we simply don't allow them. |
| /// But the related code may be useful in future so I'm keeping it around. |
| const SUPPORT_MUTABLE_CASTS: bool = false; |
| |
| use super::{ |
| fun::function_wrapper::{CppFunctionBody, CppFunctionKind}, |
| pod::{PodAnalysis, PodPhase}, |
| }; |
| |
| pub(crate) fn add_casts(apis: ApiVec<PodPhase>) -> ApiVec<PodPhase> { |
| apis.into_iter() |
| .flat_map(|api| { |
| let mut resultant_apis = match api { |
| Api::Struct { |
| ref name, |
| details: _, |
| ref analysis, |
| } => create_casts(&name.name, analysis).collect_vec(), |
| _ => Vec::new(), |
| }; |
| resultant_apis.push(api); |
| resultant_apis.into_iter() |
| }) |
| .collect() |
| } |
| |
| fn create_casts<'a>( |
| name: &'a QualifiedName, |
| analysis: &'a PodAnalysis, |
| ) -> impl Iterator<Item = Api<PodPhase>> + 'a { |
| // Create casts only to base classes which are on the allowlist |
| // because otherwise we won't know for sure whether they're abstract or not. |
| analysis |
| .castable_bases |
| .iter() |
| .flat_map(move |base| cast_types().map(|mutable| create_cast(name, base, mutable))) |
| } |
| |
| /// Iterate through the types of cast we should make. |
| fn cast_types() -> impl Iterator<Item = CastMutability> { |
| if SUPPORT_MUTABLE_CASTS { |
| vec![ |
| CastMutability::ConstToConst, |
| CastMutability::MutToConst, |
| CastMutability::MutToMut, |
| ] |
| .into_iter() |
| } else { |
| vec![CastMutability::ConstToConst].into_iter() |
| } |
| } |
| |
| fn create_cast(from: &QualifiedName, to: &QualifiedName, mutable: CastMutability) -> Api<PodPhase> { |
| let name = name_for_cast(from, to, mutable); |
| let ident = name.get_final_ident(); |
| let from_typ = from.to_type_path(); |
| let to_typ = to.to_type_path(); |
| let return_mutability = match mutable { |
| CastMutability::ConstToConst | CastMutability::MutToConst => quote! { const }, |
| CastMutability::MutToMut => quote! { mut }, |
| }; |
| let param_mutability = match mutable { |
| CastMutability::ConstToConst => quote! { const }, |
| CastMutability::MutToConst | CastMutability::MutToMut => quote! { mut }, |
| }; |
| let fnarg: FnArg = parse_quote! { |
| this: * #param_mutability #from_typ |
| }; |
| Api::Function { |
| name: ApiName::new_from_qualified_name(name), |
| fun: Box::new(crate::conversion::api::FuncToConvert { |
| ident, |
| doc_attrs: Vec::new(), |
| inputs: [fnarg].into_iter().collect(), |
| output: parse_quote! { |
| -> * #return_mutability #to_typ |
| }, |
| vis: parse_quote! { pub }, |
| virtualness: crate::conversion::api::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, |
| add_to_trait: Some(TraitSynthesis::Cast { |
| to_type: to.clone(), |
| mutable, |
| }), |
| synthetic_cpp: Some((CppFunctionBody::Cast, CppFunctionKind::Function)), |
| is_deleted: false, |
| provenance: Provenance::SynthesizedOther, |
| variadic: false, |
| }), |
| analysis: (), |
| } |
| } |
| |
| fn name_for_cast( |
| from: &QualifiedName, |
| to: &QualifiedName, |
| mutable: CastMutability, |
| ) -> QualifiedName { |
| let suffix = match mutable { |
| CastMutability::ConstToConst => "", |
| CastMutability::MutToConst => "_to_const", |
| CastMutability::MutToMut => "_mut", |
| }; |
| let name = format!( |
| "cast_{}_to_{}{}", |
| from.get_final_item(), |
| to.get_final_item(), |
| suffix |
| ); |
| let name = make_ident(name); |
| QualifiedName::new(from.get_namespace(), name) |
| } |