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