Brian Silverman | 4e662aa | 2022-05-11 23:10:19 -0700 | [diff] [blame^] | 1 | // Copyright 2022 Google LLC |
| 2 | // |
| 3 | // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or |
| 4 | // https://www.apache.org/licenses/LICENSE-2.0> or the MIT license |
| 5 | // <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your |
| 6 | // option. This file may not be copied, modified, or distributed |
| 7 | // except according to those terms. |
| 8 | |
| 9 | use itertools::Itertools; |
| 10 | use quote::quote; |
| 11 | use syn::{parse_quote, FnArg}; |
| 12 | |
| 13 | use crate::{ |
| 14 | conversion::{ |
| 15 | api::{Api, ApiName, CastMutability, Provenance, References, TraitSynthesis}, |
| 16 | apivec::ApiVec, |
| 17 | }, |
| 18 | types::{make_ident, QualifiedName}, |
| 19 | }; |
| 20 | |
| 21 | /// If A is a base of B, we might want to be able to cast from |
| 22 | /// &B to &A, or from Pin<&mut A> to &B, or from Pin<&mut A> to &B. |
| 23 | /// The first is OK; the others turn out to be hard due to all |
| 24 | /// the Pin stuff. For now therefore, we simply don't allow them. |
| 25 | /// But the related code may be useful in future so I'm keeping it around. |
| 26 | const SUPPORT_MUTABLE_CASTS: bool = false; |
| 27 | |
| 28 | use super::{ |
| 29 | fun::function_wrapper::{CppFunctionBody, CppFunctionKind}, |
| 30 | pod::{PodAnalysis, PodPhase}, |
| 31 | }; |
| 32 | |
| 33 | pub(crate) fn add_casts(apis: ApiVec<PodPhase>) -> ApiVec<PodPhase> { |
| 34 | apis.into_iter() |
| 35 | .flat_map(|api| { |
| 36 | let mut resultant_apis = match api { |
| 37 | Api::Struct { |
| 38 | ref name, |
| 39 | details: _, |
| 40 | ref analysis, |
| 41 | } => create_casts(&name.name, analysis).collect_vec(), |
| 42 | _ => Vec::new(), |
| 43 | }; |
| 44 | resultant_apis.push(api); |
| 45 | resultant_apis.into_iter() |
| 46 | }) |
| 47 | .collect() |
| 48 | } |
| 49 | |
| 50 | fn create_casts<'a>( |
| 51 | name: &'a QualifiedName, |
| 52 | analysis: &'a PodAnalysis, |
| 53 | ) -> impl Iterator<Item = Api<PodPhase>> + 'a { |
| 54 | // Create casts only to base classes which are on the allowlist |
| 55 | // because otherwise we won't know for sure whether they're abstract or not. |
| 56 | analysis |
| 57 | .castable_bases |
| 58 | .iter() |
| 59 | .flat_map(move |base| cast_types().map(|mutable| create_cast(name, base, mutable))) |
| 60 | } |
| 61 | |
| 62 | /// Iterate through the types of cast we should make. |
| 63 | fn cast_types() -> impl Iterator<Item = CastMutability> { |
| 64 | if SUPPORT_MUTABLE_CASTS { |
| 65 | vec![ |
| 66 | CastMutability::ConstToConst, |
| 67 | CastMutability::MutToConst, |
| 68 | CastMutability::MutToMut, |
| 69 | ] |
| 70 | .into_iter() |
| 71 | } else { |
| 72 | vec![CastMutability::ConstToConst].into_iter() |
| 73 | } |
| 74 | } |
| 75 | |
| 76 | fn create_cast(from: &QualifiedName, to: &QualifiedName, mutable: CastMutability) -> Api<PodPhase> { |
| 77 | let name = name_for_cast(from, to, mutable); |
| 78 | let ident = name.get_final_ident(); |
| 79 | let from_typ = from.to_type_path(); |
| 80 | let to_typ = to.to_type_path(); |
| 81 | let return_mutability = match mutable { |
| 82 | CastMutability::ConstToConst | CastMutability::MutToConst => quote! { const }, |
| 83 | CastMutability::MutToMut => quote! { mut }, |
| 84 | }; |
| 85 | let param_mutability = match mutable { |
| 86 | CastMutability::ConstToConst => quote! { const }, |
| 87 | CastMutability::MutToConst | CastMutability::MutToMut => quote! { mut }, |
| 88 | }; |
| 89 | let fnarg: FnArg = parse_quote! { |
| 90 | this: * #param_mutability #from_typ |
| 91 | }; |
| 92 | Api::Function { |
| 93 | name: ApiName::new_from_qualified_name(name), |
| 94 | fun: Box::new(crate::conversion::api::FuncToConvert { |
| 95 | ident, |
| 96 | doc_attrs: Vec::new(), |
| 97 | inputs: [fnarg].into_iter().collect(), |
| 98 | output: parse_quote! { |
| 99 | -> * #return_mutability #to_typ |
| 100 | }, |
| 101 | vis: parse_quote! { pub }, |
| 102 | virtualness: crate::conversion::api::Virtualness::None, |
| 103 | cpp_vis: crate::conversion::api::CppVisibility::Public, |
| 104 | special_member: None, |
| 105 | unused_template_param: false, |
| 106 | references: References::new_with_this_and_return_as_reference(), |
| 107 | original_name: None, |
| 108 | self_ty: Some(from.clone()), |
| 109 | synthesized_this_type: None, |
| 110 | add_to_trait: Some(TraitSynthesis::Cast { |
| 111 | to_type: to.clone(), |
| 112 | mutable, |
| 113 | }), |
| 114 | synthetic_cpp: Some((CppFunctionBody::Cast, CppFunctionKind::Function)), |
| 115 | is_deleted: false, |
| 116 | provenance: Provenance::SynthesizedOther, |
| 117 | variadic: false, |
| 118 | }), |
| 119 | analysis: (), |
| 120 | } |
| 121 | } |
| 122 | |
| 123 | fn name_for_cast( |
| 124 | from: &QualifiedName, |
| 125 | to: &QualifiedName, |
| 126 | mutable: CastMutability, |
| 127 | ) -> QualifiedName { |
| 128 | let suffix = match mutable { |
| 129 | CastMutability::ConstToConst => "", |
| 130 | CastMutability::MutToConst => "_to_const", |
| 131 | CastMutability::MutToMut => "_mut", |
| 132 | }; |
| 133 | let name = format!( |
| 134 | "cast_{}_to_{}{}", |
| 135 | from.get_final_item(), |
| 136 | to.get_final_item(), |
| 137 | suffix |
| 138 | ); |
| 139 | let name = make_ident(name); |
| 140 | QualifiedName::new(from.get_namespace(), name) |
| 141 | } |