blob: 8d4621884580032c6be86f051d588668bf97fec6 [file] [log] [blame]
Marcel Hlopko42abfc82021-08-09 07:03:17 +00001// Part of the Crubit project, under the Apache License v2.0 with LLVM
2// Exceptions. See /LICENSE for license information.
3// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
4
Lukasz Anforowicz40c2eb82022-01-11 18:22:31 +00005use anyhow::{anyhow, bail, ensure, Context, Result};
Marcel Hlopko884ae7f2021-08-18 13:58:22 +00006use ffi_types::*;
Marcel Hlopko42abfc82021-08-09 07:03:17 +00007use ir::*;
8use itertools::Itertools;
Googler5ea88642021-09-29 08:05:59 +00009use proc_macro2::{Ident, Literal, TokenStream};
Marcel Hlopko42abfc82021-08-09 07:03:17 +000010use quote::format_ident;
11use quote::quote;
Googlerd03d05b2022-01-07 10:10:57 +000012use std::collections::{BTreeSet, HashMap, HashSet};
Marcel Hlopko42abfc82021-08-09 07:03:17 +000013use std::iter::Iterator;
14use std::panic::catch_unwind;
15use std::process;
Marcel Hlopko65d05f02021-12-09 12:29:24 +000016use token_stream_printer::{rs_tokens_to_formatted_string, tokens_to_string};
Marcel Hlopko42abfc82021-08-09 07:03:17 +000017
Marcel Hlopko45fba972021-08-23 19:52:20 +000018/// FFI equivalent of `Bindings`.
19#[repr(C)]
20pub struct FfiBindings {
21 rs_api: FfiU8SliceBox,
22 rs_api_impl: FfiU8SliceBox,
23}
24
25/// Deserializes IR from `json` and generates bindings source code.
Marcel Hlopko42abfc82021-08-09 07:03:17 +000026///
27/// This function panics on error.
28///
29/// Ownership:
Michael Forsterbee84482021-10-13 08:35:38 +000030/// * function doesn't take ownership of (in other words it borrows) the
31/// param `json`
Marcel Hlopko42abfc82021-08-09 07:03:17 +000032/// * function passes ownership of the returned value to the caller
33///
34/// Safety:
Michael Forsterbee84482021-10-13 08:35:38 +000035/// * function expects that param `json` is a FfiU8Slice for a valid array of
36/// bytes with the given size.
Marcel Hlopko42abfc82021-08-09 07:03:17 +000037/// * function expects that param `json` doesn't change during the call.
38#[no_mangle]
Marcel Hlopko45fba972021-08-23 19:52:20 +000039pub unsafe extern "C" fn GenerateBindingsImpl(json: FfiU8Slice) -> FfiBindings {
Marcel Hlopko42abfc82021-08-09 07:03:17 +000040 catch_unwind(|| {
Marcel Hlopko45fba972021-08-23 19:52:20 +000041 // It is ok to abort here.
42 let Bindings { rs_api, rs_api_impl } = generate_bindings(json.as_slice()).unwrap();
43
44 FfiBindings {
45 rs_api: FfiU8SliceBox::from_boxed_slice(rs_api.into_bytes().into_boxed_slice()),
46 rs_api_impl: FfiU8SliceBox::from_boxed_slice(
47 rs_api_impl.into_bytes().into_boxed_slice(),
48 ),
49 }
Marcel Hlopko42abfc82021-08-09 07:03:17 +000050 })
51 .unwrap_or_else(|_| process::abort())
52}
53
Marcel Hlopko45fba972021-08-23 19:52:20 +000054/// Source code for generated bindings.
55struct Bindings {
56 // Rust source code.
57 rs_api: String,
58 // C++ source code.
59 rs_api_impl: String,
Marcel Hlopko42abfc82021-08-09 07:03:17 +000060}
61
Marcel Hlopko45fba972021-08-23 19:52:20 +000062fn generate_bindings(json: &[u8]) -> Result<Bindings> {
63 let ir = deserialize_ir(json)?;
Marcel Hlopkoca84ff42021-12-09 14:15:14 +000064
65 // The code is formatted with a non-default rustfmt configuration. Prevent
66 // downstream workflows from reformatting with a different configuration.
Marcel Hlopko89547752021-12-10 09:39:41 +000067 let rs_api =
68 format!("#![rustfmt::skip]\n{}", rs_tokens_to_formatted_string(generate_rs_api(&ir)?)?);
69 let rs_api_impl = tokens_to_string(generate_rs_api_impl(&ir)?)?;
Marcel Hlopkoca84ff42021-12-09 14:15:14 +000070
Marcel Hlopko45fba972021-08-23 19:52:20 +000071 Ok(Bindings { rs_api, rs_api_impl })
72}
73
Devin Jeanpierre6d5e7cc2021-10-21 12:56:07 +000074/// Rust source code with attached information about how to modify the parent
75/// crate.
Devin Jeanpierre273eeae2021-10-06 13:29:35 +000076///
Michael Forsterbee84482021-10-13 08:35:38 +000077/// For example, the snippet `vec![].into_raw_parts()` is not valid unless the
78/// `vec_into_raw_parts` feature is enabled. So such a snippet should be
79/// represented as:
Devin Jeanpierre273eeae2021-10-06 13:29:35 +000080///
81/// ```
82/// RsSnippet {
Devin Jeanpierre6d5e7cc2021-10-21 12:56:07 +000083/// features: btree_set![make_ident("vec_into_raw_parts")],
Devin Jeanpierre273eeae2021-10-06 13:29:35 +000084/// tokens: quote!{vec![].into_raw_parts()},
85/// }
86/// ```
87struct RsSnippet {
88 /// Rust feature flags used by this snippet.
89 features: BTreeSet<Ident>,
90 /// The snippet itself, as a token stream.
91 tokens: TokenStream,
92}
93
94impl From<TokenStream> for RsSnippet {
95 fn from(tokens: TokenStream) -> Self {
96 RsSnippet { features: BTreeSet::new(), tokens }
97 }
98}
99
Michael Forsterbee84482021-10-13 08:35:38 +0000100/// If we know the original C++ function is codegenned and already compatible
101/// with `extern "C"` calling convention we skip creating/calling the C++ thunk
102/// since we can call the original C++ directly.
Marcel Hlopko3164eee2021-08-24 20:09:22 +0000103fn can_skip_cc_thunk(func: &Func) -> bool {
Devin Jeanpierre96839c12021-12-14 00:27:38 +0000104 // ## Inline functions
105 //
Michael Forsterbee84482021-10-13 08:35:38 +0000106 // Inline functions may not be codegenned in the C++ library since Clang doesn't
107 // know if Rust calls the function or not. Therefore in order to make inline
108 // functions callable from Rust we need to generate a C++ file that defines
109 // a thunk that delegates to the original inline function. When compiled,
110 // Clang will emit code for this thunk and Rust code will call the
Marcel Hlopko3164eee2021-08-24 20:09:22 +0000111 // thunk when the user wants to call the original inline function.
112 //
Michael Forsterbee84482021-10-13 08:35:38 +0000113 // This is not great runtime-performance-wise in regular builds (inline function
114 // will not be inlined, there will always be a function call), but it is
115 // correct. ThinLTO builds will be able to see through the thunk and inline
116 // code across the language boundary. For non-ThinLTO builds we plan to
117 // implement <internal link> which removes the runtime performance overhead.
Devin Jeanpierre96839c12021-12-14 00:27:38 +0000118 if func.is_inline {
119 return false;
120 }
121 // ## Virtual functions
122 //
123 // When calling virtual `A::Method()`, it's not necessarily the case that we'll
124 // specifically call the concrete `A::Method` impl. For example, if this is
125 // called on something whose dynamic type is some subclass `B` with an
126 // overridden `B::Method`, then we'll call that.
127 //
128 // We must reuse the C++ dynamic dispatching system. In this case, the easiest
129 // way to do it is by resorting to a C++ thunk, whose implementation will do
130 // the lookup.
131 //
132 // In terms of runtime performance, since this only occurs for virtual function
133 // calls, which are already slow, it may not be such a big deal. We can
134 // benchmark it later. :)
135 if let Some(meta) = &func.member_func_metadata {
136 if let Some(inst_meta) = &meta.instance_method_metadata {
137 if inst_meta.is_virtual {
138 return false;
139 }
140 }
141 }
142
143 true
Marcel Hlopko3164eee2021-08-24 20:09:22 +0000144}
145
Googlerd03d05b2022-01-07 10:10:57 +0000146/// Uniquely identifies a generated Rust function.
147#[derive(Clone, PartialEq, Eq, Hash)]
148struct FunctionId {
149 // If the function is on a trait impl, contains the name of the Self type for
150 // which the trait is being implemented.
151 self_type: Option<syn::Path>,
152 // Fully qualified path of the function. For functions in impl blocks, this
153 // includes the name of the type or trait on which the function is being
154 // implemented, e.g. `Default::default`.
155 function_path: syn::Path,
156}
157
158/// Returns the name of `func` in C++ synatx.
159fn cxx_function_name(func: &Func, ir: &IR) -> Result<String> {
160 let record: Option<&str> = func
161 .member_func_metadata
162 .as_ref()
163 .map(|meta| meta.find_record(ir))
164 .transpose()?
165 .map(|r| &*r.identifier.identifier);
166
167 let func_name = match &func.name {
168 UnqualifiedIdentifier::Identifier(id) => id.identifier.clone(),
169 UnqualifiedIdentifier::Destructor => {
170 format!("~{}", record.expect("destructor must be associated with a record"))
171 }
172 UnqualifiedIdentifier::Constructor => {
173 format!("~{}", record.expect("constructor must be associated with a record"))
174 }
175 };
176
177 if let Some(record_name) = record {
178 Ok(format!("{}::{}", record_name, func_name))
179 } else {
180 Ok(func_name)
181 }
182}
183
Michael Forstered642022021-10-04 09:48:25 +0000184/// Generates Rust source code for a given `Func`.
185///
Googlerd03d05b2022-01-07 10:10:57 +0000186/// Returns None if no code was generated for the function; otherwise, returns
187/// a tuple containing:
188/// - The generated function or trait impl
189/// - The thunk
190/// - A `FunctionId` identifying the generated Rust function
191fn generate_func(func: &Func, ir: &IR) -> Result<Option<(RsSnippet, RsSnippet, FunctionId)>> {
Michael Forstered642022021-10-04 09:48:25 +0000192 let mangled_name = &func.mangled_name;
Googlera675ae02021-12-07 08:04:59 +0000193 let thunk_ident = thunk_ident(func);
Michael Forster409d9412021-10-07 08:35:29 +0000194 let doc_comment = generate_doc_comment(&func.doc_comment);
Googler7cced422021-12-06 11:58:39 +0000195 let lifetime_to_name = HashMap::<LifetimeId, String>::from_iter(
196 func.lifetime_params.iter().map(|l| (l.id, l.name.clone())),
197 );
Lukasz Anforowicz4ad012b2021-12-15 18:13:40 +0000198 let return_type_fragment = if func.return_type.rs_type.is_unit_type() {
199 quote! {}
200 } else {
Googlerb7e361d2022-01-04 14:02:59 +0000201 let return_type_name = format_rs_type(&func.return_type.rs_type, ir, &lifetime_to_name)
202 .with_context(|| format!("Failed to format return type for {:?}", func))?;
Lukasz Anforowicz4ad012b2021-12-15 18:13:40 +0000203 quote! { -> #return_type_name }
204 };
Michael Forstered642022021-10-04 09:48:25 +0000205
206 let param_idents =
207 func.params.iter().map(|p| make_ident(&p.identifier.identifier)).collect_vec();
208
Marcel Hlopkoc0956cf2021-11-29 08:31:28 +0000209 let param_types = func
210 .params
211 .iter()
Googlerb7e361d2022-01-04 14:02:59 +0000212 .map(|p| {
213 format_rs_type(&p.type_.rs_type, ir, &lifetime_to_name).with_context(|| {
214 format!("Failed to format type for parameter {:?} on {:?}", p, func)
215 })
216 })
Marcel Hlopkoc0956cf2021-11-29 08:31:28 +0000217 .collect::<Result<Vec<_>>>()?;
Michael Forstered642022021-10-04 09:48:25 +0000218
Googler7cced422021-12-06 11:58:39 +0000219 let lifetimes = func
220 .lifetime_params
221 .iter()
Lukasz Anforowiczdaff0402021-12-23 00:37:50 +0000222 .map(|l| syn::Lifetime::new(&format!("'{}", l.name), proc_macro2::Span::call_site()));
223 let generic_params = format_generic_params(lifetimes);
Googler7cced422021-12-06 11:58:39 +0000224
Lukasz Anforowicz13cf7492021-12-22 15:29:52 +0000225 let record: Option<&Record> =
226 func.member_func_metadata.as_ref().map(|meta| meta.find_record(ir)).transpose()?;
227
Googlerd03d05b2022-01-07 10:10:57 +0000228 let api_func: TokenStream;
229 let function_id: FunctionId;
230 match &func.name {
Devin Jeanpierref2ec8712021-10-13 20:47:16 +0000231 UnqualifiedIdentifier::Identifier(id) => {
232 let ident = make_ident(&id.identifier);
Lukasz Anforowiczee7669e2021-12-15 17:04:36 +0000233 let fn_def = quote! {
Devin Jeanpierref2ec8712021-10-13 20:47:16 +0000234 #doc_comment
235 #[inline(always)]
Lukasz Anforowicz4ad012b2021-12-15 18:13:40 +0000236 pub fn #ident #generic_params( #( #param_idents: #param_types ),*
237 ) #return_type_fragment {
Devin Jeanpierref2ec8712021-10-13 20:47:16 +0000238 unsafe { crate::detail::#thunk_ident( #( #param_idents ),* ) }
239 }
Lukasz Anforowiczee7669e2021-12-15 17:04:36 +0000240 };
Lukasz Anforowiczaab8ad22021-12-19 20:29:26 +0000241 match &func.member_func_metadata {
Googlerd03d05b2022-01-07 10:10:57 +0000242 None => {
243 api_func = fn_def;
244 function_id = FunctionId { self_type: None, function_path: ident.into() };
245 }
Lukasz Anforowiczaab8ad22021-12-19 20:29:26 +0000246 Some(meta) => {
247 let type_name = make_ident(&meta.find_record(ir)?.identifier.identifier);
Googlerd03d05b2022-01-07 10:10:57 +0000248 api_func = quote! { impl #type_name { #fn_def } };
249 function_id = FunctionId {
250 self_type: None,
251 function_path: syn::parse2(quote! { #type_name :: #ident })?,
252 };
Lukasz Anforowiczee7669e2021-12-15 17:04:36 +0000253 }
Googlerd03d05b2022-01-07 10:10:57 +0000254 };
Michael Forstered642022021-10-04 09:48:25 +0000255 }
Devin Jeanpierre91de7012021-10-21 12:53:51 +0000256
257 UnqualifiedIdentifier::Destructor => {
Lukasz Anforowicz13cf7492021-12-22 15:29:52 +0000258 let record = record.ok_or_else(|| anyhow!("Destructors must be member functions."))?;
Devin Jeanpierre7e9a1de2021-12-03 08:04:22 +0000259 let type_name = make_ident(&record.identifier.identifier);
260 match record.destructor.definition {
261 // TODO(b/202258760): Only omit destructor if `Copy` is specified.
262 SpecialMemberDefinition::Trivial => {
Googlerd03d05b2022-01-07 10:10:57 +0000263 return Ok(None);
Devin Jeanpierre7e9a1de2021-12-03 08:04:22 +0000264 }
Lukasz Anforowicz6d553632022-01-06 21:36:14 +0000265 SpecialMemberDefinition::NontrivialMembers
266 | SpecialMemberDefinition::NontrivialUserDefined => {
Devin Jeanpierref61ba6f2021-12-17 08:57:45 +0000267 // Note: to avoid double-destruction of the fields, they are all wrapped in
268 // ManuallyDrop in this case. See `generate_record`.
Googlerd03d05b2022-01-07 10:10:57 +0000269 api_func = quote! {
Devin Jeanpierre7e9a1de2021-12-03 08:04:22 +0000270 #doc_comment
271 impl Drop for #type_name {
272 #[inline(always)]
273 fn drop(&mut self) {
274 unsafe { crate::detail::#thunk_ident(self) }
275 }
276 }
Googlerd03d05b2022-01-07 10:10:57 +0000277 };
278 function_id = FunctionId {
279 self_type: Some(type_name.into()),
280 function_path: syn::parse2(quote! {Drop::drop})?,
281 };
Devin Jeanpierre7e9a1de2021-12-03 08:04:22 +0000282 }
283 SpecialMemberDefinition::Deleted => {
284 bail!("Deleted destructors can't be called") // TODO(b/200066399): handle this?
285 }
Devin Jeanpierre91de7012021-10-21 12:53:51 +0000286 }
287 }
288
Lukasz Anforowicz13cf7492021-12-22 15:29:52 +0000289 UnqualifiedIdentifier::Constructor => {
Lukasz Anforowicz73326af2022-01-05 01:13:10 +0000290 // TODO(lukasza): Also allow mapping constructors to inherent static methods
291 // (e.g. if named via a bindings-generator-recognized C++
292 // attribute).
Lukasz Anforowicz13cf7492021-12-22 15:29:52 +0000293 let record = record.ok_or_else(|| anyhow!("Constructors must be member functions."))?;
Lukasz Anforowicz9bab8352021-12-22 17:35:31 +0000294 if !record.is_trivial_abi {
Googlerd03d05b2022-01-07 10:10:57 +0000295 return Ok(None);
Lukasz Anforowicz9bab8352021-12-22 17:35:31 +0000296 }
Lukasz Anforowicz2e41bb62022-01-11 18:23:07 +0000297 let trait_name: TokenStream;
298 let method_name: TokenStream;
299 let param_decls: TokenStream;
300 let extra_arg_expressions: TokenStream;
301 match func.params.len() {
Lukasz Anforowicz13cf7492021-12-22 15:29:52 +0000302 0 => bail!("Constructor should have at least 1 parameter (__this)"),
Lukasz Anforowicz2e41bb62022-01-11 18:23:07 +0000303 1 => {
304 trait_name = quote! { Default };
305 method_name = quote! { default };
306 param_decls = quote! {};
307 extra_arg_expressions = quote! {};
308 }
Lukasz Anforowicz73326af2022-01-05 01:13:10 +0000309 2 => {
310 let param_type = &func.params[1].type_;
Lukasz Anforowicz2e41bb62022-01-11 18:23:07 +0000311 // TODO(lukasza): Do something smart with move constructor.
Lukasz Anforowicz73326af2022-01-05 01:13:10 +0000312 if param_type.cc_type.is_const_ref_to(record) {
Lukasz Anforowicz2e41bb62022-01-11 18:23:07 +0000313 // Copy constructor
314 if should_derive_clone(record) {
315 return Ok(None);
316 } else {
317 trait_name = quote! { Clone };
318 method_name = quote! { clone };
319 param_decls = quote! { &self };
320 extra_arg_expressions = quote! { , self };
321 }
Lukasz Anforowicz73326af2022-01-05 01:13:10 +0000322 } else {
Lukasz Anforowicz2e41bb62022-01-11 18:23:07 +0000323 let param_ident = &param_idents[1];
324 let param_type = &param_types[1];
325 trait_name = quote! { From< #param_type > };
326 method_name = quote! { from };
327 param_decls = quote! { #param_ident: #param_type };
328 extra_arg_expressions = quote! { , #param_ident };
Lukasz Anforowicz73326af2022-01-05 01:13:10 +0000329 }
330 }
331 _ => {
332 // TODO(b/200066396): Map other constructors to something.
Googlerd03d05b2022-01-07 10:10:57 +0000333 return Ok(None);
Lukasz Anforowicz73326af2022-01-05 01:13:10 +0000334 }
335 };
Lukasz Anforowiczbedbdee2022-01-05 01:14:52 +0000336 // SAFETY: A user-defined constructor is not guaranteed to
337 // initialize all the fields. To make the `assume_init()` call
338 // below safe, the memory is zero-initialized first. This is safer,
339 // because zero-initialized memory represents a valid value for the
340 // currently supported field types (this may change once the
341 // bindings generator starts supporting reference fields).
342 // TODO(b/213243309): Double-check if zero-initialization is
343 // desirable here.
Lukasz Anforowicz73326af2022-01-05 01:13:10 +0000344 let struct_name = make_ident(&record.identifier.identifier);
Googlerd03d05b2022-01-07 10:10:57 +0000345 api_func = quote! {
Lukasz Anforowicz73326af2022-01-05 01:13:10 +0000346 #doc_comment
347 impl #trait_name for #struct_name {
348 #[inline(always)]
Lukasz Anforowicz2e41bb62022-01-11 18:23:07 +0000349 fn #method_name #generic_params( #param_decls ) -> Self {
Lukasz Anforowiczbedbdee2022-01-05 01:14:52 +0000350 let mut tmp = std::mem::MaybeUninit::<Self>::zeroed();
Lukasz Anforowicz73326af2022-01-05 01:13:10 +0000351 unsafe {
Lukasz Anforowicz2e41bb62022-01-11 18:23:07 +0000352 crate::detail::#thunk_ident(&mut tmp #extra_arg_expressions );
Lukasz Anforowicz73326af2022-01-05 01:13:10 +0000353 tmp.assume_init()
Lukasz Anforowicz13cf7492021-12-22 15:29:52 +0000354 }
355 }
Lukasz Anforowicz13cf7492021-12-22 15:29:52 +0000356 }
Googlerd03d05b2022-01-07 10:10:57 +0000357 };
358 function_id = FunctionId {
359 self_type: Some(struct_name.into()),
360 function_path: syn::parse2(quote! { #trait_name :: #method_name })?,
361 };
Lukasz Anforowicz13cf7492021-12-22 15:29:52 +0000362 }
Michael Forstered642022021-10-04 09:48:25 +0000363 };
364
Lukasz Anforowicz6d553632022-01-06 21:36:14 +0000365 let thunk = {
Devin Jeanpierre7e9a1de2021-12-03 08:04:22 +0000366 let thunk_attr = if can_skip_cc_thunk(func) {
367 quote! {#[link_name = #mangled_name]}
368 } else {
369 quote! {}
370 };
371
Lukasz Anforowicz40c2eb82022-01-11 18:22:31 +0000372 // For constructors inject MaybeUninit into the type of `__this_` parameter.
373 let mut param_types = param_types;
374 if func.name == UnqualifiedIdentifier::Constructor {
375 if param_types.is_empty() || func.params.is_empty() {
376 bail!("Constructors should have at least one parameter (__this)");
377 }
378 param_types[0] = RsTypeKind::new(&func.params[0].type_.rs_type, ir)?
379 .format_constructor_this_param(ir, &lifetime_to_name)?;
380 }
381
Devin Jeanpierre7e9a1de2021-12-03 08:04:22 +0000382 quote! {
383 #thunk_attr
Lukasz Anforowicz4ad012b2021-12-15 18:13:40 +0000384 pub(crate) fn #thunk_ident #generic_params( #( #param_idents: #param_types ),*
385 ) #return_type_fragment ;
Devin Jeanpierre7e9a1de2021-12-03 08:04:22 +0000386 }
Michael Forstered642022021-10-04 09:48:25 +0000387 };
388
Googlerd03d05b2022-01-07 10:10:57 +0000389 Ok(Some((api_func.into(), thunk.into(), function_id)))
Michael Forstered642022021-10-04 09:48:25 +0000390}
391
Michael Forstercc5941a2021-10-07 07:12:24 +0000392fn generate_doc_comment(comment: &Option<String>) -> TokenStream {
393 match comment {
Michael Forster028800b2021-10-05 12:39:59 +0000394 Some(text) => {
Marcel Hlopko89547752021-12-10 09:39:41 +0000395 // token_stream_printer (and rustfmt) don't put a space between /// and the doc
396 // comment, let's add it here so our comments are pretty.
Michael Forster028800b2021-10-05 12:39:59 +0000397 let doc = format!(" {}", text.replace("\n", "\n "));
398 quote! {#[doc=#doc]}
399 }
400 None => quote! {},
Michael Forstercc5941a2021-10-07 07:12:24 +0000401 }
402}
Lukasz Anforowiczdaff0402021-12-23 00:37:50 +0000403
404fn format_generic_params<T: quote::ToTokens>(params: impl IntoIterator<Item = T>) -> TokenStream {
405 let mut params = params.into_iter().peekable();
406 if params.peek().is_none() {
407 quote! {}
408 } else {
409 quote! { < #( #params ),* > }
410 }
411}
412
Michael Forsterbee84482021-10-13 08:35:38 +0000413/// Generates Rust source code for a given `Record` and associated assertions as
414/// a tuple.
Marcel Hlopkoc0956cf2021-11-29 08:31:28 +0000415fn generate_record(record: &Record, ir: &IR) -> Result<(RsSnippet, RsSnippet)> {
Michael Forstercc5941a2021-10-07 07:12:24 +0000416 let ident = make_ident(&record.identifier.identifier);
417 let doc_comment = generate_doc_comment(&record.doc_comment);
Marcel Hlopkob4b28742021-09-15 12:45:20 +0000418 let field_idents =
419 record.fields.iter().map(|f| make_ident(&f.identifier.identifier)).collect_vec();
Michael Forstercc5941a2021-10-07 07:12:24 +0000420 let field_doc_coments =
421 record.fields.iter().map(|f| generate_doc_comment(&f.doc_comment)).collect_vec();
Devin Jeanpierre09c6f452021-09-29 07:34:24 +0000422 let field_types = record
423 .fields
424 .iter()
Devin Jeanpierre7e9a1de2021-12-03 08:04:22 +0000425 .map(|f| {
Lukasz Anforowicz6d553632022-01-06 21:36:14 +0000426 let formatted =
427 format_rs_type(&f.type_.rs_type, ir, &HashMap::new()).with_context(|| {
Googlerb7e361d2022-01-04 14:02:59 +0000428 format!("Failed to format type for field {:?} on record {:?}", f, record)
429 })?;
Lukasz Anforowicz6d553632022-01-06 21:36:14 +0000430 let formatted = match record.destructor.definition {
431 // TODO(b/212690698): Avoid (somewhat unergonomic) ManuallyDrop if
432 // we can ask Rust to preserve field destruction order in
433 // NontrivialMembers case.
434 SpecialMemberDefinition::NontrivialMembers
435 | SpecialMemberDefinition::NontrivialUserDefined => {
436 quote! { std::mem::ManuallyDrop<#formatted> }
437 }
438 _ => formatted,
439 };
Devin Jeanpierre7e9a1de2021-12-03 08:04:22 +0000440 Ok(formatted)
441 })
Devin Jeanpierre09c6f452021-09-29 07:34:24 +0000442 .collect::<Result<Vec<_>>>()?;
Googlerec589eb2021-09-17 07:45:39 +0000443 let field_accesses = record
444 .fields
445 .iter()
446 .map(|f| {
447 if f.access == AccessSpecifier::Public {
448 quote! { pub }
449 } else {
450 quote! {}
451 }
452 })
453 .collect_vec();
Googlerec648ff2021-09-23 07:19:53 +0000454 let size = record.size;
455 let alignment = record.alignment;
Googleraaa0a532021-10-01 09:11:27 +0000456 let field_assertions =
457 record.fields.iter().zip(field_idents.iter()).map(|(field, field_ident)| {
458 let offset = field.offset;
459 quote! {
460 // The IR contains the offset in bits, while offset_of!()
461 // returns the offset in bytes, so we need to convert.
Googler209b10a2021-12-06 09:11:57 +0000462 const _: () = assert!(offset_of!(#ident, #field_ident) * 8 == #offset);
Googleraaa0a532021-10-01 09:11:27 +0000463 }
464 });
Michael Forsterdb8101a2021-10-08 06:56:03 +0000465 let mut record_features = BTreeSet::new();
466 let mut assertion_features = BTreeSet::new();
Devin Jeanpierre273eeae2021-10-06 13:29:35 +0000467
468 // TODO(mboehme): For the time being, we're using unstable features to
469 // be able to use offset_of!() in static assertions. This is fine for a
470 // prototype, but longer-term we want to either get those features
471 // stabilized or find an alternative. For more details, see
472 // b/200120034#comment15
Michael Forsterdb8101a2021-10-08 06:56:03 +0000473 assertion_features.insert(make_ident("const_ptr_offset_from"));
Devin Jeanpierre2ed14ec2021-10-06 11:32:19 +0000474
475 let derives = generate_copy_derives(record);
Devin Jeanpierre9227d2c2021-10-06 12:26:05 +0000476 let derives = if derives.is_empty() {
477 quote! {}
478 } else {
479 quote! {#[derive( #(#derives),* )]}
480 };
Devin Jeanpierre273eeae2021-10-06 13:29:35 +0000481 let unpin_impl;
Devin Jeanpierree6e16652021-12-22 15:54:46 +0000482 if record.is_unpin() {
Devin Jeanpierre273eeae2021-10-06 13:29:35 +0000483 unpin_impl = quote! {};
Devin Jeanpierreea700d32021-10-06 11:33:56 +0000484 } else {
Michael Forsterbee84482021-10-13 08:35:38 +0000485 // negative_impls are necessary for universal initialization due to Rust's
486 // coherence rules: PhantomPinned isn't enough to prove to Rust that a
487 // blanket impl that requires Unpin doesn't apply. See http://<internal link>=h.f6jp8ifzgt3n
Michael Forsterdb8101a2021-10-08 06:56:03 +0000488 record_features.insert(make_ident("negative_impls"));
489 unpin_impl = quote! {
490 __NEWLINE__ __NEWLINE__
491 impl !Unpin for #ident {}
492 };
Devin Jeanpierre273eeae2021-10-06 13:29:35 +0000493 }
494
Googlerf4792062021-10-20 07:21:21 +0000495 let empty_struct_placeholder_field = if record.fields.is_empty() {
496 quote! {
497 /// Prevent empty C++ struct being zero-size in Rust.
Lukasz Anforowicz13cf7492021-12-22 15:29:52 +0000498 placeholder: std::mem::MaybeUninit<u8>,
Googlerf4792062021-10-20 07:21:21 +0000499 }
500 } else {
501 quote! {}
502 };
503
Michael Forsterdb8101a2021-10-08 06:56:03 +0000504 let record_tokens = quote! {
Michael Forster028800b2021-10-05 12:39:59 +0000505 #doc_comment
Devin Jeanpierre9227d2c2021-10-06 12:26:05 +0000506 #derives
Marcel Hlopkob4b28742021-09-15 12:45:20 +0000507 #[repr(C)]
508 pub struct #ident {
Michael Forstercc5941a2021-10-07 07:12:24 +0000509 #( #field_doc_coments #field_accesses #field_idents: #field_types, )*
Googlerf4792062021-10-20 07:21:21 +0000510 #empty_struct_placeholder_field
Marcel Hlopkob4b28742021-09-15 12:45:20 +0000511 }
Googlerec648ff2021-09-23 07:19:53 +0000512
Devin Jeanpierreea700d32021-10-06 11:33:56 +0000513 #unpin_impl
Devin Jeanpierre273eeae2021-10-06 13:29:35 +0000514 };
515
Michael Forsterdb8101a2021-10-08 06:56:03 +0000516 let assertion_tokens = quote! {
Googler209b10a2021-12-06 09:11:57 +0000517 const _: () = assert!(std::mem::size_of::<#ident>() == #size);
518 const _: () = assert!(std::mem::align_of::<#ident>() == #alignment);
Michael Forsterdb8101a2021-10-08 06:56:03 +0000519 #( #field_assertions )*
520 };
521
522 Ok((
523 RsSnippet { features: record_features, tokens: record_tokens },
524 RsSnippet { features: assertion_features, tokens: assertion_tokens },
525 ))
Marcel Hlopkob4b28742021-09-15 12:45:20 +0000526}
527
Lukasz Anforowicz2e41bb62022-01-11 18:23:07 +0000528fn should_derive_clone(record: &Record) -> bool {
529 record.is_trivial_abi
Devin Jeanpierre2ed14ec2021-10-06 11:32:19 +0000530 && record.copy_constructor.access == ir::AccessSpecifier::Public
531 && record.copy_constructor.definition == SpecialMemberDefinition::Trivial
Lukasz Anforowicz2e41bb62022-01-11 18:23:07 +0000532}
533
534fn generate_copy_derives(record: &Record) -> Vec<Ident> {
535 if should_derive_clone(record) {
Devin Jeanpierre2ed14ec2021-10-06 11:32:19 +0000536 // TODO(b/202258760): Make `Copy` inclusion configurable.
537 vec![make_ident("Clone"), make_ident("Copy")]
538 } else {
539 vec![]
540 }
541}
542
Googler6a0a5252022-01-11 14:08:09 +0000543fn generate_type_alias(type_alias: &TypeAlias, ir: &IR) -> Result<TokenStream> {
544 let ident = make_ident(&type_alias.identifier.identifier);
545 let underlying_type = format_rs_type(&type_alias.underlying_type.rs_type, ir, &HashMap::new())
546 .with_context(|| format!("Failed to format underlying type for {:?}", type_alias))?;
547 Ok(quote! {pub type #ident = #underlying_type;})
548}
549
Michael Forster523dbd42021-10-12 11:05:44 +0000550/// Generates Rust source code for a given `UnsupportedItem`.
551fn generate_unsupported(item: &UnsupportedItem) -> Result<TokenStream> {
Googler48a74dd2021-10-25 07:31:53 +0000552 let location = if item.source_loc.filename.is_empty() {
553 "<unknown location>".to_string()
554 } else {
555 // TODO(forster): The "google3" prefix should probably come from a command line
556 // argument.
557 // TODO(forster): Consider linking to the symbol instead of to the line number
558 // to avoid wrong links while generated files have not caught up.
559 format!("google3/{};l={}", &item.source_loc.filename, &item.source_loc.line)
560 };
Michael Forster6a184ad2021-10-12 13:04:05 +0000561 let message = format!(
Googler48a74dd2021-10-25 07:31:53 +0000562 "{}\nError while generating bindings for item '{}':\n{}",
563 &location, &item.name, &item.message
Michael Forster6a184ad2021-10-12 13:04:05 +0000564 );
Michael Forster523dbd42021-10-12 11:05:44 +0000565 Ok(quote! { __COMMENT__ #message })
566}
567
Michael Forsterf1dce422021-10-13 09:50:16 +0000568/// Generates Rust source code for a given `Comment`.
569fn generate_comment(comment: &Comment) -> Result<TokenStream> {
570 let text = &comment.text;
571 Ok(quote! { __COMMENT__ #text })
572}
573
Marcel Hlopko89547752021-12-10 09:39:41 +0000574fn generate_rs_api(ir: &IR) -> Result<TokenStream> {
Michael Forstered642022021-10-04 09:48:25 +0000575 let mut items = vec![];
Marcel Hlopko42abfc82021-08-09 07:03:17 +0000576 let mut thunks = vec![];
Michael Forsterdb8101a2021-10-08 06:56:03 +0000577 let mut assertions = vec![];
Marcel Hlopko42abfc82021-08-09 07:03:17 +0000578
Googler454f2652021-12-06 12:53:12 +0000579 // We import nullable pointers as an Option<&T> and assume that at the ABI
580 // level, None is represented as a zero pointer value whereas Some is
581 // represented as as non-zero pointer value. This seems like a pretty safe
582 // assumption to make, but to provide some safeguard, assert that
583 // `Option<&i32>` and `&i32` have the same size.
584 assertions.push(quote! {
585 const _: () = assert!(std::mem::size_of::<Option<&i32>>() == std::mem::size_of::<&i32>());
586 });
587
Michael Forsterbee84482021-10-13 08:35:38 +0000588 // TODO(jeanpierreda): Delete has_record, either in favor of using RsSnippet, or not
589 // having uses. See https://chat.google.com/room/AAAAnQmj8Qs/6QbkSvWcfhA
Devin Jeanpierreea700d32021-10-06 11:33:56 +0000590 let mut has_record = false;
Devin Jeanpierre273eeae2021-10-06 13:29:35 +0000591 let mut features = BTreeSet::new();
Marcel Hlopko42abfc82021-08-09 07:03:17 +0000592
Devin Jeanpierred6da7002021-10-21 12:55:20 +0000593 // For #![rustfmt::skip].
594 features.insert(make_ident("custom_inner_attributes"));
595
Googlerd03d05b2022-01-07 10:10:57 +0000596 // Identify all functions having overloads that we can't import (yet).
597 // TODO(b/213280424): Implement support for overloaded functions.
598 let mut seen_funcs = HashSet::new();
599 let mut overloaded_funcs = HashSet::new();
600 for func in ir.functions() {
601 if let Some((_, _, function_id)) = generate_func(func, ir)? {
602 if !seen_funcs.insert(function_id.clone()) {
603 overloaded_funcs.insert(function_id);
604 }
605 }
606 }
607
Marcel Hlopko3b9bf9e2021-11-29 08:25:14 +0000608 for item in ir.items() {
Michael Forstered642022021-10-04 09:48:25 +0000609 match item {
610 Item::Func(func) => {
Googlerd03d05b2022-01-07 10:10:57 +0000611 if let Some((snippet, thunk, function_id)) = generate_func(func, ir)? {
612 if overloaded_funcs.contains(&function_id) {
613 items.push(generate_unsupported(&UnsupportedItem {
614 name: cxx_function_name(func, ir)?,
615 message: "Cannot generate bindings for overloaded function".to_string(),
616 source_loc: func.source_loc.clone(),
617 })?);
618 continue;
619 }
620 features.extend(snippet.features);
621 features.extend(thunk.features);
622 items.push(snippet.tokens);
623 thunks.push(thunk.tokens);
624 }
Marcel Hlopko42abfc82021-08-09 07:03:17 +0000625 }
Michael Forstered642022021-10-04 09:48:25 +0000626 Item::Record(record) => {
Googler6a0a5252022-01-11 14:08:09 +0000627 if !ir.is_current_target(&record.owning_target)
628 && !ir.is_stdlib_target(&record.owning_target)
629 {
Marcel Hlopkoa0f38662021-12-03 08:45:26 +0000630 continue;
631 }
Marcel Hlopkoc0956cf2021-11-29 08:31:28 +0000632 let (snippet, assertions_snippet) = generate_record(record, ir)?;
Devin Jeanpierre273eeae2021-10-06 13:29:35 +0000633 features.extend(snippet.features);
Michael Forsterdb8101a2021-10-08 06:56:03 +0000634 features.extend(assertions_snippet.features);
Devin Jeanpierre273eeae2021-10-06 13:29:35 +0000635 items.push(snippet.tokens);
Michael Forsterdb8101a2021-10-08 06:56:03 +0000636 assertions.push(assertions_snippet.tokens);
Devin Jeanpierreea700d32021-10-06 11:33:56 +0000637 has_record = true;
Michael Forstered642022021-10-04 09:48:25 +0000638 }
Googler098c4582022-01-10 12:29:34 +0000639 Item::TypeAlias(type_alias) => {
Googler6a0a5252022-01-11 14:08:09 +0000640 if !ir.is_current_target(&type_alias.owning_target)
641 && !ir.is_stdlib_target(&type_alias.owning_target)
642 {
643 continue;
644 }
645 items.push(generate_type_alias(type_alias, ir)?);
Googler098c4582022-01-10 12:29:34 +0000646 }
Michael Forster523dbd42021-10-12 11:05:44 +0000647 Item::UnsupportedItem(unsupported) => items.push(generate_unsupported(unsupported)?),
Michael Forsterf1dce422021-10-13 09:50:16 +0000648 Item::Comment(comment) => items.push(generate_comment(comment)?),
Michael Forstered642022021-10-04 09:48:25 +0000649 }
Marcel Hlopko42abfc82021-08-09 07:03:17 +0000650 }
651
Marcel Hlopkob4b28742021-09-15 12:45:20 +0000652 let mod_detail = if thunks.is_empty() {
653 quote! {}
654 } else {
655 quote! {
656 mod detail {
Googler55647142022-01-11 12:37:39 +0000657 #[allow(unused_imports)]
Devin Jeanpierred4dde0e2021-10-13 20:48:25 +0000658 use super::*;
Marcel Hlopkob4b28742021-09-15 12:45:20 +0000659 extern "C" {
660 #( #thunks )*
661 }
Marcel Hlopko42abfc82021-08-09 07:03:17 +0000662 }
663 }
664 };
665
Devin Jeanpierreea700d32021-10-06 11:33:56 +0000666 let imports = if has_record {
Googlerec648ff2021-09-23 07:19:53 +0000667 quote! {
Googleraaa0a532021-10-01 09:11:27 +0000668 use memoffset_unstable_const::offset_of;
Googlerec648ff2021-09-23 07:19:53 +0000669 }
Michael Forstered642022021-10-04 09:48:25 +0000670 } else {
671 quote! {}
Googlerec648ff2021-09-23 07:19:53 +0000672 };
673
Devin Jeanpierre273eeae2021-10-06 13:29:35 +0000674 let features = if features.is_empty() {
675 quote! {}
676 } else {
677 quote! {
678 #![feature( #(#features),* )]
679 }
680 };
681
Marcel Hlopko89547752021-12-10 09:39:41 +0000682 Ok(quote! {
Googler55647142022-01-11 12:37:39 +0000683 #features __NEWLINE__
684 #![allow(non_camel_case_types)] __NEWLINE__
685 #![allow(non_snake_case)] __NEWLINE__ __NEWLINE__
686
Michael Forsterdb8101a2021-10-08 06:56:03 +0000687 #imports __NEWLINE__ __NEWLINE__
Googlerec648ff2021-09-23 07:19:53 +0000688
Michael Forsterdb8101a2021-10-08 06:56:03 +0000689 #( #items __NEWLINE__ __NEWLINE__ )*
Marcel Hlopkob4b28742021-09-15 12:45:20 +0000690
Michael Forsterdb8101a2021-10-08 06:56:03 +0000691 #mod_detail __NEWLINE__ __NEWLINE__
692
693 #( #assertions __NEWLINE__ __NEWLINE__ )*
Marcel Hlopko89547752021-12-10 09:39:41 +0000694 })
Marcel Hlopko42abfc82021-08-09 07:03:17 +0000695}
696
697fn make_ident(ident: &str) -> Ident {
698 format_ident!("{}", ident)
699}
700
Googler6a0a5252022-01-11 14:08:09 +0000701fn rs_type_name_for_target_and_identifier(
702 owning_target: &BlazeLabel,
703 identifier: &ir::Identifier,
704 ir: &IR,
705) -> Result<TokenStream> {
706 let ident = make_ident(identifier.identifier.as_str());
707
708 if ir.is_current_target(owning_target) || ir.is_stdlib_target(owning_target) {
709 Ok(quote! {#ident})
710 } else {
711 let owning_crate = make_ident(owning_target.target_name()?);
712 Ok(quote! {#owning_crate::#ident})
713 }
714}
715
Lukasz Anforowicz40c2eb82022-01-11 18:22:31 +0000716#[derive(Debug, Eq, PartialEq)]
717enum Mutability {
718 Const,
719 Mut,
720}
721
722impl Mutability {
723 fn format_for_pointer(&self) -> TokenStream {
724 match self {
725 Mutability::Mut => quote! {mut},
726 Mutability::Const => quote! {const},
727 }
728 }
729
730 fn format_for_reference(&self) -> TokenStream {
731 match self {
732 Mutability::Mut => quote! {mut},
733 Mutability::Const => quote! {},
734 }
735 }
736}
737
738// TODO(b/213947473): Instead of having a separate RsTypeKind here, consider
739// changing ir::RsType into a similar `enum`, with fields that contain
740// references (e.g. &'ir Record`) instead of DeclIds.
741#[derive(Debug)]
742enum RsTypeKind<'ir> {
743 Pointer { pointee: Box<RsTypeKind<'ir>>, mutability: Mutability },
744 Reference { referent: Box<RsTypeKind<'ir>>, mutability: Mutability, lifetime_id: LifetimeId },
745 Record(&'ir Record),
746 TypeAlias(&'ir TypeAlias),
747 Unit,
748 Other { name: &'ir str, type_args: Vec<RsTypeKind<'ir>> },
749}
750
751impl<'ir> RsTypeKind<'ir> {
752 pub fn new(ty: &'ir ir::RsType, ir: &'ir IR) -> Result<Self> {
753 // The lambdas deduplicate code needed by multiple `match` branches.
754 let get_type_args = || -> Result<Vec<RsTypeKind<'ir>>> {
755 ty.type_args.iter().map(|type_arg| RsTypeKind::<'ir>::new(type_arg, ir)).collect()
756 };
757 let get_pointee = || -> Result<Box<RsTypeKind<'ir>>> {
758 if ty.type_args.len() != 1 {
759 bail!("Missing pointee/referent type (need exactly 1 type argument): {:?}", ty);
760 }
761 Ok(Box::new(get_type_args()?.remove(0)))
762 };
763 let get_lifetime = || -> Result<LifetimeId> {
764 if ty.lifetime_args.len() != 1 {
765 bail!("Missing reference lifetime (need exactly 1 lifetime argument): {:?}", ty);
766 }
767 Ok(ty.lifetime_args[0])
768 };
769
770 let result = match ty.name.as_deref() {
771 None => {
772 ensure!(
773 ty.type_args.is_empty(),
774 "Type arguments on records nor type aliases are not yet supported: {:?}",
775 ty
776 );
777 match ir.item_for_type(ty)? {
778 Item::Record(record) => RsTypeKind::Record(record),
779 Item::TypeAlias(type_alias) => RsTypeKind::TypeAlias(type_alias),
780 other_item => bail!("Item does not define a type: {:?}", other_item),
781 }
782 }
783 Some(name) => match name {
784 "()" => {
785 if !ty.type_args.is_empty() {
786 bail!("Unit type must not have type arguments: {:?}", ty);
787 }
788 RsTypeKind::Unit
789 }
790 "*mut" => {
791 RsTypeKind::Pointer { pointee: get_pointee()?, mutability: Mutability::Mut }
792 }
793 "*const" => {
794 RsTypeKind::Pointer { pointee: get_pointee()?, mutability: Mutability::Const }
795 }
796 "&mut" => RsTypeKind::Reference {
797 referent: get_pointee()?,
798 mutability: Mutability::Mut,
799 lifetime_id: get_lifetime()?,
800 },
801 "&" => RsTypeKind::Reference {
802 referent: get_pointee()?,
803 mutability: Mutability::Const,
804 lifetime_id: get_lifetime()?,
805 },
806 name => RsTypeKind::Other { name, type_args: get_type_args()? },
807 },
808 };
809 Ok(result)
810 }
811
812 pub fn format(
813 &self,
814 ir: &IR,
815 lifetime_to_name: &HashMap<LifetimeId, String>,
816 ) -> Result<TokenStream> {
817 let result = match self {
818 RsTypeKind::Pointer { pointee, mutability } => {
819 let mutability = mutability.format_for_pointer();
820 let nested_type = pointee.format(ir, lifetime_to_name)?;
821 quote! {* #mutability #nested_type}
822 }
823 RsTypeKind::Reference { referent, mutability, lifetime_id } => {
824 let mutability = mutability.format_for_reference();
825 let lifetime = Self::format_lifetime(lifetime_id, lifetime_to_name)?;
826 let nested_type = referent.format(ir, lifetime_to_name)?;
827 quote! {& #lifetime #mutability #nested_type}
828 }
829 RsTypeKind::Record(record) => rs_type_name_for_target_and_identifier(
830 &record.owning_target,
831 &record.identifier,
832 ir,
833 )?,
834 RsTypeKind::TypeAlias(type_alias) => rs_type_name_for_target_and_identifier(
835 &type_alias.owning_target,
836 &type_alias.identifier,
837 ir,
838 )?,
839 RsTypeKind::Unit => quote! {()},
840 RsTypeKind::Other { name, type_args } => {
841 let ident = make_ident(name);
842 let generic_params = format_generic_params(
843 type_args
844 .iter()
845 .map(|type_arg| type_arg.format(ir, lifetime_to_name))
846 .collect::<Result<Vec<_>>>()?,
847 );
848 quote! {#ident #generic_params}
849 }
850 };
851 Ok(result)
852 }
853
854 /// Formats the Rust type of `__this` parameter of a constructor - injecting
855 /// MaybeUninit to return something like `&'a mut MaybeUninit<SomeStruct>`.
856 pub fn format_constructor_this_param(
857 &self,
858 ir: &IR,
859 lifetime_to_name: &HashMap<LifetimeId, String>,
860 ) -> Result<TokenStream> {
861 let nested_type = match self {
862 RsTypeKind::Pointer {
863 pointee: pointee_or_referent,
864 mutability: Mutability::Mut,
865 ..
866 }
867 | RsTypeKind::Reference {
868 referent: pointee_or_referent,
869 mutability: Mutability::Mut,
870 ..
871 } => pointee_or_referent.format(ir, lifetime_to_name)?,
872 _ => bail!("Unexpected type of `__this` parameter in a constructor: {:?}", self),
873 };
874 let lifetime = match self {
875 RsTypeKind::Pointer { .. } => quote! {},
876 RsTypeKind::Reference { lifetime_id, .. } => {
877 Self::format_lifetime(lifetime_id, lifetime_to_name)?
878 }
879 _ => unreachable!(), // Because of the earlier `match`.
880 };
881 // `mut` can be hardcoded, because of the `match` patterns above.
882 Ok(quote! { & #lifetime mut std::mem::MaybeUninit< #nested_type > })
883 }
884
885 fn format_lifetime(
886 lifetime_id: &LifetimeId,
887 lifetime_to_name: &HashMap<LifetimeId, String>,
888 ) -> Result<TokenStream> {
889 let lifetime_name = lifetime_to_name.get(lifetime_id).ok_or_else(|| {
890 anyhow!("`lifetime_to_name` doesn't have an entry for {:?}", lifetime_id)
891 })?;
892 let lifetime =
893 syn::Lifetime::new(&format!("'{}", lifetime_name), proc_macro2::Span::call_site());
894 Ok(quote! { #lifetime })
895 }
896}
897
Googler7cced422021-12-06 11:58:39 +0000898fn format_rs_type(
899 ty: &ir::RsType,
900 ir: &IR,
901 lifetime_to_name: &HashMap<LifetimeId, String>,
902) -> Result<TokenStream> {
Lukasz Anforowicz40c2eb82022-01-11 18:22:31 +0000903 RsTypeKind::new(ty, ir)
904 .and_then(|kind| kind.format(ir, lifetime_to_name))
905 .with_context(|| format!("Failed to format Rust type {:?}", ty))
Devin Jeanpierre7a7328e2021-09-17 07:10:08 +0000906}
907
Googler6a0a5252022-01-11 14:08:09 +0000908fn cc_type_name_for_item(item: &ir::Item) -> Result<TokenStream> {
909 let (disambiguator_fragment, identifier) = match item {
910 Item::Record(record) => (quote! { class }, &record.identifier),
911 Item::TypeAlias(type_alias) => (quote! {}, &type_alias.identifier),
912 _ => bail!("Item does not define a type: {:?}", item),
913 };
914
915 let ident = make_ident(identifier.identifier.as_str());
916 Ok(quote! { #disambiguator_fragment #ident })
917}
918
Marcel Hlopkoc0956cf2021-11-29 08:31:28 +0000919fn format_cc_type(ty: &ir::CcType, ir: &IR) -> Result<TokenStream> {
Devin Jeanpierre09c6f452021-09-29 07:34:24 +0000920 let const_fragment = if ty.is_const {
Devin Jeanpierre184f9ac2021-09-17 13:47:03 +0000921 quote! {const}
922 } else {
923 quote! {}
924 };
Marcel Hlopkoc0956cf2021-11-29 08:31:28 +0000925 if let Some(ref name) = ty.name {
926 match name.as_str() {
927 "*" => {
Googlerff7fc232021-12-02 09:43:00 +0000928 if ty.type_args.len() != 1 {
929 bail!("Invalid pointer type (need exactly 1 type argument): {:?}", ty);
Marcel Hlopkoc0956cf2021-11-29 08:31:28 +0000930 }
Googlerff7fc232021-12-02 09:43:00 +0000931 assert_eq!(ty.type_args.len(), 1);
932 let nested_type = format_cc_type(&ty.type_args[0], ir)?;
Marcel Hlopkoc0956cf2021-11-29 08:31:28 +0000933 Ok(quote! {#nested_type * #const_fragment})
Devin Jeanpierre7a7328e2021-09-17 07:10:08 +0000934 }
Lukasz Anforowicz275fa922022-01-05 16:13:20 +0000935 "&" => {
936 if ty.type_args.len() != 1 {
937 bail!("Invalid reference type (need exactly 1 type argument): {:?}", ty);
938 }
939 let nested_type = format_cc_type(&ty.type_args[0], ir)?;
940 Ok(quote! {#nested_type &})
941 }
Lukasz Anforowicz957cbf22022-01-05 16:14:05 +0000942 cc_type_name => {
Googlerff7fc232021-12-02 09:43:00 +0000943 if !ty.type_args.is_empty() {
Marcel Hlopkoc0956cf2021-11-29 08:31:28 +0000944 bail!("Type not yet supported: {:?}", ty);
945 }
Lukasz Anforowicz957cbf22022-01-05 16:14:05 +0000946 let idents = cc_type_name.split_whitespace().map(make_ident);
947 Ok(quote! {#( #idents )* #const_fragment})
Devin Jeanpierre7a7328e2021-09-17 07:10:08 +0000948 }
Devin Jeanpierre7a7328e2021-09-17 07:10:08 +0000949 }
Marcel Hlopkoc0956cf2021-11-29 08:31:28 +0000950 } else {
Googler6a0a5252022-01-11 14:08:09 +0000951 let item = ir.item_for_type(ty)?;
952 let type_name = cc_type_name_for_item(item)?;
953 Ok(quote! {#const_fragment #type_name})
Devin Jeanpierre7a7328e2021-09-17 07:10:08 +0000954 }
955}
956
Marcel Hlopkoa0f38662021-12-03 08:45:26 +0000957fn cc_struct_layout_assertion(record: &Record, ir: &IR) -> TokenStream {
Googler6a0a5252022-01-11 14:08:09 +0000958 if !ir.is_current_target(&record.owning_target) && !ir.is_stdlib_target(&record.owning_target) {
Marcel Hlopkoa0f38662021-12-03 08:45:26 +0000959 return quote! {};
960 }
Googler5ea88642021-09-29 08:05:59 +0000961 let record_ident = make_ident(&record.identifier.identifier);
962 let size = Literal::usize_unsuffixed(record.size);
963 let alignment = Literal::usize_unsuffixed(record.alignment);
Lukasz Anforowicz74704712021-12-22 15:30:31 +0000964 let field_assertions =
965 record.fields.iter().filter(|f| f.access == AccessSpecifier::Public).map(|field| {
966 let field_ident = make_ident(&field.identifier.identifier);
967 let offset = Literal::usize_unsuffixed(field.offset);
968 // The IR contains the offset in bits, while C++'s offsetof()
969 // returns the offset in bytes, so we need to convert.
970 quote! {
Googler972d3582022-01-11 10:17:22 +0000971 static_assert(offsetof(class #record_ident, #field_ident) * 8 == #offset);
Lukasz Anforowicz74704712021-12-22 15:30:31 +0000972 }
973 });
Googler5ea88642021-09-29 08:05:59 +0000974 quote! {
Googler972d3582022-01-11 10:17:22 +0000975 static_assert(sizeof(class #record_ident) == #size);
976 static_assert(alignof(class #record_ident) == #alignment);
Googler5ea88642021-09-29 08:05:59 +0000977 #( #field_assertions )*
978 }
979}
980
Googlera675ae02021-12-07 08:04:59 +0000981fn thunk_ident(func: &Func) -> Ident {
982 format_ident!("__rust_thunk__{}", func.mangled_name)
Devin Jeanpierref2ec8712021-10-13 20:47:16 +0000983}
984
Marcel Hlopko89547752021-12-10 09:39:41 +0000985fn generate_rs_api_impl(ir: &IR) -> Result<TokenStream> {
Michael Forsterbee84482021-10-13 08:35:38 +0000986 // This function uses quote! to generate C++ source code out of convenience.
987 // This is a bold idea so we have to continously evaluate if it still makes
988 // sense or the cost of working around differences in Rust and C++ tokens is
989 // greather than the value added.
Marcel Hlopko3164eee2021-08-24 20:09:22 +0000990 //
Michael Forsterbee84482021-10-13 08:35:38 +0000991 // See rs_bindings_from_cc/
992 // token_stream_printer.rs for a list of supported placeholders.
Marcel Hlopko3164eee2021-08-24 20:09:22 +0000993 let mut thunks = vec![];
Michael Forster7ef80732021-10-01 18:12:19 +0000994 for func in ir.functions() {
Marcel Hlopko3164eee2021-08-24 20:09:22 +0000995 if can_skip_cc_thunk(&func) {
996 continue;
997 }
998
Googlera675ae02021-12-07 08:04:59 +0000999 let thunk_ident = thunk_ident(func);
Devin Jeanpierref2ec8712021-10-13 20:47:16 +00001000 let implementation_function = match &func.name {
1001 UnqualifiedIdentifier::Identifier(id) => {
Lukasz Anforowiczaab8ad22021-12-19 20:29:26 +00001002 let fn_ident = make_ident(&id.identifier);
1003 let static_method_metadata = func
1004 .member_func_metadata
1005 .as_ref()
1006 .filter(|meta| meta.instance_method_metadata.is_none());
1007 match static_method_metadata {
1008 None => quote! {#fn_ident},
1009 Some(meta) => {
1010 let record_ident = make_ident(&meta.find_record(ir)?.identifier.identifier);
1011 quote! { #record_ident :: #fn_ident }
1012 }
1013 }
Devin Jeanpierref2ec8712021-10-13 20:47:16 +00001014 }
Lukasz Anforowicz7b0042d2022-01-06 23:00:19 +00001015 // Use `destroy_at` to avoid needing to spell out the class name. Destructor identiifers
Devin Jeanpierrecc6cf092021-12-16 04:31:14 +00001016 // use the name of the type itself, without namespace qualification, template
1017 // parameters, or aliases. We do not need to use that naming scheme anywhere else in
1018 // the bindings, and it can be difficult (impossible?) to spell in the general case. By
1019 // using destroy_at, we avoid needing to determine or remember what the correct spelling
Lukasz Anforowicz7b0042d2022-01-06 23:00:19 +00001020 // is. Similar arguments apply to `construct_at`.
Lukasz Anforowicze643ec92021-12-22 15:45:15 +00001021 UnqualifiedIdentifier::Constructor => {
Lukasz Anforowicz7b0042d2022-01-06 23:00:19 +00001022 quote! { rs_api_impl_support::construct_at }
Lukasz Anforowicze643ec92021-12-22 15:45:15 +00001023 }
Devin Jeanpierref2ec8712021-10-13 20:47:16 +00001024 UnqualifiedIdentifier::Destructor => quote! {std::destroy_at},
Devin Jeanpierref2ec8712021-10-13 20:47:16 +00001025 };
Marcel Hlopkoc0956cf2021-11-29 08:31:28 +00001026 let return_type_name = format_cc_type(&func.return_type.cc_type, ir)?;
Lukasz Anforowicze643ec92021-12-22 15:45:15 +00001027 let return_stmt = if func.return_type.cc_type.is_void() {
1028 quote! {}
1029 } else {
1030 quote! { return }
1031 };
Marcel Hlopko3164eee2021-08-24 20:09:22 +00001032
1033 let param_idents =
1034 func.params.iter().map(|p| make_ident(&p.identifier.identifier)).collect_vec();
1035
Devin Jeanpierre09c6f452021-09-29 07:34:24 +00001036 let param_types = func
1037 .params
1038 .iter()
Marcel Hlopkoc0956cf2021-11-29 08:31:28 +00001039 .map(|p| format_cc_type(&p.type_.cc_type, ir))
Devin Jeanpierre09c6f452021-09-29 07:34:24 +00001040 .collect::<Result<Vec<_>>>()?;
Marcel Hlopko3164eee2021-08-24 20:09:22 +00001041
1042 thunks.push(quote! {
1043 extern "C" #return_type_name #thunk_ident( #( #param_types #param_idents ),* ) {
Lukasz Anforowicze643ec92021-12-22 15:45:15 +00001044 #return_stmt #implementation_function( #( #param_idents ),* );
Marcel Hlopko3164eee2021-08-24 20:09:22 +00001045 }
1046 });
1047 }
1048
Marcel Hlopkoa0f38662021-12-03 08:45:26 +00001049 let layout_assertions = ir.records().map(|record| cc_struct_layout_assertion(record, ir));
Googler5ea88642021-09-29 08:05:59 +00001050
Devin Jeanpierre231ef8d2021-10-27 10:50:44 +00001051 let mut standard_headers = <BTreeSet<Ident>>::new();
1052 standard_headers.insert(make_ident("memory")); // ubiquitous.
1053 if ir.records().next().is_some() {
1054 standard_headers.insert(make_ident("cstddef"));
1055 };
Googler5ea88642021-09-29 08:05:59 +00001056
Lukasz Anforowicz4457baf2021-12-23 17:24:04 +00001057 let mut includes =
1058 vec!["rs_bindings_from_cc/support/cxx20_backports.h"];
1059
Michael Forsterbee84482021-10-13 08:35:38 +00001060 // In order to generate C++ thunk in all the cases Clang needs to be able to
1061 // access declarations from public headers of the C++ library.
Lukasz Anforowicz4457baf2021-12-23 17:24:04 +00001062 includes.extend(ir.used_headers().map(|i| &i.name as &str));
Marcel Hlopko3164eee2021-08-24 20:09:22 +00001063
Marcel Hlopko89547752021-12-10 09:39:41 +00001064 Ok(quote! {
Googler5ea88642021-09-29 08:05:59 +00001065 #( __HASH_TOKEN__ include <#standard_headers> __NEWLINE__)*
Michael Forsterdb8101a2021-10-08 06:56:03 +00001066 #( __HASH_TOKEN__ include #includes __NEWLINE__)* __NEWLINE__
Marcel Hlopko3164eee2021-08-24 20:09:22 +00001067
Michael Forsterdb8101a2021-10-08 06:56:03 +00001068 #( #thunks )* __NEWLINE__ __NEWLINE__
Googler5ea88642021-09-29 08:05:59 +00001069
Michael Forsterdb8101a2021-10-08 06:56:03 +00001070 #( #layout_assertions __NEWLINE__ __NEWLINE__ )*
Marcel Hlopkoc6b726c2021-10-07 06:53:09 +00001071
1072 // To satisfy http://cs/symbol:devtools.metadata.Presubmit.CheckTerminatingNewline check.
1073 __NEWLINE__
Marcel Hlopko89547752021-12-10 09:39:41 +00001074 })
Marcel Hlopko3164eee2021-08-24 20:09:22 +00001075}
1076
Marcel Hlopko42abfc82021-08-09 07:03:17 +00001077#[cfg(test)]
1078mod tests {
Devin Jeanpierre45cb1162021-10-27 10:54:28 +00001079 use super::*;
Michael Forstered642022021-10-04 09:48:25 +00001080 use anyhow::anyhow;
Marcel Hlopko89547752021-12-10 09:39:41 +00001081 use ir_testing::{ir_from_cc, ir_from_cc_dependency, ir_func, ir_record};
1082 use token_stream_matchers::{
1083 assert_cc_matches, assert_cc_not_matches, assert_rs_matches, assert_rs_not_matches,
Devin Jeanpierre7e9a1de2021-12-03 08:04:22 +00001084 };
Michael Forsterdb8101a2021-10-08 06:56:03 +00001085 use token_stream_printer::tokens_to_string;
Marcel Hlopko42abfc82021-08-09 07:03:17 +00001086
1087 #[test]
Marcel Hlopko3b9bf9e2021-11-29 08:25:14 +00001088 // TODO(hlopko): Move this test to a more principled place where it can access
1089 // `ir_testing`.
1090 fn test_duplicate_decl_ids_err() {
1091 let mut r1 = ir_record("R1");
Marcel Hlopko264b9ad2021-12-02 21:06:44 +00001092 r1.id = DeclId(42);
Marcel Hlopko3b9bf9e2021-11-29 08:25:14 +00001093 let mut r2 = ir_record("R2");
Marcel Hlopko264b9ad2021-12-02 21:06:44 +00001094 r2.id = DeclId(42);
Marcel Hlopko3b9bf9e2021-11-29 08:25:14 +00001095 let result = make_ir_from_items([r1.into(), r2.into()]);
1096 assert!(result.is_err());
1097 assert!(result.unwrap_err().to_string().contains("Duplicate decl_id found in"));
1098 }
1099
1100 #[test]
Marcel Hlopko45fba972021-08-23 19:52:20 +00001101 fn test_simple_function() -> Result<()> {
Marcel Hlopko89547752021-12-10 09:39:41 +00001102 let ir = ir_from_cc("int Add(int a, int b);")?;
1103 let rs_api = generate_rs_api(&ir)?;
1104 assert_rs_matches!(
1105 rs_api,
1106 quote! {
Michael Forsterdb8101a2021-10-08 06:56:03 +00001107 #[inline(always)]
Marcel Hlopko89547752021-12-10 09:39:41 +00001108 pub fn Add(a: i32, b: i32) -> i32 {
Googlera675ae02021-12-07 08:04:59 +00001109 unsafe { crate::detail::__rust_thunk___Z3Addii(a, b) }
Marcel Hlopko89547752021-12-10 09:39:41 +00001110 }
1111 }
1112 );
1113 assert_rs_matches!(
1114 rs_api,
1115 quote! {
Michael Forsterdb8101a2021-10-08 06:56:03 +00001116 mod detail {
Googler55647142022-01-11 12:37:39 +00001117 #[allow(unused_imports)]
Devin Jeanpierred4dde0e2021-10-13 20:48:25 +00001118 use super::*;
Michael Forsterdb8101a2021-10-08 06:56:03 +00001119 extern "C" {
1120 #[link_name = "_Z3Addii"]
Googlera675ae02021-12-07 08:04:59 +00001121 pub(crate) fn __rust_thunk___Z3Addii(a: i32, b: i32) -> i32;
Marcel Hlopko89547752021-12-10 09:39:41 +00001122 }
1123 }
1124 }
Marcel Hlopko42abfc82021-08-09 07:03:17 +00001125 );
Michael Forsterdb8101a2021-10-08 06:56:03 +00001126
Marcel Hlopko89547752021-12-10 09:39:41 +00001127 assert_cc_not_matches!(generate_rs_api_impl(&ir)?, quote! {__rust_thunk___Z3Addii});
Michael Forsterdb8101a2021-10-08 06:56:03 +00001128
Marcel Hlopko3164eee2021-08-24 20:09:22 +00001129 Ok(())
1130 }
1131
1132 #[test]
1133 fn test_inline_function() -> Result<()> {
Marcel Hlopko89547752021-12-10 09:39:41 +00001134 let ir = ir_from_cc("inline int Add(int a, int b);")?;
1135 let rs_api = generate_rs_api(&ir)?;
1136 assert_rs_matches!(
1137 rs_api,
1138 quote! {
Michael Forsterdb8101a2021-10-08 06:56:03 +00001139 #[inline(always)]
Marcel Hlopko89547752021-12-10 09:39:41 +00001140 pub fn Add(a: i32, b: i32) -> i32 {
Googlera675ae02021-12-07 08:04:59 +00001141 unsafe { crate::detail::__rust_thunk___Z3Addii(a, b) }
Marcel Hlopko89547752021-12-10 09:39:41 +00001142 }
1143 }
1144 );
1145 assert_rs_matches!(
1146 rs_api,
1147 quote! {
Michael Forsterdb8101a2021-10-08 06:56:03 +00001148 mod detail {
Googler55647142022-01-11 12:37:39 +00001149 #[allow(unused_imports)]
Devin Jeanpierred4dde0e2021-10-13 20:48:25 +00001150 use super::*;
Michael Forsterdb8101a2021-10-08 06:56:03 +00001151 extern "C" {
Googlera675ae02021-12-07 08:04:59 +00001152 pub(crate) fn __rust_thunk___Z3Addii(a: i32, b: i32) -> i32;
Marcel Hlopko89547752021-12-10 09:39:41 +00001153 }
1154 }
1155 }
Marcel Hlopko3164eee2021-08-24 20:09:22 +00001156 );
1157
Marcel Hlopko89547752021-12-10 09:39:41 +00001158 assert_cc_matches!(
1159 generate_rs_api_impl(&ir)?,
1160 quote! {
Googlera675ae02021-12-07 08:04:59 +00001161 extern "C" int __rust_thunk___Z3Addii(int a, int b) {
Marcel Hlopko89547752021-12-10 09:39:41 +00001162 return Add(a, b);
Marcel Hlopko3164eee2021-08-24 20:09:22 +00001163 }
Marcel Hlopko89547752021-12-10 09:39:41 +00001164 }
Marcel Hlopko3164eee2021-08-24 20:09:22 +00001165 );
Marcel Hlopko42abfc82021-08-09 07:03:17 +00001166 Ok(())
1167 }
Marcel Hlopkob4b28742021-09-15 12:45:20 +00001168
1169 #[test]
Marcel Hlopkoa0f38662021-12-03 08:45:26 +00001170 fn test_simple_function_with_types_from_other_target() -> Result<()> {
1171 let ir = ir_from_cc_dependency(
1172 "inline ReturnStruct DoSomething(ParamStruct param);",
1173 "struct ReturnStruct {}; struct ParamStruct {};",
1174 )?;
1175
Marcel Hlopko89547752021-12-10 09:39:41 +00001176 let rs_api = generate_rs_api(&ir)?;
1177 assert_rs_matches!(
1178 rs_api,
1179 quote! {
Marcel Hlopkoa0f38662021-12-03 08:45:26 +00001180 #[inline(always)]
1181 pub fn DoSomething(param: dependency::ParamStruct)
Marcel Hlopko89547752021-12-10 09:39:41 +00001182 -> dependency::ReturnStruct {
Googlera675ae02021-12-07 08:04:59 +00001183 unsafe { crate::detail::__rust_thunk___Z11DoSomething11ParamStruct(param) }
Marcel Hlopko89547752021-12-10 09:39:41 +00001184 }
1185 }
1186 );
1187 assert_rs_matches!(
1188 rs_api,
1189 quote! {
1190 mod detail {
Googler55647142022-01-11 12:37:39 +00001191 #[allow(unused_imports)]
Marcel Hlopko89547752021-12-10 09:39:41 +00001192 use super::*;
1193 extern "C" {
1194 pub(crate) fn __rust_thunk___Z11DoSomething11ParamStruct(param: dependency::ParamStruct)
1195 -> dependency::ReturnStruct;
1196 }
1197 }}
Marcel Hlopkoa0f38662021-12-03 08:45:26 +00001198 );
1199
Marcel Hlopko89547752021-12-10 09:39:41 +00001200 assert_cc_matches!(
1201 generate_rs_api_impl(&ir)?,
1202 quote! {
Googler972d3582022-01-11 10:17:22 +00001203 extern "C" class ReturnStruct __rust_thunk___Z11DoSomething11ParamStruct(class ParamStruct param) {
Marcel Hlopkoa0f38662021-12-03 08:45:26 +00001204 return DoSomething(param);
1205 }
Marcel Hlopko89547752021-12-10 09:39:41 +00001206 }
Marcel Hlopkoa0f38662021-12-03 08:45:26 +00001207 );
1208 Ok(())
1209 }
1210
1211 #[test]
Marcel Hlopkob4b28742021-09-15 12:45:20 +00001212 fn test_simple_struct() -> Result<()> {
Marcel Hlopko89547752021-12-10 09:39:41 +00001213 let ir = ir_from_cc(&tokens_to_string(quote! {
1214 struct SomeStruct {
1215 int public_int;
1216 protected:
1217 int protected_int;
1218 private:
1219 int private_int;
1220 };
1221 })?)?;
Michael Forster028800b2021-10-05 12:39:59 +00001222
Marcel Hlopko89547752021-12-10 09:39:41 +00001223 let rs_api = generate_rs_api(&ir)?;
1224 assert_rs_matches!(
1225 rs_api,
1226 quote! {
Michael Forsterdb8101a2021-10-08 06:56:03 +00001227 #[derive(Clone, Copy)]
1228 #[repr(C)]
1229 pub struct SomeStruct {
1230 pub public_int: i32,
1231 protected_int: i32,
1232 private_int: i32,
Marcel Hlopko89547752021-12-10 09:39:41 +00001233 }
1234 }
1235 );
1236 assert_rs_matches!(
1237 rs_api,
1238 quote! {
Googler454f2652021-12-06 12:53:12 +00001239 const _: () = assert!(std::mem::size_of::<Option<&i32>>() == std::mem::size_of::<&i32>());
Googler209b10a2021-12-06 09:11:57 +00001240 const _: () = assert!(std::mem::size_of::<SomeStruct>() == 12usize);
1241 const _: () = assert!(std::mem::align_of::<SomeStruct>() == 4usize);
1242 const _: () = assert!(offset_of!(SomeStruct, public_int) * 8 == 0usize);
1243 const _: () = assert!(offset_of!(SomeStruct, protected_int) * 8 == 32usize);
1244 const _: () = assert!(offset_of!(SomeStruct, private_int) * 8 == 64usize);
Marcel Hlopko89547752021-12-10 09:39:41 +00001245 }
Marcel Hlopkob4b28742021-09-15 12:45:20 +00001246 );
Marcel Hlopko89547752021-12-10 09:39:41 +00001247 let rs_api_impl = generate_rs_api_impl(&ir)?;
1248 assert_cc_matches!(
1249 rs_api_impl,
1250 quote! {
Googler972d3582022-01-11 10:17:22 +00001251 extern "C" void __rust_thunk___ZN10SomeStructD1Ev(class SomeStruct * __this) {
Lukasz Anforowicze643ec92021-12-22 15:45:15 +00001252 std :: destroy_at (__this) ;
Marcel Hlopko89547752021-12-10 09:39:41 +00001253 }
1254 }
1255 );
1256 assert_cc_matches!(
1257 rs_api_impl,
1258 quote! {
Googler972d3582022-01-11 10:17:22 +00001259 static_assert(sizeof(class SomeStruct) == 12);
1260 static_assert(alignof(class SomeStruct) == 4);
1261 static_assert(offsetof(class SomeStruct, public_int) * 8 == 0);
Marcel Hlopko89547752021-12-10 09:39:41 +00001262 }
Googler5ea88642021-09-29 08:05:59 +00001263 );
Marcel Hlopkob4b28742021-09-15 12:45:20 +00001264 Ok(())
1265 }
Devin Jeanpierre7a7328e2021-09-17 07:10:08 +00001266
Devin Jeanpierre2ed14ec2021-10-06 11:32:19 +00001267 #[test]
Lukasz Anforowicz275fa922022-01-05 16:13:20 +00001268 fn test_ref_to_struct_in_thunk_impls() -> Result<()> {
Googler972d3582022-01-11 10:17:22 +00001269 let ir = ir_from_cc("struct S{}; inline void foo(class S& s) {} ")?;
Lukasz Anforowicz275fa922022-01-05 16:13:20 +00001270 let rs_api_impl = generate_rs_api_impl(&ir)?;
1271 assert_cc_matches!(
1272 rs_api_impl,
1273 quote! {
Googler972d3582022-01-11 10:17:22 +00001274 extern "C" void __rust_thunk___Z3fooR1S(class S& s) {
Lukasz Anforowicz275fa922022-01-05 16:13:20 +00001275 foo(s);
1276 }
1277 }
1278 );
1279 Ok(())
1280 }
1281
1282 #[test]
1283 fn test_const_ref_to_struct_in_thunk_impls() -> Result<()> {
Googler972d3582022-01-11 10:17:22 +00001284 let ir = ir_from_cc("struct S{}; inline void foo(const class S& s) {} ")?;
Lukasz Anforowicz275fa922022-01-05 16:13:20 +00001285 let rs_api_impl = generate_rs_api_impl(&ir)?;
1286 assert_cc_matches!(
1287 rs_api_impl,
1288 quote! {
Googler972d3582022-01-11 10:17:22 +00001289 extern "C" void __rust_thunk___Z3fooRK1S(const class S& s) {
Lukasz Anforowicz275fa922022-01-05 16:13:20 +00001290 foo(s);
1291 }
1292 }
1293 );
1294 Ok(())
1295 }
1296
1297 #[test]
Lukasz Anforowicz957cbf22022-01-05 16:14:05 +00001298 fn test_unsigned_int_in_thunk_impls() -> Result<()> {
1299 let ir = ir_from_cc("inline void foo(unsigned int i) {} ")?;
1300 let rs_api_impl = generate_rs_api_impl(&ir)?;
1301 assert_cc_matches!(
1302 rs_api_impl,
1303 quote! {
1304 extern "C" void __rust_thunk___Z3fooj(unsigned int i) {
1305 foo(i);
1306 }
1307 }
1308 );
1309 Ok(())
1310 }
1311
1312 #[test]
Marcel Hlopkodd1fcb12021-12-22 14:13:59 +00001313 fn test_record_static_methods_qualify_call_in_thunk() -> Result<()> {
1314 let ir = ir_from_cc(&tokens_to_string(quote! {
1315 struct SomeStruct {
1316 static inline int some_func() { return 42; }
1317 };
1318 })?)?;
1319
1320 assert_cc_matches!(
1321 generate_rs_api_impl(&ir)?,
1322 quote! {
1323 extern "C" int __rust_thunk___ZN10SomeStruct9some_funcEv() {
1324 return SomeStruct::some_func();
1325 }
1326 }
1327 );
1328 Ok(())
1329 }
1330
1331 #[test]
Marcel Hlopkoa0f38662021-12-03 08:45:26 +00001332 fn test_struct_from_other_target() -> Result<()> {
1333 let ir = ir_from_cc_dependency("// intentionally empty", "struct SomeStruct {};")?;
Marcel Hlopko89547752021-12-10 09:39:41 +00001334 assert_rs_not_matches!(generate_rs_api(&ir)?, quote! { SomeStruct });
1335 assert_cc_not_matches!(generate_rs_api_impl(&ir)?, quote! { SomeStruct });
Marcel Hlopkoa0f38662021-12-03 08:45:26 +00001336 Ok(())
1337 }
1338
1339 #[test]
Devin Jeanpierre2ed14ec2021-10-06 11:32:19 +00001340 fn test_copy_derives() {
Devin Jeanpierreccfefc82021-10-27 10:54:00 +00001341 let record = ir_record("S");
Devin Jeanpierre2ed14ec2021-10-06 11:32:19 +00001342 assert_eq!(generate_copy_derives(&record), &["Clone", "Copy"]);
1343 }
1344
1345 #[test]
1346 fn test_copy_derives_not_is_trivial_abi() {
Devin Jeanpierreccfefc82021-10-27 10:54:00 +00001347 let mut record = ir_record("S");
Devin Jeanpierre2ed14ec2021-10-06 11:32:19 +00001348 record.is_trivial_abi = false;
1349 assert_eq!(generate_copy_derives(&record), &[""; 0]);
1350 }
1351
Devin Jeanpierree6e16652021-12-22 15:54:46 +00001352 /// A type can be unsafe to pass in mut references from C++, but still
1353 /// Clone+Copy when handled by value.
1354 #[test]
1355 fn test_copy_derives_not_is_mut_reference_safe() {
1356 let mut record = ir_record("S");
1357 record.is_final = false;
1358 assert_eq!(generate_copy_derives(&record), &["Clone", "Copy"]);
1359 }
1360
Devin Jeanpierre2ed14ec2021-10-06 11:32:19 +00001361 #[test]
1362 fn test_copy_derives_ctor_nonpublic() {
Devin Jeanpierreccfefc82021-10-27 10:54:00 +00001363 let mut record = ir_record("S");
Devin Jeanpierre2ed14ec2021-10-06 11:32:19 +00001364 for access in [ir::AccessSpecifier::Protected, ir::AccessSpecifier::Private] {
1365 record.copy_constructor.access = access;
1366 assert_eq!(generate_copy_derives(&record), &[""; 0]);
1367 }
1368 }
1369
1370 #[test]
1371 fn test_copy_derives_ctor_deleted() {
Devin Jeanpierreccfefc82021-10-27 10:54:00 +00001372 let mut record = ir_record("S");
Devin Jeanpierre2ed14ec2021-10-06 11:32:19 +00001373 record.copy_constructor.definition = ir::SpecialMemberDefinition::Deleted;
1374 assert_eq!(generate_copy_derives(&record), &[""; 0]);
1375 }
1376
1377 #[test]
Devin Jeanpierrebe2f33b2021-10-21 12:54:19 +00001378 fn test_copy_derives_ctor_nontrivial_members() {
Devin Jeanpierreccfefc82021-10-27 10:54:00 +00001379 let mut record = ir_record("S");
Devin Jeanpierrebe2f33b2021-10-21 12:54:19 +00001380 record.copy_constructor.definition = ir::SpecialMemberDefinition::NontrivialMembers;
1381 assert_eq!(generate_copy_derives(&record), &[""; 0]);
1382 }
1383
1384 #[test]
1385 fn test_copy_derives_ctor_nontrivial_self() {
Devin Jeanpierreccfefc82021-10-27 10:54:00 +00001386 let mut record = ir_record("S");
Devin Jeanpierre7b62e952021-12-08 21:43:30 +00001387 record.copy_constructor.definition = ir::SpecialMemberDefinition::NontrivialUserDefined;
Devin Jeanpierre2ed14ec2021-10-06 11:32:19 +00001388 assert_eq!(generate_copy_derives(&record), &[""; 0]);
1389 }
1390
Devin Jeanpierre7a7328e2021-09-17 07:10:08 +00001391 #[test]
1392 fn test_ptr_func() -> Result<()> {
Marcel Hlopko89547752021-12-10 09:39:41 +00001393 let ir = ir_from_cc(&tokens_to_string(quote! {
1394 inline int* Deref(int*const* p);
1395 })?)?;
Devin Jeanpierred6da7002021-10-21 12:55:20 +00001396
Marcel Hlopko89547752021-12-10 09:39:41 +00001397 let rs_api = generate_rs_api(&ir)?;
1398 assert_rs_matches!(
1399 rs_api,
1400 quote! {
Michael Forsterdb8101a2021-10-08 06:56:03 +00001401 #[inline(always)]
1402 pub fn Deref(p: *const *mut i32) -> *mut i32 {
Googlera675ae02021-12-07 08:04:59 +00001403 unsafe { crate::detail::__rust_thunk___Z5DerefPKPi(p) }
Marcel Hlopko89547752021-12-10 09:39:41 +00001404 }
1405 }
1406 );
1407 assert_rs_matches!(
1408 rs_api,
1409 quote! {
Michael Forsterdb8101a2021-10-08 06:56:03 +00001410 mod detail {
Googler55647142022-01-11 12:37:39 +00001411 #[allow(unused_imports)]
Devin Jeanpierred4dde0e2021-10-13 20:48:25 +00001412 use super::*;
Michael Forsterdb8101a2021-10-08 06:56:03 +00001413 extern "C" {
Googlera675ae02021-12-07 08:04:59 +00001414 pub(crate) fn __rust_thunk___Z5DerefPKPi(p: *const *mut i32) -> *mut i32;
Marcel Hlopko89547752021-12-10 09:39:41 +00001415 }
1416 }
1417 }
Devin Jeanpierre7a7328e2021-09-17 07:10:08 +00001418 );
Devin Jeanpierre184f9ac2021-09-17 13:47:03 +00001419
Marcel Hlopko89547752021-12-10 09:39:41 +00001420 assert_cc_matches!(
1421 generate_rs_api_impl(&ir)?,
1422 quote! {
Googlera675ae02021-12-07 08:04:59 +00001423 extern "C" int* __rust_thunk___Z5DerefPKPi(int* const * p) {
Devin Jeanpierre184f9ac2021-09-17 13:47:03 +00001424 return Deref(p);
1425 }
Marcel Hlopko89547752021-12-10 09:39:41 +00001426 }
Devin Jeanpierre184f9ac2021-09-17 13:47:03 +00001427 );
Devin Jeanpierre7a7328e2021-09-17 07:10:08 +00001428 Ok(())
1429 }
Michael Forstered642022021-10-04 09:48:25 +00001430
1431 #[test]
Googlerdb111532022-01-05 06:12:13 +00001432 fn test_const_char_ptr_func() -> Result<()> {
1433 // This is a regression test: We used to include the "const" in the name
1434 // of the CcType, which caused a panic in the code generator
1435 // ('"const char" is not a valid Ident').
1436 // It's therefore important that f() is inline so that we need to
1437 // generate a thunk for it (where we then process the CcType).
1438 let ir = ir_from_cc(&tokens_to_string(quote! {
1439 inline void f(const char *str);
1440 })?)?;
1441
1442 let rs_api = generate_rs_api(&ir)?;
1443 assert_rs_matches!(
1444 rs_api,
1445 quote! {
1446 #[inline(always)]
1447 pub fn f(str: *const i8) {
1448 unsafe { crate::detail::__rust_thunk___Z1fPKc(str) }
1449 }
1450 }
1451 );
1452 assert_rs_matches!(
1453 rs_api,
1454 quote! {
1455 extern "C" {
1456 pub(crate) fn __rust_thunk___Z1fPKc(str: *const i8);
1457 }
1458 }
1459 );
1460
1461 assert_cc_matches!(
1462 generate_rs_api_impl(&ir)?,
1463 quote! {
1464 extern "C" void __rust_thunk___Z1fPKc(char const * str){ f(str) ; }
1465 }
1466 );
1467 Ok(())
1468 }
1469
1470 #[test]
Michael Forstered642022021-10-04 09:48:25 +00001471 fn test_item_order() -> Result<()> {
Marcel Hlopko89547752021-12-10 09:39:41 +00001472 let ir = ir_from_cc(
1473 "int first_func();
1474 struct FirstStruct {};
1475 int second_func();
1476 struct SecondStruct {};",
1477 )?;
Michael Forstered642022021-10-04 09:48:25 +00001478
Marcel Hlopko89547752021-12-10 09:39:41 +00001479 let rs_api = rs_tokens_to_formatted_string(generate_rs_api(&ir)?)?;
1480
Michael Forstered642022021-10-04 09:48:25 +00001481 let idx = |s: &str| rs_api.find(s).ok_or(anyhow!("'{}' missing", s));
1482
1483 let f1 = idx("fn first_func")?;
1484 let f2 = idx("fn second_func")?;
1485 let s1 = idx("struct FirstStruct")?;
1486 let s2 = idx("struct SecondStruct")?;
Googlera675ae02021-12-07 08:04:59 +00001487 let t1 = idx("fn __rust_thunk___Z10first_funcv")?;
1488 let t2 = idx("fn __rust_thunk___Z11second_funcv")?;
Michael Forstered642022021-10-04 09:48:25 +00001489
1490 assert!(f1 < s1);
1491 assert!(s1 < f2);
1492 assert!(f2 < s2);
1493 assert!(s2 < t1);
1494 assert!(t1 < t2);
1495
1496 Ok(())
1497 }
Michael Forster028800b2021-10-05 12:39:59 +00001498
1499 #[test]
Michael Forster409d9412021-10-07 08:35:29 +00001500 fn test_doc_comment_func() -> Result<()> {
Marcel Hlopko89547752021-12-10 09:39:41 +00001501 let ir = ir_from_cc(
1502 "
1503 // Doc Comment
1504 // with two lines
1505 int func();",
1506 )?;
Michael Forster409d9412021-10-07 08:35:29 +00001507
Marcel Hlopko89547752021-12-10 09:39:41 +00001508 assert_rs_matches!(
1509 generate_rs_api(&ir)?,
1510 // leading space is intentional so there is a space between /// and the text of the
1511 // comment
1512 quote! {
1513 #[doc = " Doc Comment\n with two lines"]
1514 #[inline(always)]
1515 pub fn func
1516 }
Michael Forster409d9412021-10-07 08:35:29 +00001517 );
1518
1519 Ok(())
1520 }
1521
1522 #[test]
1523 fn test_doc_comment_record() -> Result<()> {
Marcel Hlopko89547752021-12-10 09:39:41 +00001524 let ir = ir_from_cc(
1525 "// Doc Comment\n\
1526 //\n\
1527 // * with bullet\n\
1528 struct SomeStruct {\n\
1529 // Field doc\n\
1530 int field;\
1531 };",
1532 )?;
Michael Forster028800b2021-10-05 12:39:59 +00001533
Marcel Hlopko89547752021-12-10 09:39:41 +00001534 assert_rs_matches!(
1535 generate_rs_api(&ir)?,
1536 quote! {
1537 #[doc = " Doc Comment\n \n * with bullet"]
1538 #[derive(Clone, Copy)]
1539 #[repr(C)]
1540 pub struct SomeStruct {
1541 # [doc = " Field doc"]
1542 pub field: i32,
1543 }
1544 }
Michael Forstercc5941a2021-10-07 07:12:24 +00001545 );
Michael Forster028800b2021-10-05 12:39:59 +00001546 Ok(())
1547 }
Devin Jeanpierre91de7012021-10-21 12:53:51 +00001548
Devin Jeanpierre96839c12021-12-14 00:27:38 +00001549 #[test]
1550 fn test_virtual_thunk() -> Result<()> {
1551 let ir = ir_from_cc("struct Polymorphic { virtual void Foo(); };")?;
1552
1553 assert_cc_matches!(
1554 generate_rs_api_impl(&ir)?,
1555 quote! {
Googler972d3582022-01-11 10:17:22 +00001556 extern "C" void __rust_thunk___ZN11Polymorphic3FooEv(class Polymorphic * __this)
Devin Jeanpierre96839c12021-12-14 00:27:38 +00001557 }
1558 );
1559 Ok(())
1560 }
1561
Devin Jeanpierree6e16652021-12-22 15:54:46 +00001562 /// A trivially relocatable final struct is safe to use in Rust as normal,
1563 /// and is Unpin.
1564 #[test]
1565 fn test_no_negative_impl_unpin() -> Result<()> {
1566 let ir = ir_from_cc("struct Trivial final {};")?;
1567 let rs_api = generate_rs_api(&ir)?;
1568 assert_rs_not_matches!(rs_api, quote! {impl !Unpin});
1569 Ok(())
1570 }
1571
1572 /// A non-final struct, even if it's trivial, is not usable by mut
1573 /// reference, and so is !Unpin.
1574 #[test]
1575 fn test_negative_impl_unpin_nonfinal() -> Result<()> {
1576 let ir = ir_from_cc("struct Nonfinal {};")?;
1577 let rs_api = generate_rs_api(&ir)?;
1578 assert_rs_matches!(rs_api, quote! {impl !Unpin for Nonfinal {}});
1579 Ok(())
1580 }
1581
Devin Jeanpierre91de7012021-10-21 12:53:51 +00001582 /// At the least, a trivial type should have no drop impl if or until we add
1583 /// empty drop impls.
1584 #[test]
1585 fn test_no_impl_drop() -> Result<()> {
Googler7cced422021-12-06 11:58:39 +00001586 let ir = ir_from_cc("struct Trivial {};")?;
Marcel Hlopko89547752021-12-10 09:39:41 +00001587 let rs_api = rs_tokens_to_formatted_string(generate_rs_api(&ir)?)?;
Devin Jeanpierre91de7012021-10-21 12:53:51 +00001588 assert!(!rs_api.contains("impl Drop"));
1589 Ok(())
1590 }
1591
Devin Jeanpierre7e9a1de2021-12-03 08:04:22 +00001592 /// User-defined destructors *must* become Drop impls with ManuallyDrop
1593 /// fields
Devin Jeanpierre91de7012021-10-21 12:53:51 +00001594 #[test]
Devin Jeanpierre7e9a1de2021-12-03 08:04:22 +00001595 fn test_impl_drop_user_defined_destructor() -> Result<()> {
Googler7cced422021-12-06 11:58:39 +00001596 let ir = ir_from_cc(
Devin Jeanpierre91de7012021-10-21 12:53:51 +00001597 r#"struct UserDefinedDestructor {
1598 ~UserDefinedDestructor();
Devin Jeanpierre7e9a1de2021-12-03 08:04:22 +00001599 int x;
Devin Jeanpierre91de7012021-10-21 12:53:51 +00001600 };"#,
1601 )?;
1602 let rs_api = generate_rs_api(&ir)?;
Lukasz Anforowicz6d553632022-01-06 21:36:14 +00001603 assert_rs_matches!(
1604 rs_api,
1605 quote! {
1606 impl Drop for UserDefinedDestructor {
1607 #[inline(always)]
1608 fn drop(&mut self) {
1609 unsafe { crate::detail::__rust_thunk___ZN21UserDefinedDestructorD1Ev(self) }
1610 }
1611 }
1612 }
1613 );
Marcel Hlopko89547752021-12-10 09:39:41 +00001614 assert_rs_matches!(rs_api, quote! {pub x: std::mem::ManuallyDrop<i32>,});
Devin Jeanpierre7e9a1de2021-12-03 08:04:22 +00001615 Ok(())
1616 }
1617
Lukasz Anforowicz6d553632022-01-06 21:36:14 +00001618 /// nontrivial types without user-defined destructors should invoke
1619 /// the C++ destructor to preserve the order of field destructions.
Devin Jeanpierre7e9a1de2021-12-03 08:04:22 +00001620 #[test]
1621 fn test_impl_drop_nontrivial_member_destructor() -> Result<()> {
1622 // TODO(jeanpierreda): This would be cleaner if the UserDefinedDestructor code were
1623 // omitted. For example, we simulate it so that UserDefinedDestructor
1624 // comes from another library.
Googler7cced422021-12-06 11:58:39 +00001625 let ir = ir_from_cc(
Devin Jeanpierre7e9a1de2021-12-03 08:04:22 +00001626 r#"struct UserDefinedDestructor {
1627 ~UserDefinedDestructor();
1628 };
1629
1630 struct NontrivialMembers {
1631 UserDefinedDestructor udd;
1632 int x;
1633 };"#,
1634 )?;
1635 let rs_api = generate_rs_api(&ir)?;
Lukasz Anforowicz6d553632022-01-06 21:36:14 +00001636 assert_rs_matches!(
1637 rs_api,
1638 quote! {
1639 impl Drop for NontrivialMembers {
1640 #[inline(always)]
1641 fn drop(&mut self) {
1642 unsafe { crate::detail::__rust_thunk___ZN17NontrivialMembersD1Ev(self) }
1643 }
1644 }
1645 }
1646 );
1647 assert_rs_matches!(rs_api, quote! {pub x: std::mem::ManuallyDrop<i32>,});
1648 assert_rs_matches!(
1649 rs_api,
1650 quote! {pub udd: std::mem::ManuallyDrop<UserDefinedDestructor>,}
1651 );
Devin Jeanpierre7e9a1de2021-12-03 08:04:22 +00001652 Ok(())
1653 }
1654
1655 /// Trivial types (at least those that are mapped to Copy rust types) do not
1656 /// get a Drop impl.
1657 #[test]
1658 fn test_impl_drop_trivial() -> Result<()> {
Googler7cced422021-12-06 11:58:39 +00001659 let ir = ir_from_cc(
Devin Jeanpierre7e9a1de2021-12-03 08:04:22 +00001660 r#"struct Trivial {
1661 ~Trivial() = default;
1662 int x;
1663 };"#,
1664 )?;
1665 let rs_api = generate_rs_api(&ir)?;
Marcel Hlopko89547752021-12-10 09:39:41 +00001666 assert_rs_not_matches!(rs_api, quote! {impl Drop});
1667 assert_rs_matches!(rs_api, quote! {pub x: i32});
Lukasz Anforowicz2f074162022-01-06 22:50:51 +00001668 let rs_api_impl = generate_rs_api_impl(&ir)?;
1669 // TODO(b/213326125): Avoid generating thunk impls that are never called.
1670 // (The test assertion below should be reversed once this bug is fixed.)
1671 assert_cc_matches!(rs_api_impl, quote! { std::destroy_at });
Devin Jeanpierre91de7012021-10-21 12:53:51 +00001672 Ok(())
1673 }
Devin Jeanpierre45cb1162021-10-27 10:54:28 +00001674
1675 #[test]
Lukasz Anforowicze643ec92021-12-22 15:45:15 +00001676 fn test_impl_default_explicitly_defaulted_constructor() -> Result<()> {
1677 let ir = ir_from_cc(
1678 r#"struct DefaultedConstructor {
1679 DefaultedConstructor() = default;
1680 };"#,
1681 )?;
1682 let rs_api = generate_rs_api(&ir)?;
1683 assert_rs_matches!(
1684 rs_api,
1685 quote! {
1686 impl Default for DefaultedConstructor {
1687 #[inline(always)]
1688 fn default() -> Self {
Lukasz Anforowiczbedbdee2022-01-05 01:14:52 +00001689 let mut tmp = std::mem::MaybeUninit::<Self>::zeroed();
Lukasz Anforowicze643ec92021-12-22 15:45:15 +00001690 unsafe {
Lukasz Anforowicz40c2eb82022-01-11 18:22:31 +00001691 crate::detail::__rust_thunk___ZN20DefaultedConstructorC1Ev(&mut tmp);
Lukasz Anforowicze643ec92021-12-22 15:45:15 +00001692 tmp.assume_init()
1693 }
1694 }
1695 }
1696 }
1697 );
1698 let rs_api_impl = generate_rs_api_impl(&ir)?;
1699 assert_cc_matches!(
1700 rs_api_impl,
1701 quote! {
1702 extern "C" void __rust_thunk___ZN20DefaultedConstructorC1Ev(
Googler972d3582022-01-11 10:17:22 +00001703 class DefaultedConstructor* __this) {
Lukasz Anforowicz4457baf2021-12-23 17:24:04 +00001704 rs_api_impl_support::construct_at (__this) ;
Lukasz Anforowicze643ec92021-12-22 15:45:15 +00001705 }
1706 }
1707 );
1708 Ok(())
1709 }
1710
1711 #[test]
Lukasz Anforowicz9bab8352021-12-22 17:35:31 +00001712 fn test_impl_default_non_trivial_struct() -> Result<()> {
1713 let ir = ir_from_cc(
1714 r#"struct NonTrivialStructWithConstructors {
1715 NonTrivialStructWithConstructors();
1716 ~NonTrivialStructWithConstructors(); // Non-trivial
1717 };"#,
1718 )?;
1719 let rs_api = generate_rs_api(&ir)?;
1720 assert_rs_not_matches!(rs_api, quote! {impl Default});
1721 Ok(())
1722 }
1723
1724 #[test]
Devin Jeanpierre45cb1162021-10-27 10:54:28 +00001725 fn test_thunk_ident_function() {
1726 let func = ir_func("foo");
Googlera675ae02021-12-07 08:04:59 +00001727 assert_eq!(thunk_ident(&func), make_ident("__rust_thunk___Z3foov"));
Devin Jeanpierre45cb1162021-10-27 10:54:28 +00001728 }
1729
1730 #[test]
1731 fn test_thunk_ident_special_names() {
Marcel Hlopko4b13b962021-12-06 12:40:56 +00001732 let ir = ir_from_cc("struct Class {};").unwrap();
Devin Jeanpierre45cb1162021-10-27 10:54:28 +00001733
Googler45ad2752021-12-06 12:12:35 +00001734 let destructor =
1735 ir.functions().find(|f| f.name == UnqualifiedIdentifier::Destructor).unwrap();
Googlera675ae02021-12-07 08:04:59 +00001736 assert_eq!(thunk_ident(&destructor), make_ident("__rust_thunk___ZN5ClassD1Ev"));
Devin Jeanpierre45cb1162021-10-27 10:54:28 +00001737
Googler45ad2752021-12-06 12:12:35 +00001738 let constructor =
1739 ir.functions().find(|f| f.name == UnqualifiedIdentifier::Constructor).unwrap();
Googlera675ae02021-12-07 08:04:59 +00001740 assert_eq!(thunk_ident(&constructor), make_ident("__rust_thunk___ZN5ClassC1Ev"));
Devin Jeanpierre45cb1162021-10-27 10:54:28 +00001741 }
Googler7cced422021-12-06 11:58:39 +00001742
1743 #[test]
Marcel Hlopko89547752021-12-10 09:39:41 +00001744 fn test_elided_lifetimes() -> Result<()> {
Googler7cced422021-12-06 11:58:39 +00001745 let ir = ir_from_cc(
1746 r#"#pragma clang lifetime_elision
1747 struct S {
1748 int& f(int& i);
1749 };"#,
Marcel Hlopko89547752021-12-10 09:39:41 +00001750 )?;
1751 let rs_api = generate_rs_api(&ir)?;
1752 assert_rs_matches!(
1753 rs_api,
1754 quote! {
Googler6804a012022-01-05 07:04:36 +00001755 pub fn f<'a, 'b>(__this: &'a mut S, i: &'b mut i32) -> &'a mut i32 { ... }
Marcel Hlopko89547752021-12-10 09:39:41 +00001756 }
Googler7cced422021-12-06 11:58:39 +00001757 );
Marcel Hlopko89547752021-12-10 09:39:41 +00001758 assert_rs_matches!(
1759 rs_api,
1760 quote! {
Googler6804a012022-01-05 07:04:36 +00001761 pub(crate) fn __rust_thunk___ZN1S1fERi<'a, 'b>(__this: &'a mut S, i: &'b mut i32)
1762 -> &'a mut i32;
Marcel Hlopko89547752021-12-10 09:39:41 +00001763 }
Googler7cced422021-12-06 11:58:39 +00001764 );
Marcel Hlopko89547752021-12-10 09:39:41 +00001765 Ok(())
Googler7cced422021-12-06 11:58:39 +00001766 }
Lukasz Anforowiczdaff0402021-12-23 00:37:50 +00001767
1768 #[test]
1769 fn test_format_generic_params() -> Result<()> {
1770 assert_rs_matches!(format_generic_params(std::iter::empty::<syn::Ident>()), quote! {});
1771
1772 let idents = ["T1", "T2"].iter().map(|s| make_ident(s));
1773 assert_rs_matches!(format_generic_params(idents), quote! { < T1, T2 > });
1774
1775 let lifetimes = ["a", "b"]
1776 .iter()
1777 .map(|s| syn::Lifetime::new(&format!("'{}", s), proc_macro2::Span::call_site()));
1778 assert_rs_matches!(format_generic_params(lifetimes), quote! { < 'a, 'b > });
1779
1780 Ok(())
1781 }
Googlerd03d05b2022-01-07 10:10:57 +00001782
1783 #[test]
1784 fn test_overloaded_functions() -> Result<()> {
1785 // TODO(b/213280424): We don't support creating bindings for overloaded
1786 // functions yet, except in the case of overloaded constructors with a
1787 // single parameter.
1788 let ir = ir_from_cc(
1789 r#"
1790 void f();
1791 void f(int i);
1792 struct S1 {
1793 void f();
1794 void f(int i);
1795 };
1796 struct S2 {
1797 void f();
1798 };
1799 struct S3 {
1800 S3(int i);
1801 S3(double d);
1802 };
1803 "#,
1804 )?;
1805 let rs_api = generate_rs_api(&ir)?;
1806 let rs_api_str = tokens_to_string(rs_api.clone())?;
1807
1808 // Cannot overload free functions.
1809 assert!(rs_api_str.contains("Error while generating bindings for item 'f'"));
1810 assert_rs_not_matches!(rs_api, quote! {pub fn f()});
1811 assert_rs_not_matches!(rs_api, quote! {pub fn f(i: i32)});
1812
1813 // Cannot overload member functions.
1814 assert!(rs_api_str.contains("Error while generating bindings for item 'S1::f'"));
1815 assert_rs_not_matches!(rs_api, quote! {pub fn f(... S1 ...)});
1816
1817 // But we can import member functions that have the same name as a free
1818 // function.
1819 assert_rs_matches!(rs_api, quote! {pub fn f(__this: *mut S2)});
1820
1821 // We can also import overloaded single-parameter constructors.
1822 assert_rs_matches!(rs_api, quote! {impl From<i32> for S3});
1823 assert_rs_matches!(rs_api, quote! {impl From<f64> for S3});
1824 Ok(())
1825 }
Googlerdcca7f72022-01-10 12:30:43 +00001826
1827 #[test]
1828 fn test_type_alias() -> Result<()> {
1829 let ir = ir_from_cc(
1830 r#"
1831 typedef int MyTypedefDecl;
1832 using MyTypeAliasDecl = int;
Googler6a0a5252022-01-11 14:08:09 +00001833 using MyTypeAliasDecl_Alias = MyTypeAliasDecl;
1834
1835 struct S{};
1836 using S_Alias = S;
1837 using S_Alias_Alias = S_Alias;
1838
1839 inline void f(MyTypedefDecl t) {}
Googlerdcca7f72022-01-10 12:30:43 +00001840 "#,
1841 )?;
1842 let rs_api = generate_rs_api(&ir)?;
Googler6a0a5252022-01-11 14:08:09 +00001843 assert_rs_matches!(rs_api, quote! { pub type MyTypedefDecl = i32; });
1844 assert_rs_matches!(rs_api, quote! { pub type MyTypeAliasDecl = i32; });
1845 assert_rs_matches!(rs_api, quote! { pub type MyTypeAliasDecl_Alias = MyTypeAliasDecl; });
1846 assert_rs_matches!(rs_api, quote! { pub type S_Alias = S; });
1847 assert_rs_matches!(rs_api, quote! { pub type S_Alias_Alias = S_Alias; });
1848 assert_rs_matches!(rs_api, quote! { pub fn f(t: MyTypedefDecl) });
1849 assert_cc_matches!(
1850 generate_rs_api_impl(&ir)?,
1851 quote! {
1852 extern "C" void __rust_thunk___Z1fi(MyTypedefDecl t){ f (t) ; }
1853 }
1854 );
Googlerdcca7f72022-01-10 12:30:43 +00001855 Ok(())
1856 }
Marcel Hlopko42abfc82021-08-09 07:03:17 +00001857}