blob: 5f493f9c79dc115e3b5fe336df8ae36bbe4a7023 [file] [log] [blame]
Brian Silverman4e662aa2022-05-11 23:10:19 -07001// 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
9use itertools::Itertools;
10use quote::quote;
11use syn::{parse_quote, FnArg};
12
13use 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.
26const SUPPORT_MUTABLE_CASTS: bool = false;
27
28use super::{
29 fun::function_wrapper::{CppFunctionBody, CppFunctionKind},
30 pod::{PodAnalysis, PodPhase},
31};
32
33pub(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
50fn 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.
63fn 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
76fn 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
123fn 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}