blob: d781978b966a3f01d9712869c7714070a05d6c01 [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 Anforowicze57215c2022-01-12 14:54:16 +00005#[cfg(test)]
6#[macro_use]
7extern crate static_assertions;
8
Lukasz Anforowicz40c2eb82022-01-11 18:22:31 +00009use anyhow::{anyhow, bail, ensure, Context, Result};
Marcel Hlopko884ae7f2021-08-18 13:58:22 +000010use ffi_types::*;
Marcel Hlopko42abfc82021-08-09 07:03:17 +000011use ir::*;
12use itertools::Itertools;
Googler5ea88642021-09-29 08:05:59 +000013use proc_macro2::{Ident, Literal, TokenStream};
Marcel Hlopko42abfc82021-08-09 07:03:17 +000014use quote::format_ident;
15use quote::quote;
Googlerd03d05b2022-01-07 10:10:57 +000016use std::collections::{BTreeSet, HashMap, HashSet};
Marcel Hlopko42abfc82021-08-09 07:03:17 +000017use std::iter::Iterator;
18use std::panic::catch_unwind;
19use std::process;
Marcel Hlopko65d05f02021-12-09 12:29:24 +000020use token_stream_printer::{rs_tokens_to_formatted_string, tokens_to_string};
Marcel Hlopko42abfc82021-08-09 07:03:17 +000021
Marcel Hlopko45fba972021-08-23 19:52:20 +000022/// FFI equivalent of `Bindings`.
23#[repr(C)]
24pub struct FfiBindings {
25 rs_api: FfiU8SliceBox,
26 rs_api_impl: FfiU8SliceBox,
27}
28
29/// Deserializes IR from `json` and generates bindings source code.
Marcel Hlopko42abfc82021-08-09 07:03:17 +000030///
31/// This function panics on error.
32///
Lukasz Anforowiczdd9ae0f2022-02-17 15:52:53 +000033/// # Safety
34///
35/// Expectations:
36/// * function expects that param `json` is a FfiU8Slice for a valid array of
37/// bytes with the given size.
38/// * function expects that param `json` doesn't change during the call.
39///
Marcel Hlopko42abfc82021-08-09 07:03:17 +000040/// Ownership:
Michael Forsterbee84482021-10-13 08:35:38 +000041/// * function doesn't take ownership of (in other words it borrows) the
42/// param `json`
Marcel Hlopko42abfc82021-08-09 07:03:17 +000043/// * function passes ownership of the returned value to the caller
Marcel Hlopko42abfc82021-08-09 07:03:17 +000044#[no_mangle]
Marcel Hlopko45fba972021-08-23 19:52:20 +000045pub unsafe extern "C" fn GenerateBindingsImpl(json: FfiU8Slice) -> FfiBindings {
Marcel Hlopko42abfc82021-08-09 07:03:17 +000046 catch_unwind(|| {
Marcel Hlopko45fba972021-08-23 19:52:20 +000047 // It is ok to abort here.
48 let Bindings { rs_api, rs_api_impl } = generate_bindings(json.as_slice()).unwrap();
49
50 FfiBindings {
51 rs_api: FfiU8SliceBox::from_boxed_slice(rs_api.into_bytes().into_boxed_slice()),
52 rs_api_impl: FfiU8SliceBox::from_boxed_slice(
53 rs_api_impl.into_bytes().into_boxed_slice(),
54 ),
55 }
Marcel Hlopko42abfc82021-08-09 07:03:17 +000056 })
57 .unwrap_or_else(|_| process::abort())
58}
59
Marcel Hlopko45fba972021-08-23 19:52:20 +000060/// Source code for generated bindings.
61struct Bindings {
62 // Rust source code.
63 rs_api: String,
64 // C++ source code.
65 rs_api_impl: String,
Marcel Hlopko42abfc82021-08-09 07:03:17 +000066}
67
Marcel Hlopko45fba972021-08-23 19:52:20 +000068fn generate_bindings(json: &[u8]) -> Result<Bindings> {
69 let ir = deserialize_ir(json)?;
Marcel Hlopkoca84ff42021-12-09 14:15:14 +000070
71 // The code is formatted with a non-default rustfmt configuration. Prevent
Lukasz Anforowicz5b3f5302022-02-07 01:04:47 +000072 // downstream workflows from reformatting with a different configuration by
73 // marking the output with `@generated`. See also
74 // https://rust-lang.github.io/rustfmt/?version=v1.4.38&search=#format_generated_files
75 //
76 // TODO(lukasza): It would be nice to include "by $argv[0]"" in the
77 // @generated comment below. OTOH, `std::env::current_exe()` in our
78 // current build environment returns a guid-like path... :-/
79 let rs_api = format!(
80 "// Automatically @generated Rust bindings for C++ target\n// {target}\n{code}",
81 target = ir.current_target().0,
82 code = rs_tokens_to_formatted_string(generate_rs_api(&ir)?)?
83 );
Marcel Hlopko89547752021-12-10 09:39:41 +000084 let rs_api_impl = tokens_to_string(generate_rs_api_impl(&ir)?)?;
Marcel Hlopkoca84ff42021-12-09 14:15:14 +000085
Marcel Hlopko45fba972021-08-23 19:52:20 +000086 Ok(Bindings { rs_api, rs_api_impl })
87}
88
Devin Jeanpierre6d5e7cc2021-10-21 12:56:07 +000089/// Rust source code with attached information about how to modify the parent
90/// crate.
Devin Jeanpierre273eeae2021-10-06 13:29:35 +000091///
Michael Forsterbee84482021-10-13 08:35:38 +000092/// For example, the snippet `vec![].into_raw_parts()` is not valid unless the
93/// `vec_into_raw_parts` feature is enabled. So such a snippet should be
94/// represented as:
Devin Jeanpierre273eeae2021-10-06 13:29:35 +000095///
96/// ```
97/// RsSnippet {
Marcel Hlopkoeaae9b72022-01-21 15:54:11 +000098/// features: btree_set![make_rs_ident("vec_into_raw_parts")],
Devin Jeanpierre273eeae2021-10-06 13:29:35 +000099/// tokens: quote!{vec![].into_raw_parts()},
100/// }
101/// ```
Lukasz Anforowiczeb19ac62022-02-05 00:10:16 +0000102#[derive(Clone, Debug)]
Devin Jeanpierre273eeae2021-10-06 13:29:35 +0000103struct RsSnippet {
104 /// Rust feature flags used by this snippet.
105 features: BTreeSet<Ident>,
106 /// The snippet itself, as a token stream.
107 tokens: TokenStream,
108}
109
110impl From<TokenStream> for RsSnippet {
111 fn from(tokens: TokenStream) -> Self {
112 RsSnippet { features: BTreeSet::new(), tokens }
113 }
114}
115
Michael Forsterbee84482021-10-13 08:35:38 +0000116/// If we know the original C++ function is codegenned and already compatible
117/// with `extern "C"` calling convention we skip creating/calling the C++ thunk
118/// since we can call the original C++ directly.
Marcel Hlopko3164eee2021-08-24 20:09:22 +0000119fn can_skip_cc_thunk(func: &Func) -> bool {
Devin Jeanpierre96839c12021-12-14 00:27:38 +0000120 // ## Inline functions
121 //
Michael Forsterbee84482021-10-13 08:35:38 +0000122 // Inline functions may not be codegenned in the C++ library since Clang doesn't
123 // know if Rust calls the function or not. Therefore in order to make inline
124 // functions callable from Rust we need to generate a C++ file that defines
125 // a thunk that delegates to the original inline function. When compiled,
126 // Clang will emit code for this thunk and Rust code will call the
Marcel Hlopko3164eee2021-08-24 20:09:22 +0000127 // thunk when the user wants to call the original inline function.
128 //
Michael Forsterbee84482021-10-13 08:35:38 +0000129 // This is not great runtime-performance-wise in regular builds (inline function
130 // will not be inlined, there will always be a function call), but it is
131 // correct. ThinLTO builds will be able to see through the thunk and inline
132 // code across the language boundary. For non-ThinLTO builds we plan to
133 // implement <internal link> which removes the runtime performance overhead.
Devin Jeanpierre96839c12021-12-14 00:27:38 +0000134 if func.is_inline {
135 return false;
136 }
137 // ## Virtual functions
138 //
139 // When calling virtual `A::Method()`, it's not necessarily the case that we'll
140 // specifically call the concrete `A::Method` impl. For example, if this is
141 // called on something whose dynamic type is some subclass `B` with an
142 // overridden `B::Method`, then we'll call that.
143 //
144 // We must reuse the C++ dynamic dispatching system. In this case, the easiest
145 // way to do it is by resorting to a C++ thunk, whose implementation will do
146 // the lookup.
147 //
148 // In terms of runtime performance, since this only occurs for virtual function
149 // calls, which are already slow, it may not be such a big deal. We can
150 // benchmark it later. :)
151 if let Some(meta) = &func.member_func_metadata {
152 if let Some(inst_meta) = &meta.instance_method_metadata {
153 if inst_meta.is_virtual {
154 return false;
155 }
156 }
157 }
158
159 true
Marcel Hlopko3164eee2021-08-24 20:09:22 +0000160}
161
Googlerd03d05b2022-01-07 10:10:57 +0000162/// Uniquely identifies a generated Rust function.
Lukasz Anforowiczeb19ac62022-02-05 00:10:16 +0000163#[derive(Clone, Debug, PartialEq, Eq, Hash)]
Googlerd03d05b2022-01-07 10:10:57 +0000164struct FunctionId {
165 // If the function is on a trait impl, contains the name of the Self type for
166 // which the trait is being implemented.
167 self_type: Option<syn::Path>,
168 // Fully qualified path of the function. For functions in impl blocks, this
169 // includes the name of the type or trait on which the function is being
170 // implemented, e.g. `Default::default`.
171 function_path: syn::Path,
172}
173
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +0000174/// Returns the name of `func` in C++ syntax.
Googlerd03d05b2022-01-07 10:10:57 +0000175fn cxx_function_name(func: &Func, ir: &IR) -> Result<String> {
176 let record: Option<&str> = func
177 .member_func_metadata
178 .as_ref()
179 .map(|meta| meta.find_record(ir))
180 .transpose()?
181 .map(|r| &*r.identifier.identifier);
182
183 let func_name = match &func.name {
184 UnqualifiedIdentifier::Identifier(id) => id.identifier.clone(),
Lukasz Anforowicz9c663ca2022-02-09 01:33:31 +0000185 UnqualifiedIdentifier::Operator(op) => op.cc_name(),
Googlerd03d05b2022-01-07 10:10:57 +0000186 UnqualifiedIdentifier::Destructor => {
187 format!("~{}", record.expect("destructor must be associated with a record"))
188 }
189 UnqualifiedIdentifier::Constructor => {
Lukasz Anforowiczeb19ac62022-02-05 00:10:16 +0000190 record.expect("constructor must be associated with a record").to_string()
Googlerd03d05b2022-01-07 10:10:57 +0000191 }
192 };
193
194 if let Some(record_name) = record {
195 Ok(format!("{}::{}", record_name, func_name))
196 } else {
197 Ok(func_name)
198 }
199}
200
Lukasz Anforowiczeb19ac62022-02-05 00:10:16 +0000201fn make_unsupported_fn(func: &Func, ir: &IR, message: impl ToString) -> Result<UnsupportedItem> {
202 Ok(UnsupportedItem {
203 name: cxx_function_name(func, ir)?,
204 message: message.to_string(),
205 source_loc: func.source_loc.clone(),
206 })
207}
208
209#[derive(Clone, Debug)]
210enum GeneratedFunc {
211 None, // No explicit function needed (e.g. when deriving Drop).
212 Unsupported(UnsupportedItem),
213 Some { api_func: RsSnippet, thunk: RsSnippet, function_id: FunctionId },
214}
215
Michael Forstered642022021-10-04 09:48:25 +0000216/// Generates Rust source code for a given `Func`.
Lukasz Anforowiczeb19ac62022-02-05 00:10:16 +0000217fn generate_func(func: &Func, ir: &IR) -> Result<GeneratedFunc> {
218 let make_unsupported_result = |msg: &str| -> Result<GeneratedFunc> {
219 Ok(GeneratedFunc::Unsupported(make_unsupported_fn(func, ir, msg)?))
220 };
221
Michael Forstered642022021-10-04 09:48:25 +0000222 let mangled_name = &func.mangled_name;
Googlera675ae02021-12-07 08:04:59 +0000223 let thunk_ident = thunk_ident(func);
Michael Forster409d9412021-10-07 08:35:29 +0000224 let doc_comment = generate_doc_comment(&func.doc_comment);
Googler7cced422021-12-06 11:58:39 +0000225 let lifetime_to_name = HashMap::<LifetimeId, String>::from_iter(
226 func.lifetime_params.iter().map(|l| (l.id, l.name.clone())),
227 );
Lukasz Anforowicz4ad012b2021-12-15 18:13:40 +0000228 let return_type_fragment = if func.return_type.rs_type.is_unit_type() {
229 quote! {}
230 } else {
Googlerb7e361d2022-01-04 14:02:59 +0000231 let return_type_name = format_rs_type(&func.return_type.rs_type, ir, &lifetime_to_name)
232 .with_context(|| format!("Failed to format return type for {:?}", func))?;
Lukasz Anforowicz4ad012b2021-12-15 18:13:40 +0000233 quote! { -> #return_type_name }
234 };
Michael Forstered642022021-10-04 09:48:25 +0000235
236 let param_idents =
Marcel Hlopkoeaae9b72022-01-21 15:54:11 +0000237 func.params.iter().map(|p| make_rs_ident(&p.identifier.identifier)).collect_vec();
Michael Forstered642022021-10-04 09:48:25 +0000238
Lukasz Anforowiczf7bdd392022-01-21 00:33:39 +0000239 let param_type_kinds = func
Marcel Hlopkoc0956cf2021-11-29 08:31:28 +0000240 .params
241 .iter()
Googlerb7e361d2022-01-04 14:02:59 +0000242 .map(|p| {
Lukasz Anforowiczf7bdd392022-01-21 00:33:39 +0000243 RsTypeKind::new(&p.type_.rs_type, ir).with_context(|| {
244 format!("Failed to process type of parameter {:?} on {:?}", p, func)
Googlerb7e361d2022-01-04 14:02:59 +0000245 })
246 })
Marcel Hlopkoc0956cf2021-11-29 08:31:28 +0000247 .collect::<Result<Vec<_>>>()?;
Lukasz Anforowiczf7bdd392022-01-21 00:33:39 +0000248 let param_types = param_type_kinds
249 .iter()
250 .map(|t| {
251 t.format(ir, &lifetime_to_name)
252 .with_context(|| format!("Failed to format parameter type {:?} on {:?}", t, func))
253 })
254 .collect::<Result<Vec<_>>>()?;
255 let is_unsafe = param_type_kinds.iter().any(|p| matches!(p, RsTypeKind::Pointer { .. }));
Michael Forstered642022021-10-04 09:48:25 +0000256
Lukasz Anforowiczab65e292022-01-14 23:04:21 +0000257 let maybe_record: Option<&Record> =
Lukasz Anforowicz13cf7492021-12-22 15:29:52 +0000258 func.member_func_metadata.as_ref().map(|meta| meta.find_record(ir)).transpose()?;
Lukasz Anforowicz732ca642022-02-03 20:58:38 +0000259 let maybe_record_name = maybe_record.map(|r| make_rs_ident(&r.identifier.identifier));
Lukasz Anforowicz13cf7492021-12-22 15:29:52 +0000260
Lukasz Anforowicz732ca642022-02-03 20:58:38 +0000261 // Find 1) the `func_name` and `impl_kind` of the API function to generate
262 // and 2) whether to `format_first_param_as_self` (`&self` or `&mut self`).
Lukasz Anforowiczab65e292022-01-14 23:04:21 +0000263 enum ImplKind {
Lukasz Anforowicz732ca642022-02-03 20:58:38 +0000264 None, // No `impl` needed
265 Struct, // e.g. `impl SomeStruct { ... }` (SomeStruct based on func.member_func_metadata)
266 Trait {
267 trait_name: TokenStream, // e.g. quote!{ From<int> }
268 record_name: Ident, /* e.g. SomeStruct (might *not* be from
269 * func.member_func_metadata) */
270 },
Lukasz Anforowiczab65e292022-01-14 23:04:21 +0000271 }
272 let impl_kind: ImplKind;
273 let func_name: syn::Ident;
274 let format_first_param_as_self: bool;
Googlerd03d05b2022-01-07 10:10:57 +0000275 match &func.name {
Lukasz Anforowicz9c663ca2022-02-09 01:33:31 +0000276 UnqualifiedIdentifier::Operator(op) if op.name == "==" => {
Lukasz Anforowiczeb19ac62022-02-05 00:10:16 +0000277 if param_type_kinds.len() != 2 {
278 bail!("Unexpected number of parameters in operator==: {:?}", func);
279 }
280 match (&param_type_kinds[0], &param_type_kinds[1]) {
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +0000281 (
Lukasz Anforowiczeb19ac62022-02-05 00:10:16 +0000282 RsTypeKind::Reference { referent: lhs, mutability: Mutability::Const, .. },
283 RsTypeKind::Reference { referent: rhs, mutability: Mutability::Const, .. },
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +0000284 ) => match **lhs {
285 RsTypeKind::Record(lhs_record) => {
Lukasz Anforowicz732ca642022-02-03 20:58:38 +0000286 let lhs: Ident = make_rs_ident(&lhs_record.identifier.identifier);
287 let rhs: TokenStream = rhs.format(ir, &lifetime_to_name)?;
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +0000288 format_first_param_as_self = true;
289 func_name = make_rs_ident("eq");
Lukasz Anforowicz732ca642022-02-03 20:58:38 +0000290 impl_kind = ImplKind::Trait {
291 trait_name: quote! {PartialEq<#rhs>},
292 record_name: lhs,
293 };
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +0000294 }
Marcel Hlopko14ee3c82022-02-09 09:46:23 +0000295 _ => {
296 return make_unsupported_result(
297 "operator== where lhs doesn't refer to a record",
298 );
299 }
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +0000300 },
Marcel Hlopko14ee3c82022-02-09 09:46:23 +0000301 _ => {
302 return make_unsupported_result(
303 "operator== where operands are not const references",
304 );
305 }
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +0000306 };
307 }
Lukasz Anforowicz9c663ca2022-02-09 01:33:31 +0000308 UnqualifiedIdentifier::Operator(_) => {
Lukasz Anforowiczeb19ac62022-02-05 00:10:16 +0000309 return make_unsupported_result("Bindings for this kind of operator are not supported");
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +0000310 }
Devin Jeanpierref2ec8712021-10-13 20:47:16 +0000311 UnqualifiedIdentifier::Identifier(id) => {
Marcel Hlopkoeaae9b72022-01-21 15:54:11 +0000312 func_name = make_rs_ident(&id.identifier);
Lukasz Anforowiczf9564622022-01-28 14:31:04 +0000313 match maybe_record {
314 None => {
315 impl_kind = ImplKind::None;
316 format_first_param_as_self = false;
317 }
318 Some(record) => {
319 impl_kind = ImplKind::Struct;
320 if func.is_instance_method() {
321 let first_param = param_type_kinds.first().ok_or_else(|| {
322 anyhow!("Missing `__this` parameter in an instance method: {:?}", func)
323 })?;
324 format_first_param_as_self = first_param.is_ref_to(record)
325 } else {
326 format_first_param_as_self = false;
327 }
328 }
329 };
Michael Forstered642022021-10-04 09:48:25 +0000330 }
Devin Jeanpierre91de7012021-10-21 12:53:51 +0000331 UnqualifiedIdentifier::Destructor => {
Lukasz Anforowiczab65e292022-01-14 23:04:21 +0000332 // Note: to avoid double-destruction of the fields, they are all wrapped in
333 // ManuallyDrop in this case. See `generate_record`.
334 let record =
335 maybe_record.ok_or_else(|| anyhow!("Destructors must be member functions."))?;
Lukasz Anforowicze57215c2022-01-12 14:54:16 +0000336 if !should_implement_drop(record) {
Lukasz Anforowiczeb19ac62022-02-05 00:10:16 +0000337 return Ok(GeneratedFunc::None);
Devin Jeanpierre91de7012021-10-21 12:53:51 +0000338 }
Lukasz Anforowicz732ca642022-02-03 20:58:38 +0000339 let record_name = maybe_record_name
340 .clone()
341 .ok_or_else(|| anyhow!("Destructors must be member functions."))?;
342 impl_kind = ImplKind::Trait { trait_name: quote! {Drop}, record_name };
Marcel Hlopkoeaae9b72022-01-21 15:54:11 +0000343 func_name = make_rs_ident("drop");
Lukasz Anforowiczab65e292022-01-14 23:04:21 +0000344 format_first_param_as_self = true;
Devin Jeanpierre91de7012021-10-21 12:53:51 +0000345 }
Lukasz Anforowicz13cf7492021-12-22 15:29:52 +0000346 UnqualifiedIdentifier::Constructor => {
Lukasz Anforowicz71716b72022-01-26 17:05:05 +0000347 let member_func_metadata = func
348 .member_func_metadata
349 .as_ref()
350 .ok_or_else(|| anyhow!("Constructors must be member functions."))?;
351 let record = maybe_record
352 .ok_or_else(|| anyhow!("Constructors must be associated with a record."))?;
353 let instance_method_metadata =
354 member_func_metadata
355 .instance_method_metadata
356 .as_ref()
357 .ok_or_else(|| anyhow!("Constructors must be instance methods."))?;
358
Devin Jeanpierre88343c72022-01-15 01:10:23 +0000359 if !record.is_unpin() {
Lukasz Anforowiczab65e292022-01-14 23:04:21 +0000360 // TODO: Handle <internal link>
Lukasz Anforowiczeb19ac62022-02-05 00:10:16 +0000361 return make_unsupported_result(
362 "Bindings for constructors of non-trivial types are not supported yet",
363 );
Lukasz Anforowicz9bab8352021-12-22 17:35:31 +0000364 }
Lukasz Anforowicz55673c92022-01-27 19:37:26 +0000365 if is_unsafe {
366 // TODO(b/216648347): Allow this outside of traits (e.g. after supporting
367 // translating C++ constructors into static methods in Rust).
Lukasz Anforowiczeb19ac62022-02-05 00:10:16 +0000368 return make_unsupported_result(
369 "Unsafe constructors (e.g. with no elided or explicit lifetimes) \
370 are intentionally not supported",
371 );
Lukasz Anforowicz55673c92022-01-27 19:37:26 +0000372 }
373
Lukasz Anforowicz732ca642022-02-03 20:58:38 +0000374 let record_name = maybe_record_name
375 .clone()
376 .ok_or_else(|| anyhow!("Constructors must be member functions."))?;
Lukasz Anforowicz2e41bb62022-01-11 18:23:07 +0000377 match func.params.len() {
Lukasz Anforowiczf9564622022-01-28 14:31:04 +0000378 0 => bail!("Missing `__this` parameter in a constructor: {:?}", func),
Lukasz Anforowicz2e41bb62022-01-11 18:23:07 +0000379 1 => {
Lukasz Anforowicz732ca642022-02-03 20:58:38 +0000380 impl_kind = ImplKind::Trait { trait_name: quote! {Default}, record_name };
Marcel Hlopkoeaae9b72022-01-21 15:54:11 +0000381 func_name = make_rs_ident("default");
Lukasz Anforowiczab65e292022-01-14 23:04:21 +0000382 format_first_param_as_self = false;
Lukasz Anforowicz2e41bb62022-01-11 18:23:07 +0000383 }
Lukasz Anforowicz73326af2022-01-05 01:13:10 +0000384 2 => {
Lukasz Anforowicz2e41bb62022-01-11 18:23:07 +0000385 // TODO(lukasza): Do something smart with move constructor.
Lukasz Anforowiczf7bdd392022-01-21 00:33:39 +0000386 if param_type_kinds[1].is_shared_ref_to(record) {
Lukasz Anforowicz2e41bb62022-01-11 18:23:07 +0000387 // Copy constructor
388 if should_derive_clone(record) {
Lukasz Anforowiczeb19ac62022-02-05 00:10:16 +0000389 return Ok(GeneratedFunc::None);
Lukasz Anforowicz2e41bb62022-01-11 18:23:07 +0000390 } else {
Lukasz Anforowicz732ca642022-02-03 20:58:38 +0000391 impl_kind = ImplKind::Trait { trait_name: quote! {Clone}, record_name };
Marcel Hlopkoeaae9b72022-01-21 15:54:11 +0000392 func_name = make_rs_ident("clone");
Lukasz Anforowiczab65e292022-01-14 23:04:21 +0000393 format_first_param_as_self = true;
Lukasz Anforowicz2e41bb62022-01-11 18:23:07 +0000394 }
Lukasz Anforowicz71716b72022-01-26 17:05:05 +0000395 } else if !instance_method_metadata.is_explicit_ctor {
Lukasz Anforowicz2e41bb62022-01-11 18:23:07 +0000396 let param_type = &param_types[1];
Lukasz Anforowicz732ca642022-02-03 20:58:38 +0000397 impl_kind = ImplKind::Trait {
398 trait_name: quote! {From< #param_type >},
399 record_name,
400 };
Marcel Hlopkoeaae9b72022-01-21 15:54:11 +0000401 func_name = make_rs_ident("from");
Lukasz Anforowiczab65e292022-01-14 23:04:21 +0000402 format_first_param_as_self = false;
Lukasz Anforowicz71716b72022-01-26 17:05:05 +0000403 } else {
Marcel Hlopko14ee3c82022-02-09 09:46:23 +0000404 return make_unsupported_result(
405 "Not yet supported type of constructor parameter",
406 );
Lukasz Anforowicz73326af2022-01-05 01:13:10 +0000407 }
408 }
409 _ => {
Lukasz Anforowicz55673c92022-01-27 19:37:26 +0000410 // TODO(b/216648347): Support bindings for other constructors.
Marcel Hlopko14ee3c82022-02-09 09:46:23 +0000411 return make_unsupported_result(
412 "More than 1 constructor parameter is not supported yet",
413 );
Lukasz Anforowicz73326af2022-01-05 01:13:10 +0000414 }
Lukasz Anforowiczab65e292022-01-14 23:04:21 +0000415 }
416 }
417 }
418
419 let api_func_def = {
Lukasz Anforowicz95551272022-01-20 00:02:24 +0000420 // Clone params, return type, etc - we may need to mutate them in the
421 // API func, but we want to retain the originals for the thunk.
Lukasz Anforowiczab65e292022-01-14 23:04:21 +0000422 let mut return_type_fragment = return_type_fragment.clone();
423 let mut thunk_args = param_idents.iter().map(|id| quote! { #id}).collect_vec();
424 let mut api_params = param_idents
425 .iter()
426 .zip(param_types.iter())
427 .map(|(ident, type_)| quote! { #ident : #type_ })
428 .collect_vec();
Lukasz Anforowicz95551272022-01-20 00:02:24 +0000429 let mut lifetimes = func.lifetime_params.iter().collect_vec();
Lukasz Anforowiczf7bdd392022-01-21 00:33:39 +0000430 let mut maybe_first_api_param = param_type_kinds.get(0);
Lukasz Anforowiczab65e292022-01-14 23:04:21 +0000431
432 if func.name == UnqualifiedIdentifier::Constructor {
433 return_type_fragment = quote! { -> Self };
434
Lukasz Anforowiczf9564622022-01-28 14:31:04 +0000435 // Drop `__this` parameter from the public Rust API. Presence of
436 // element #0 is indirectly verified by a `Constructor`-related
437 // `match` branch a little bit above.
438 api_params.remove(0);
439 thunk_args.remove(0);
Lukasz Anforowicz95551272022-01-20 00:02:24 +0000440
Lukasz Anforowicz326c4e42022-01-27 14:43:00 +0000441 // Remove the lifetime associated with `__this`.
Lukasz Anforowicz90bdb962022-02-14 21:07:45 +0000442 ensure!(func.return_type.rs_type.is_unit_type(),
443 "Unexpectedly non-void return type of a constructor: {:?}", func);
Lukasz Anforowiczf7bdd392022-01-21 00:33:39 +0000444 let maybe_first_lifetime = func.params[0].type_.rs_type.lifetime_args.first();
Lukasz Anforowicz55673c92022-01-27 19:37:26 +0000445 let no_longer_needed_lifetime_id = maybe_first_lifetime
446 .ok_or_else(|| anyhow!("Missing lifetime on `__this` parameter: {:?}", func))?;
447 lifetimes.retain(|l| l.id != *no_longer_needed_lifetime_id);
Lukasz Anforowicz55673c92022-01-27 19:37:26 +0000448 if let Some(type_still_dependent_on_removed_lifetime) = param_type_kinds
449 .iter()
450 .skip(1) // Skipping `__this`
Lukasz Anforowicz90bdb962022-02-14 21:07:45 +0000451 .flat_map(|t| t.lifetimes())
452 .find(|lifetime_id| *lifetime_id == *no_longer_needed_lifetime_id)
Lukasz Anforowicz55673c92022-01-27 19:37:26 +0000453 {
454 bail!(
455 "The lifetime of `__this` is unexpectedly also used by another \
456 parameter {:?} in function {:?}",
457 type_still_dependent_on_removed_lifetime,
458 func.name
459 );
Lukasz Anforowicz95551272022-01-20 00:02:24 +0000460 }
461
462 // Rebind `maybe_first_api_param` to the next param after `__this`.
Lukasz Anforowiczf7bdd392022-01-21 00:33:39 +0000463 maybe_first_api_param = param_type_kinds.get(1);
Lukasz Anforowiczab65e292022-01-14 23:04:21 +0000464 }
465
466 // Change `__this: &'a SomeStruct` into `&'a self` if needed.
467 if format_first_param_as_self {
468 let first_api_param = maybe_first_api_param
469 .ok_or_else(|| anyhow!("No parameter to format as 'self': {:?}", func))?;
Lukasz Anforowiczcde4b1b2022-02-03 21:20:55 +0000470 let self_decl =
471 first_api_param.format_as_self_param(func, ir, &lifetime_to_name).with_context(
472 || format!("Failed to format as `self` param: {:?}", first_api_param),
473 )?;
Lukasz Anforowiczf9564622022-01-28 14:31:04 +0000474 // Presence of element #0 is verified by `ok_or_else` on
475 // `maybe_first_api_param` above.
476 api_params[0] = self_decl;
477 thunk_args[0] = quote! { self };
Lukasz Anforowiczab65e292022-01-14 23:04:21 +0000478 }
479
480 let func_body = match &func.name {
Lukasz Anforowicz9c663ca2022-02-09 01:33:31 +0000481 UnqualifiedIdentifier::Identifier(_) | UnqualifiedIdentifier::Operator(_) => {
Lukasz Anforowiczf7bdd392022-01-21 00:33:39 +0000482 let mut body = quote! { crate::detail::#thunk_ident( #( #thunk_args ),* ) };
483 // Only need to wrap everything in an `unsafe { ... }` block if
484 // the *whole* api function is safe.
485 if !is_unsafe {
486 body = quote! { unsafe { #body } };
487 }
488 body
489 }
490 UnqualifiedIdentifier::Destructor => {
Lukasz Anforowiczab65e292022-01-14 23:04:21 +0000491 quote! { unsafe { crate::detail::#thunk_ident( #( #thunk_args ),* ) } }
492 }
493 UnqualifiedIdentifier::Constructor => {
494 // SAFETY: A user-defined constructor is not guaranteed to
495 // initialize all the fields. To make the `assume_init()` call
496 // below safe, the memory is zero-initialized first. This is a
497 // bit safer, because zero-initialized memory represents a valid
498 // value for the currently supported field types (this may
499 // change once the bindings generator starts supporting
500 // reference fields). TODO(b/213243309): Double-check if
501 // zero-initialization is desirable here.
502 quote! {
503 let mut tmp = std::mem::MaybeUninit::<Self>::zeroed();
504 unsafe {
505 crate::detail::#thunk_ident( &mut tmp #( , #thunk_args )* );
506 tmp.assume_init()
Lukasz Anforowicz13cf7492021-12-22 15:29:52 +0000507 }
Lukasz Anforowicz13cf7492021-12-22 15:29:52 +0000508 }
Lukasz Anforowiczab65e292022-01-14 23:04:21 +0000509 }
510 };
511
Lukasz Anforowiczf7bdd392022-01-21 00:33:39 +0000512 let (pub_, unsafe_) = match impl_kind {
513 ImplKind::None | ImplKind::Struct => (
514 quote! { pub },
515 if is_unsafe {
516 quote! {unsafe}
517 } else {
518 quote! {}
519 },
520 ),
Lukasz Anforowicz732ca642022-02-03 20:58:38 +0000521 ImplKind::Trait { .. } => {
Lukasz Anforowicz55673c92022-01-27 19:37:26 +0000522 // Currently supported bindings have no unsafe trait functions.
523 assert!(!is_unsafe || func.name == UnqualifiedIdentifier::Destructor);
524 (quote! {}, quote! {})
525 }
Lukasz Anforowiczab65e292022-01-14 23:04:21 +0000526 };
527
Lukasz Anforowicz95551272022-01-20 00:02:24 +0000528 let lifetimes = lifetimes.into_iter().map(|l| format_lifetime_name(&l.name));
529 let generic_params = format_generic_params(lifetimes);
530
Lukasz Anforowiczab65e292022-01-14 23:04:21 +0000531 quote! {
532 #[inline(always)]
Lukasz Anforowiczf7bdd392022-01-21 00:33:39 +0000533 #pub_ #unsafe_ fn #func_name #generic_params( #( #api_params ),* ) #return_type_fragment {
Lukasz Anforowiczab65e292022-01-14 23:04:21 +0000534 #func_body
535 }
Lukasz Anforowicz13cf7492021-12-22 15:29:52 +0000536 }
Michael Forstered642022021-10-04 09:48:25 +0000537 };
538
Lukasz Anforowiczab65e292022-01-14 23:04:21 +0000539 let api_func: TokenStream;
540 let function_id: FunctionId;
Lukasz Anforowiczab65e292022-01-14 23:04:21 +0000541 match impl_kind {
542 ImplKind::None => {
543 api_func = quote! { #doc_comment #api_func_def };
544 function_id = FunctionId { self_type: None, function_path: func_name.into() };
545 }
546 ImplKind::Struct => {
547 let record_name =
548 maybe_record_name.ok_or_else(|| anyhow!("Struct methods must have records"))?;
549 api_func = quote! { impl #record_name { #doc_comment #api_func_def } };
550 function_id = FunctionId {
551 self_type: None,
552 function_path: syn::parse2(quote! { #record_name :: #func_name })?,
553 };
554 }
Lukasz Anforowicz732ca642022-02-03 20:58:38 +0000555 ImplKind::Trait { trait_name, record_name } => {
Lukasz Anforowiczab65e292022-01-14 23:04:21 +0000556 api_func = quote! { #doc_comment impl #trait_name for #record_name { #api_func_def } };
557 function_id = FunctionId {
558 self_type: Some(record_name.into()),
559 function_path: syn::parse2(quote! { #trait_name :: #func_name })?,
560 };
561 }
562 }
563
Lukasz Anforowicz6d553632022-01-06 21:36:14 +0000564 let thunk = {
Devin Jeanpierre7e9a1de2021-12-03 08:04:22 +0000565 let thunk_attr = if can_skip_cc_thunk(func) {
566 quote! {#[link_name = #mangled_name]}
567 } else {
568 quote! {}
569 };
570
Lukasz Anforowicz40c2eb82022-01-11 18:22:31 +0000571 // For constructors inject MaybeUninit into the type of `__this_` parameter.
572 let mut param_types = param_types;
573 if func.name == UnqualifiedIdentifier::Constructor {
574 if param_types.is_empty() || func.params.is_empty() {
575 bail!("Constructors should have at least one parameter (__this)");
576 }
Lukasz Anforowiczf7bdd392022-01-21 00:33:39 +0000577 param_types[0] = param_type_kinds[0]
Lukasz Anforowiczcde4b1b2022-02-03 21:20:55 +0000578 .format_mut_ref_as_uninitialized(ir, &lifetime_to_name)
Lukasz Anforowicz231a3bb2022-01-12 14:05:59 +0000579 .with_context(|| {
580 format!("Failed to format `__this` param for a thunk: {:?}", func.params[0])
581 })?;
Lukasz Anforowicz40c2eb82022-01-11 18:22:31 +0000582 }
583
Lukasz Anforowicz95551272022-01-20 00:02:24 +0000584 let lifetimes = func.lifetime_params.iter().map(|l| format_lifetime_name(&l.name));
585 let generic_params = format_generic_params(lifetimes);
586
Devin Jeanpierre7e9a1de2021-12-03 08:04:22 +0000587 quote! {
588 #thunk_attr
Lukasz Anforowicz4ad012b2021-12-15 18:13:40 +0000589 pub(crate) fn #thunk_ident #generic_params( #( #param_idents: #param_types ),*
590 ) #return_type_fragment ;
Devin Jeanpierre7e9a1de2021-12-03 08:04:22 +0000591 }
Michael Forstered642022021-10-04 09:48:25 +0000592 };
593
Lukasz Anforowiczeb19ac62022-02-05 00:10:16 +0000594 Ok(GeneratedFunc::Some { api_func: api_func.into(), thunk: thunk.into(), function_id })
Michael Forstered642022021-10-04 09:48:25 +0000595}
596
Michael Forstercc5941a2021-10-07 07:12:24 +0000597fn generate_doc_comment(comment: &Option<String>) -> TokenStream {
598 match comment {
Michael Forster028800b2021-10-05 12:39:59 +0000599 Some(text) => {
Marcel Hlopko89547752021-12-10 09:39:41 +0000600 // token_stream_printer (and rustfmt) don't put a space between /// and the doc
601 // comment, let's add it here so our comments are pretty.
Lukasz Anforowiczdd9ae0f2022-02-17 15:52:53 +0000602 let doc = format!(" {}", text.replace('\n', "\n "));
Michael Forster028800b2021-10-05 12:39:59 +0000603 quote! {#[doc=#doc]}
604 }
605 None => quote! {},
Michael Forstercc5941a2021-10-07 07:12:24 +0000606 }
607}
Lukasz Anforowiczdaff0402021-12-23 00:37:50 +0000608
609fn format_generic_params<T: quote::ToTokens>(params: impl IntoIterator<Item = T>) -> TokenStream {
610 let mut params = params.into_iter().peekable();
611 if params.peek().is_none() {
612 quote! {}
613 } else {
614 quote! { < #( #params ),* > }
615 }
616}
617
Lukasz Anforowicze57215c2022-01-12 14:54:16 +0000618fn should_implement_drop(record: &Record) -> bool {
619 match record.destructor.definition {
620 // TODO(b/202258760): Only omit destructor if `Copy` is specified.
621 SpecialMemberDefinition::Trivial => false,
622
623 // TODO(b/212690698): Avoid calling into the C++ destructor (e.g. let
624 // Rust drive `drop`-ing) to avoid (somewhat unergonomic) ManuallyDrop
625 // if we can ask Rust to preserve C++ field destruction order in
626 // NontrivialMembers case.
627 SpecialMemberDefinition::NontrivialMembers => true,
628
629 // The `impl Drop` for NontrivialUserDefined needs to call into the
630 // user-defined destructor on C++ side.
631 SpecialMemberDefinition::NontrivialUserDefined => true,
632
633 // TODO(b/213516512): Today the IR doesn't contain Func entries for
634 // deleted functions/destructors/etc. But, maybe we should generate
635 // `impl Drop` in this case? With `unreachable!`? With
636 // `std::mem::forget`?
637 SpecialMemberDefinition::Deleted => false,
638 }
639}
640
641/// Returns whether fields of type `ty` need to be wrapped in `ManuallyDrop<T>`
642/// to prevent the fields from being destructed twice (once by the C++
643/// destructor calkled from the `impl Drop` of the struct and once by `drop` on
644/// the Rust side).
645///
646/// A type is safe to destroy twice if it implements `Copy`. Fields of such
647/// don't need to be wrapped in `ManuallyDrop<T>` even if the struct
648/// containing the fields provides an `impl Drop` that calles into a C++
649/// destructor (in addition to dropping the fields on the Rust side).
650///
651/// Note that it is not enough to just be `!needs_drop<T>()`: Rust only
652/// guarantees that it is safe to use-after-destroy for `Copy` types. See
653/// e.g. the documentation for
654/// [`drop_in_place`](https://doc.rust-lang.org/std/ptr/fn.drop_in_place.html):
655///
656/// > if `T` is not `Copy`, using the pointed-to value after calling
657/// > `drop_in_place` can cause undefined behavior
658fn needs_manually_drop(ty: &ir::RsType, ir: &IR) -> Result<bool> {
659 let ty_implements_copy = RsTypeKind::new(ty, ir)?.implements_copy();
660 Ok(!ty_implements_copy)
661}
662
Michael Forsterbee84482021-10-13 08:35:38 +0000663/// Generates Rust source code for a given `Record` and associated assertions as
664/// a tuple.
Marcel Hlopkoc0956cf2021-11-29 08:31:28 +0000665fn generate_record(record: &Record, ir: &IR) -> Result<(RsSnippet, RsSnippet)> {
Marcel Hlopkoeaae9b72022-01-21 15:54:11 +0000666 let ident = make_rs_ident(&record.identifier.identifier);
Michael Forstercc5941a2021-10-07 07:12:24 +0000667 let doc_comment = generate_doc_comment(&record.doc_comment);
Marcel Hlopkob4b28742021-09-15 12:45:20 +0000668 let field_idents =
Marcel Hlopkoeaae9b72022-01-21 15:54:11 +0000669 record.fields.iter().map(|f| make_rs_ident(&f.identifier.identifier)).collect_vec();
Michael Forstercc5941a2021-10-07 07:12:24 +0000670 let field_doc_coments =
671 record.fields.iter().map(|f| generate_doc_comment(&f.doc_comment)).collect_vec();
Devin Jeanpierre09c6f452021-09-29 07:34:24 +0000672 let field_types = record
673 .fields
674 .iter()
Devin Jeanpierreb69bcae2022-02-03 09:45:50 +0000675 .enumerate()
676 .map(|(i, f)| {
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +0000677 // [[no_unique_address]] fields are replaced by an unaligned block of memory
678 // which fills space up to the next field.
Devin Jeanpierreb69bcae2022-02-03 09:45:50 +0000679 // See: docs/struct_layout
680 if f.is_no_unique_address {
681 let next_offset = if let Some(next) = record.fields.get(i + 1) {
682 next.offset
683 } else {
684 record.size * 8
685 };
686 let width = Literal::usize_unsuffixed((next_offset - f.offset) / 8);
687 return Ok(quote! {[std::mem::MaybeUninit<u8>; #width]});
688 }
Lukasz Anforowicze57215c2022-01-12 14:54:16 +0000689 let mut formatted = format_rs_type(&f.type_.rs_type, ir, &HashMap::new())
690 .with_context(|| {
Googlerb7e361d2022-01-04 14:02:59 +0000691 format!("Failed to format type for field {:?} on record {:?}", f, record)
692 })?;
Lukasz Anforowicze57215c2022-01-12 14:54:16 +0000693 // TODO(b/212696226): Verify cases where ManuallyDrop<T> is skipped
694 // via static asserts in the generated code.
695 if should_implement_drop(record) && needs_manually_drop(&f.type_.rs_type, ir)? {
696 // TODO(b/212690698): Avoid (somewhat unergonomic) ManuallyDrop
697 // if we can ask Rust to preserve field destruction order if the
698 // destructor is the SpecialMemberDefinition::NontrivialMembers
699 // case.
700 formatted = quote! { std::mem::ManuallyDrop<#formatted> }
Lukasz Anforowicz6d553632022-01-06 21:36:14 +0000701 };
Devin Jeanpierre7e9a1de2021-12-03 08:04:22 +0000702 Ok(formatted)
703 })
Devin Jeanpierre09c6f452021-09-29 07:34:24 +0000704 .collect::<Result<Vec<_>>>()?;
Googlerec589eb2021-09-17 07:45:39 +0000705 let field_accesses = record
706 .fields
707 .iter()
708 .map(|f| {
Devin Jeanpierreb69bcae2022-02-03 09:45:50 +0000709 if f.access == AccessSpecifier::Public && !f.is_no_unique_address {
Googlerec589eb2021-09-17 07:45:39 +0000710 quote! { pub }
711 } else {
712 quote! {}
713 }
714 })
715 .collect_vec();
Googlerec648ff2021-09-23 07:19:53 +0000716 let size = record.size;
717 let alignment = record.alignment;
Googleraaa0a532021-10-01 09:11:27 +0000718 let field_assertions =
719 record.fields.iter().zip(field_idents.iter()).map(|(field, field_ident)| {
720 let offset = field.offset;
721 quote! {
722 // The IR contains the offset in bits, while offset_of!()
723 // returns the offset in bytes, so we need to convert.
Googler209b10a2021-12-06 09:11:57 +0000724 const _: () = assert!(offset_of!(#ident, #field_ident) * 8 == #offset);
Googleraaa0a532021-10-01 09:11:27 +0000725 }
726 });
Michael Forsterdb8101a2021-10-08 06:56:03 +0000727 let mut record_features = BTreeSet::new();
728 let mut assertion_features = BTreeSet::new();
Devin Jeanpierre273eeae2021-10-06 13:29:35 +0000729
730 // TODO(mboehme): For the time being, we're using unstable features to
731 // be able to use offset_of!() in static assertions. This is fine for a
732 // prototype, but longer-term we want to either get those features
733 // stabilized or find an alternative. For more details, see
734 // b/200120034#comment15
Marcel Hlopkoeaae9b72022-01-21 15:54:11 +0000735 assertion_features.insert(make_rs_ident("const_ptr_offset_from"));
Devin Jeanpierre2ed14ec2021-10-06 11:32:19 +0000736
Lukasz Anforowicze57215c2022-01-12 14:54:16 +0000737 let derives = generate_derives(record);
Devin Jeanpierre9227d2c2021-10-06 12:26:05 +0000738 let derives = if derives.is_empty() {
739 quote! {}
740 } else {
741 quote! {#[derive( #(#derives),* )]}
742 };
Lukasz Anforowiczdd9ae0f2022-02-17 15:52:53 +0000743 let unpin_impl = if record.is_unpin() {
744 quote! {}
Devin Jeanpierreea700d32021-10-06 11:33:56 +0000745 } else {
Michael Forsterbee84482021-10-13 08:35:38 +0000746 // negative_impls are necessary for universal initialization due to Rust's
747 // coherence rules: PhantomPinned isn't enough to prove to Rust that a
748 // blanket impl that requires Unpin doesn't apply. See http://<internal link>=h.f6jp8ifzgt3n
Marcel Hlopkoeaae9b72022-01-21 15:54:11 +0000749 record_features.insert(make_rs_ident("negative_impls"));
Lukasz Anforowiczdd9ae0f2022-02-17 15:52:53 +0000750 quote! {
Michael Forsterdb8101a2021-10-08 06:56:03 +0000751 __NEWLINE__ __NEWLINE__
752 impl !Unpin for #ident {}
Lukasz Anforowiczdd9ae0f2022-02-17 15:52:53 +0000753 }
754 };
Devin Jeanpierre273eeae2021-10-06 13:29:35 +0000755
Devin Jeanpierrec80e6242022-02-03 01:56:40 +0000756 let mut repr_attributes = vec![quote! {C}];
757 if record.override_alignment && record.alignment > 1 {
758 let alignment = Literal::usize_unsuffixed(record.alignment);
759 repr_attributes.push(quote! {align(#alignment)});
760 }
761
762 // Adjust the struct to also include base class subobjects. We use an opaque
763 // field because subobjects can live in the alignment of base class
764 // subobjects.
765 let base_subobjects_field = if let Some(base_size) = record.base_size {
766 let n = proc_macro2::Literal::usize_unsuffixed(base_size);
767 quote! {
768 __base_class_subobjects: [std::mem::MaybeUninit<u8>; #n],
769 }
770 } else {
771 quote! {}
772 };
773
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +0000774 let empty_struct_placeholder_field =
775 if record.fields.is_empty() && record.base_size.unwrap_or(0) == 0 {
776 quote! {
777 /// Prevent empty C++ struct being zero-size in Rust.
778 placeholder: std::mem::MaybeUninit<u8>,
779 }
780 } else {
781 quote! {}
782 };
Googlerf4792062021-10-20 07:21:21 +0000783
Devin Jeanpierre58181ac2022-02-14 21:30:05 +0000784 let no_unique_address_accessors = cc_struct_no_unique_address_impl(record, ir)?;
Devin Jeanpierre56777022022-02-03 01:57:15 +0000785 let base_class_into = cc_struct_upcast_impl(record, ir)?;
786
Michael Forsterdb8101a2021-10-08 06:56:03 +0000787 let record_tokens = quote! {
Michael Forster028800b2021-10-05 12:39:59 +0000788 #doc_comment
Devin Jeanpierre9227d2c2021-10-06 12:26:05 +0000789 #derives
Devin Jeanpierrec80e6242022-02-03 01:56:40 +0000790 #[repr(#( #repr_attributes ),*)]
Marcel Hlopkob4b28742021-09-15 12:45:20 +0000791 pub struct #ident {
Devin Jeanpierrec80e6242022-02-03 01:56:40 +0000792 #base_subobjects_field
Michael Forstercc5941a2021-10-07 07:12:24 +0000793 #( #field_doc_coments #field_accesses #field_idents: #field_types, )*
Googlerf4792062021-10-20 07:21:21 +0000794 #empty_struct_placeholder_field
Marcel Hlopkob4b28742021-09-15 12:45:20 +0000795 }
Googlerec648ff2021-09-23 07:19:53 +0000796
Devin Jeanpierre58181ac2022-02-14 21:30:05 +0000797 #no_unique_address_accessors
798
Devin Jeanpierre56777022022-02-03 01:57:15 +0000799 #base_class_into
800
Devin Jeanpierreea700d32021-10-06 11:33:56 +0000801 #unpin_impl
Devin Jeanpierre273eeae2021-10-06 13:29:35 +0000802 };
803
Michael Forsterdb8101a2021-10-08 06:56:03 +0000804 let assertion_tokens = quote! {
Googler209b10a2021-12-06 09:11:57 +0000805 const _: () = assert!(std::mem::size_of::<#ident>() == #size);
806 const _: () = assert!(std::mem::align_of::<#ident>() == #alignment);
Michael Forsterdb8101a2021-10-08 06:56:03 +0000807 #( #field_assertions )*
808 };
809
810 Ok((
811 RsSnippet { features: record_features, tokens: record_tokens },
812 RsSnippet { features: assertion_features, tokens: assertion_tokens },
813 ))
Marcel Hlopkob4b28742021-09-15 12:45:20 +0000814}
815
Lukasz Anforowicz2e41bb62022-01-11 18:23:07 +0000816fn should_derive_clone(record: &Record) -> bool {
Devin Jeanpierre88343c72022-01-15 01:10:23 +0000817 record.is_unpin()
Devin Jeanpierre2ed14ec2021-10-06 11:32:19 +0000818 && record.copy_constructor.access == ir::AccessSpecifier::Public
819 && record.copy_constructor.definition == SpecialMemberDefinition::Trivial
Lukasz Anforowicz2e41bb62022-01-11 18:23:07 +0000820}
821
Lukasz Anforowicze57215c2022-01-12 14:54:16 +0000822fn should_derive_copy(record: &Record) -> bool {
823 // TODO(b/202258760): Make `Copy` inclusion configurable.
824 should_derive_clone(record)
825}
826
827fn generate_derives(record: &Record) -> Vec<Ident> {
828 let mut derives = vec![];
Lukasz Anforowicz2e41bb62022-01-11 18:23:07 +0000829 if should_derive_clone(record) {
Marcel Hlopkoeaae9b72022-01-21 15:54:11 +0000830 derives.push(make_rs_ident("Clone"));
Devin Jeanpierre2ed14ec2021-10-06 11:32:19 +0000831 }
Lukasz Anforowicze57215c2022-01-12 14:54:16 +0000832 if should_derive_copy(record) {
Marcel Hlopkoeaae9b72022-01-21 15:54:11 +0000833 derives.push(make_rs_ident("Copy"));
Lukasz Anforowicze57215c2022-01-12 14:54:16 +0000834 }
835 derives
Devin Jeanpierre2ed14ec2021-10-06 11:32:19 +0000836}
837
Googler6a0a5252022-01-11 14:08:09 +0000838fn generate_type_alias(type_alias: &TypeAlias, ir: &IR) -> Result<TokenStream> {
Marcel Hlopkoeaae9b72022-01-21 15:54:11 +0000839 let ident = make_rs_ident(&type_alias.identifier.identifier);
Googler6a0a5252022-01-11 14:08:09 +0000840 let underlying_type = format_rs_type(&type_alias.underlying_type.rs_type, ir, &HashMap::new())
841 .with_context(|| format!("Failed to format underlying type for {:?}", type_alias))?;
842 Ok(quote! {pub type #ident = #underlying_type;})
843}
844
Michael Forster523dbd42021-10-12 11:05:44 +0000845/// Generates Rust source code for a given `UnsupportedItem`.
846fn generate_unsupported(item: &UnsupportedItem) -> Result<TokenStream> {
Googler48a74dd2021-10-25 07:31:53 +0000847 let location = if item.source_loc.filename.is_empty() {
848 "<unknown location>".to_string()
849 } else {
850 // TODO(forster): The "google3" prefix should probably come from a command line
851 // argument.
852 // TODO(forster): Consider linking to the symbol instead of to the line number
853 // to avoid wrong links while generated files have not caught up.
854 format!("google3/{};l={}", &item.source_loc.filename, &item.source_loc.line)
855 };
Michael Forster6a184ad2021-10-12 13:04:05 +0000856 let message = format!(
Googler48a74dd2021-10-25 07:31:53 +0000857 "{}\nError while generating bindings for item '{}':\n{}",
858 &location, &item.name, &item.message
Michael Forster6a184ad2021-10-12 13:04:05 +0000859 );
Michael Forster523dbd42021-10-12 11:05:44 +0000860 Ok(quote! { __COMMENT__ #message })
861}
862
Michael Forsterf1dce422021-10-13 09:50:16 +0000863/// Generates Rust source code for a given `Comment`.
864fn generate_comment(comment: &Comment) -> Result<TokenStream> {
865 let text = &comment.text;
866 Ok(quote! { __COMMENT__ #text })
867}
868
Marcel Hlopko89547752021-12-10 09:39:41 +0000869fn generate_rs_api(ir: &IR) -> Result<TokenStream> {
Michael Forstered642022021-10-04 09:48:25 +0000870 let mut items = vec![];
Marcel Hlopko42abfc82021-08-09 07:03:17 +0000871 let mut thunks = vec![];
Michael Forsterdb8101a2021-10-08 06:56:03 +0000872 let mut assertions = vec![];
Marcel Hlopko42abfc82021-08-09 07:03:17 +0000873
Googler454f2652021-12-06 12:53:12 +0000874 // We import nullable pointers as an Option<&T> and assume that at the ABI
875 // level, None is represented as a zero pointer value whereas Some is
876 // represented as as non-zero pointer value. This seems like a pretty safe
877 // assumption to make, but to provide some safeguard, assert that
878 // `Option<&i32>` and `&i32` have the same size.
879 assertions.push(quote! {
880 const _: () = assert!(std::mem::size_of::<Option<&i32>>() == std::mem::size_of::<&i32>());
881 });
882
Michael Forsterbee84482021-10-13 08:35:38 +0000883 // TODO(jeanpierreda): Delete has_record, either in favor of using RsSnippet, or not
884 // having uses. See https://chat.google.com/room/AAAAnQmj8Qs/6QbkSvWcfhA
Devin Jeanpierreea700d32021-10-06 11:33:56 +0000885 let mut has_record = false;
Devin Jeanpierre273eeae2021-10-06 13:29:35 +0000886 let mut features = BTreeSet::new();
Marcel Hlopko42abfc82021-08-09 07:03:17 +0000887
Googlerd03d05b2022-01-07 10:10:57 +0000888 // Identify all functions having overloads that we can't import (yet).
889 // TODO(b/213280424): Implement support for overloaded functions.
890 let mut seen_funcs = HashSet::new();
891 let mut overloaded_funcs = HashSet::new();
892 for func in ir.functions() {
Lukasz Anforowiczeb19ac62022-02-05 00:10:16 +0000893 if let GeneratedFunc::Some { function_id, .. } = generate_func(func, ir)? {
Googlerd03d05b2022-01-07 10:10:57 +0000894 if !seen_funcs.insert(function_id.clone()) {
895 overloaded_funcs.insert(function_id);
896 }
897 }
898 }
899
Marcel Hlopko3b9bf9e2021-11-29 08:25:14 +0000900 for item in ir.items() {
Michael Forstered642022021-10-04 09:48:25 +0000901 match item {
Lukasz Anforowiczeb19ac62022-02-05 00:10:16 +0000902 Item::Func(func) => match generate_func(func, ir)? {
903 GeneratedFunc::None => (),
904 GeneratedFunc::Unsupported(unsupported) => {
905 items.push(generate_unsupported(&unsupported)?)
906 }
907 GeneratedFunc::Some { api_func, thunk, function_id } => {
Googlerd03d05b2022-01-07 10:10:57 +0000908 if overloaded_funcs.contains(&function_id) {
Lukasz Anforowiczeb19ac62022-02-05 00:10:16 +0000909 items.push(generate_unsupported(&make_unsupported_fn(
910 func,
911 ir,
912 "Cannot generate bindings for overloaded function",
913 )?)?);
Googlerd03d05b2022-01-07 10:10:57 +0000914 continue;
915 }
Lukasz Anforowiczeb19ac62022-02-05 00:10:16 +0000916 features.extend(api_func.features);
Googlerd03d05b2022-01-07 10:10:57 +0000917 features.extend(thunk.features);
Lukasz Anforowiczeb19ac62022-02-05 00:10:16 +0000918 items.push(api_func.tokens);
Googlerd03d05b2022-01-07 10:10:57 +0000919 thunks.push(thunk.tokens);
920 }
Lukasz Anforowiczeb19ac62022-02-05 00:10:16 +0000921 },
Michael Forstered642022021-10-04 09:48:25 +0000922 Item::Record(record) => {
Googler6a0a5252022-01-11 14:08:09 +0000923 if !ir.is_current_target(&record.owning_target)
924 && !ir.is_stdlib_target(&record.owning_target)
925 {
Marcel Hlopkoa0f38662021-12-03 08:45:26 +0000926 continue;
927 }
Marcel Hlopkoc0956cf2021-11-29 08:31:28 +0000928 let (snippet, assertions_snippet) = generate_record(record, ir)?;
Devin Jeanpierre273eeae2021-10-06 13:29:35 +0000929 features.extend(snippet.features);
Michael Forsterdb8101a2021-10-08 06:56:03 +0000930 features.extend(assertions_snippet.features);
Devin Jeanpierre273eeae2021-10-06 13:29:35 +0000931 items.push(snippet.tokens);
Michael Forsterdb8101a2021-10-08 06:56:03 +0000932 assertions.push(assertions_snippet.tokens);
Devin Jeanpierreea700d32021-10-06 11:33:56 +0000933 has_record = true;
Michael Forstered642022021-10-04 09:48:25 +0000934 }
Googler098c4582022-01-10 12:29:34 +0000935 Item::TypeAlias(type_alias) => {
Googler6a0a5252022-01-11 14:08:09 +0000936 if !ir.is_current_target(&type_alias.owning_target)
937 && !ir.is_stdlib_target(&type_alias.owning_target)
938 {
939 continue;
940 }
941 items.push(generate_type_alias(type_alias, ir)?);
Googler098c4582022-01-10 12:29:34 +0000942 }
Michael Forster523dbd42021-10-12 11:05:44 +0000943 Item::UnsupportedItem(unsupported) => items.push(generate_unsupported(unsupported)?),
Michael Forsterf1dce422021-10-13 09:50:16 +0000944 Item::Comment(comment) => items.push(generate_comment(comment)?),
Michael Forstered642022021-10-04 09:48:25 +0000945 }
Marcel Hlopko42abfc82021-08-09 07:03:17 +0000946 }
947
Marcel Hlopkob4b28742021-09-15 12:45:20 +0000948 let mod_detail = if thunks.is_empty() {
949 quote! {}
950 } else {
951 quote! {
952 mod detail {
Googler55647142022-01-11 12:37:39 +0000953 #[allow(unused_imports)]
Devin Jeanpierred4dde0e2021-10-13 20:48:25 +0000954 use super::*;
Marcel Hlopkob4b28742021-09-15 12:45:20 +0000955 extern "C" {
956 #( #thunks )*
957 }
Marcel Hlopko42abfc82021-08-09 07:03:17 +0000958 }
959 }
960 };
961
Devin Jeanpierreea700d32021-10-06 11:33:56 +0000962 let imports = if has_record {
Googlerec648ff2021-09-23 07:19:53 +0000963 quote! {
Googleraaa0a532021-10-01 09:11:27 +0000964 use memoffset_unstable_const::offset_of;
Googlerec648ff2021-09-23 07:19:53 +0000965 }
Michael Forstered642022021-10-04 09:48:25 +0000966 } else {
967 quote! {}
Googlerec648ff2021-09-23 07:19:53 +0000968 };
969
Devin Jeanpierre273eeae2021-10-06 13:29:35 +0000970 let features = if features.is_empty() {
971 quote! {}
972 } else {
973 quote! {
974 #![feature( #(#features),* )]
975 }
976 };
977
Marcel Hlopko89547752021-12-10 09:39:41 +0000978 Ok(quote! {
Googler55647142022-01-11 12:37:39 +0000979 #features __NEWLINE__
980 #![allow(non_camel_case_types)] __NEWLINE__
981 #![allow(non_snake_case)] __NEWLINE__ __NEWLINE__
982
Michael Forsterdb8101a2021-10-08 06:56:03 +0000983 #imports __NEWLINE__ __NEWLINE__
Googlerec648ff2021-09-23 07:19:53 +0000984
Michael Forsterdb8101a2021-10-08 06:56:03 +0000985 #( #items __NEWLINE__ __NEWLINE__ )*
Marcel Hlopkob4b28742021-09-15 12:45:20 +0000986
Michael Forsterdb8101a2021-10-08 06:56:03 +0000987 #mod_detail __NEWLINE__ __NEWLINE__
988
989 #( #assertions __NEWLINE__ __NEWLINE__ )*
Marcel Hlopko89547752021-12-10 09:39:41 +0000990 })
Marcel Hlopko42abfc82021-08-09 07:03:17 +0000991}
992
Marcel Hlopkoeaae9b72022-01-21 15:54:11 +0000993/// Makes an 'Ident' to be used in the Rust source code. Escapes Rust keywords.
994fn make_rs_ident(ident: &str) -> Ident {
995 // TODO(https://github.com/dtolnay/syn/pull/1098): Remove the hardcoded list once syn recognizes
996 // 2018 and 2021 keywords.
997 if ["async", "await", "try", "dyn"].contains(&ident) {
998 return format_ident!("r#{}", ident);
999 }
1000 match syn::parse_str::<syn::Ident>(ident) {
1001 Ok(_) => format_ident!("{}", ident),
1002 Err(_) => format_ident!("r#{}", ident),
1003 }
1004}
1005
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +00001006/// Formats a C++ identifier. Does not escape C++ keywords.
1007fn format_cc_ident(ident: &str) -> TokenStream {
1008 ident.parse().unwrap()
Marcel Hlopko42abfc82021-08-09 07:03:17 +00001009}
1010
Googler6a0a5252022-01-11 14:08:09 +00001011fn rs_type_name_for_target_and_identifier(
1012 owning_target: &BlazeLabel,
1013 identifier: &ir::Identifier,
1014 ir: &IR,
1015) -> Result<TokenStream> {
Marcel Hlopkoeaae9b72022-01-21 15:54:11 +00001016 let ident = make_rs_ident(identifier.identifier.as_str());
Googler6a0a5252022-01-11 14:08:09 +00001017
1018 if ir.is_current_target(owning_target) || ir.is_stdlib_target(owning_target) {
1019 Ok(quote! {#ident})
1020 } else {
Marcel Hlopkod906b892022-01-27 08:52:36 +00001021 let owning_crate_name = owning_target.target_name()?;
1022 // TODO(b/216587072): Remove this hacky escaping and use the import! macro once
1023 // available
Lukasz Anforowiczdd9ae0f2022-02-17 15:52:53 +00001024 let escaped_owning_crate_name = owning_crate_name.replace('-', "_");
Marcel Hlopkod906b892022-01-27 08:52:36 +00001025 let owning_crate = make_rs_ident(&escaped_owning_crate_name);
Googler6a0a5252022-01-11 14:08:09 +00001026 Ok(quote! {#owning_crate::#ident})
1027 }
1028}
1029
Lukasz Anforowicz40c2eb82022-01-11 18:22:31 +00001030#[derive(Debug, Eq, PartialEq)]
1031enum Mutability {
1032 Const,
1033 Mut,
1034}
1035
1036impl Mutability {
1037 fn format_for_pointer(&self) -> TokenStream {
1038 match self {
1039 Mutability::Mut => quote! {mut},
1040 Mutability::Const => quote! {const},
1041 }
1042 }
1043
1044 fn format_for_reference(&self) -> TokenStream {
1045 match self {
1046 Mutability::Mut => quote! {mut},
1047 Mutability::Const => quote! {},
1048 }
1049 }
1050}
1051
1052// TODO(b/213947473): Instead of having a separate RsTypeKind here, consider
1053// changing ir::RsType into a similar `enum`, with fields that contain
1054// references (e.g. &'ir Record`) instead of DeclIds.
1055#[derive(Debug)]
1056enum RsTypeKind<'ir> {
1057 Pointer { pointee: Box<RsTypeKind<'ir>>, mutability: Mutability },
1058 Reference { referent: Box<RsTypeKind<'ir>>, mutability: Mutability, lifetime_id: LifetimeId },
1059 Record(&'ir Record),
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00001060 TypeAlias { type_alias: &'ir TypeAlias, underlying_type: Box<RsTypeKind<'ir>> },
Lukasz Anforowicz40c2eb82022-01-11 18:22:31 +00001061 Unit,
1062 Other { name: &'ir str, type_args: Vec<RsTypeKind<'ir>> },
1063}
1064
1065impl<'ir> RsTypeKind<'ir> {
1066 pub fn new(ty: &'ir ir::RsType, ir: &'ir IR) -> Result<Self> {
1067 // The lambdas deduplicate code needed by multiple `match` branches.
1068 let get_type_args = || -> Result<Vec<RsTypeKind<'ir>>> {
1069 ty.type_args.iter().map(|type_arg| RsTypeKind::<'ir>::new(type_arg, ir)).collect()
1070 };
1071 let get_pointee = || -> Result<Box<RsTypeKind<'ir>>> {
1072 if ty.type_args.len() != 1 {
1073 bail!("Missing pointee/referent type (need exactly 1 type argument): {:?}", ty);
1074 }
1075 Ok(Box::new(get_type_args()?.remove(0)))
1076 };
1077 let get_lifetime = || -> Result<LifetimeId> {
1078 if ty.lifetime_args.len() != 1 {
1079 bail!("Missing reference lifetime (need exactly 1 lifetime argument): {:?}", ty);
1080 }
1081 Ok(ty.lifetime_args[0])
1082 };
1083
1084 let result = match ty.name.as_deref() {
1085 None => {
1086 ensure!(
1087 ty.type_args.is_empty(),
1088 "Type arguments on records nor type aliases are not yet supported: {:?}",
1089 ty
1090 );
1091 match ir.item_for_type(ty)? {
1092 Item::Record(record) => RsTypeKind::Record(record),
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00001093 Item::TypeAlias(type_alias) => RsTypeKind::TypeAlias {
1094 type_alias,
1095 underlying_type: Box::new(RsTypeKind::new(
1096 &type_alias.underlying_type.rs_type,
1097 ir,
1098 )?),
1099 },
Lukasz Anforowicz40c2eb82022-01-11 18:22:31 +00001100 other_item => bail!("Item does not define a type: {:?}", other_item),
1101 }
1102 }
1103 Some(name) => match name {
1104 "()" => {
1105 if !ty.type_args.is_empty() {
1106 bail!("Unit type must not have type arguments: {:?}", ty);
1107 }
1108 RsTypeKind::Unit
1109 }
1110 "*mut" => {
1111 RsTypeKind::Pointer { pointee: get_pointee()?, mutability: Mutability::Mut }
1112 }
1113 "*const" => {
1114 RsTypeKind::Pointer { pointee: get_pointee()?, mutability: Mutability::Const }
1115 }
1116 "&mut" => RsTypeKind::Reference {
1117 referent: get_pointee()?,
1118 mutability: Mutability::Mut,
1119 lifetime_id: get_lifetime()?,
1120 },
1121 "&" => RsTypeKind::Reference {
1122 referent: get_pointee()?,
1123 mutability: Mutability::Const,
1124 lifetime_id: get_lifetime()?,
1125 },
1126 name => RsTypeKind::Other { name, type_args: get_type_args()? },
1127 },
1128 };
1129 Ok(result)
1130 }
1131
1132 pub fn format(
1133 &self,
1134 ir: &IR,
1135 lifetime_to_name: &HashMap<LifetimeId, String>,
1136 ) -> Result<TokenStream> {
1137 let result = match self {
1138 RsTypeKind::Pointer { pointee, mutability } => {
1139 let mutability = mutability.format_for_pointer();
1140 let nested_type = pointee.format(ir, lifetime_to_name)?;
1141 quote! {* #mutability #nested_type}
1142 }
1143 RsTypeKind::Reference { referent, mutability, lifetime_id } => {
1144 let mutability = mutability.format_for_reference();
1145 let lifetime = Self::format_lifetime(lifetime_id, lifetime_to_name)?;
1146 let nested_type = referent.format(ir, lifetime_to_name)?;
1147 quote! {& #lifetime #mutability #nested_type}
1148 }
1149 RsTypeKind::Record(record) => rs_type_name_for_target_and_identifier(
1150 &record.owning_target,
1151 &record.identifier,
1152 ir,
1153 )?,
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00001154 RsTypeKind::TypeAlias { type_alias, .. } => rs_type_name_for_target_and_identifier(
Lukasz Anforowicz40c2eb82022-01-11 18:22:31 +00001155 &type_alias.owning_target,
1156 &type_alias.identifier,
1157 ir,
1158 )?,
1159 RsTypeKind::Unit => quote! {()},
1160 RsTypeKind::Other { name, type_args } => {
Marcel Hlopkoeaae9b72022-01-21 15:54:11 +00001161 let ident = make_rs_ident(name);
Lukasz Anforowicz40c2eb82022-01-11 18:22:31 +00001162 let generic_params = format_generic_params(
1163 type_args
1164 .iter()
1165 .map(|type_arg| type_arg.format(ir, lifetime_to_name))
1166 .collect::<Result<Vec<_>>>()?,
1167 );
1168 quote! {#ident #generic_params}
1169 }
1170 };
1171 Ok(result)
1172 }
1173
Lukasz Anforowiczcde4b1b2022-02-03 21:20:55 +00001174 /// Formats this RsTypeKind as `&'a mut MaybeUninit<SomeStruct>`. This is
1175 /// used to format `__this` parameter in a constructor thunk.
1176 pub fn format_mut_ref_as_uninitialized(
Lukasz Anforowicz40c2eb82022-01-11 18:22:31 +00001177 &self,
1178 ir: &IR,
1179 lifetime_to_name: &HashMap<LifetimeId, String>,
1180 ) -> Result<TokenStream> {
Lukasz Anforowiczcde4b1b2022-02-03 21:20:55 +00001181 match self {
1182 RsTypeKind::Reference { referent, lifetime_id, mutability: Mutability::Mut } => {
1183 let nested_type = referent.format(ir, lifetime_to_name)?;
1184 let lifetime = Self::format_lifetime(lifetime_id, lifetime_to_name)?;
1185 Ok(quote! { & #lifetime mut std::mem::MaybeUninit< #nested_type > })
Lukasz Anforowicz40c2eb82022-01-11 18:22:31 +00001186 }
Lukasz Anforowicz40c2eb82022-01-11 18:22:31 +00001187 _ => bail!("Unexpected type of `__this` parameter in a constructor: {:?}", self),
Lukasz Anforowiczcde4b1b2022-02-03 21:20:55 +00001188 }
Lukasz Anforowicz40c2eb82022-01-11 18:22:31 +00001189 }
1190
Lukasz Anforowicz231a3bb2022-01-12 14:05:59 +00001191 /// Formats this RsTypeKind as either `&'a self` or `&'a mut self`.
Lukasz Anforowiczcde4b1b2022-02-03 21:20:55 +00001192 pub fn format_as_self_param(
Lukasz Anforowicz231a3bb2022-01-12 14:05:59 +00001193 &self,
Lukasz Anforowiczce345392022-01-14 22:41:16 +00001194 func: &Func,
1195 ir: &IR,
Lukasz Anforowicz231a3bb2022-01-12 14:05:59 +00001196 lifetime_to_name: &HashMap<LifetimeId, String>,
Lukasz Anforowiczf9564622022-01-28 14:31:04 +00001197 ) -> Result<TokenStream> {
Lukasz Anforowicz732ca642022-02-03 20:58:38 +00001198 if func.name == UnqualifiedIdentifier::Destructor {
1199 let record = func
1200 .member_func_metadata
1201 .as_ref()
1202 .ok_or_else(|| anyhow!("Destructors must be member functions: {:?}", func))?
1203 .find_record(ir)?;
1204 if self.is_mut_ptr_to(record) {
1205 // Even in C++ it is UB to retain `this` pointer and dereference it
1206 // after a destructor runs. Therefore it is safe to use `&self` or
1207 // `&mut self` in Rust even if IR represents `__this` as a Rust
1208 // pointer (e.g. when lifetime annotations are missing - lifetime
1209 // annotations are required to represent it as a Rust reference).
1210 return Ok(quote! { &mut self });
1211 }
Lukasz Anforowicz231a3bb2022-01-12 14:05:59 +00001212 }
1213
1214 match self {
Lukasz Anforowicz231a3bb2022-01-12 14:05:59 +00001215 RsTypeKind::Reference { mutability, lifetime_id, .. } => {
1216 let mutability = mutability.format_for_reference();
1217 let lifetime = Self::format_lifetime(lifetime_id, lifetime_to_name)?;
Lukasz Anforowiczf9564622022-01-28 14:31:04 +00001218 Ok(quote! { & #lifetime #mutability self })
Lukasz Anforowicz231a3bb2022-01-12 14:05:59 +00001219 }
Lukasz Anforowicz732ca642022-02-03 20:58:38 +00001220 _ => bail!("Unexpected type of `self` parameter: {:?}", self),
Lukasz Anforowicz231a3bb2022-01-12 14:05:59 +00001221 }
1222 }
1223
Lukasz Anforowicz40c2eb82022-01-11 18:22:31 +00001224 fn format_lifetime(
1225 lifetime_id: &LifetimeId,
1226 lifetime_to_name: &HashMap<LifetimeId, String>,
1227 ) -> Result<TokenStream> {
1228 let lifetime_name = lifetime_to_name.get(lifetime_id).ok_or_else(|| {
1229 anyhow!("`lifetime_to_name` doesn't have an entry for {:?}", lifetime_id)
1230 })?;
Lukasz Anforowicz95551272022-01-20 00:02:24 +00001231 Ok(format_lifetime_name(lifetime_name))
Lukasz Anforowicz40c2eb82022-01-11 18:22:31 +00001232 }
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00001233
Lukasz Anforowicz90bdb962022-02-14 21:07:45 +00001234 /// Returns whether the type represented by `self` implements the `Copy`
1235 /// trait.
Lukasz Anforowicza94ab702022-01-14 22:40:25 +00001236 pub fn implements_copy(&self) -> bool {
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00001237 // TODO(b/212696226): Verify results of `implements_copy` via static
1238 // assertions in the generated Rust code (because incorrect results
1239 // can silently lead to unsafe behavior).
1240 match self {
1241 RsTypeKind::Unit => true,
1242 RsTypeKind::Pointer { .. } => true,
1243 RsTypeKind::Reference { mutability: Mutability::Const, .. } => true,
1244 RsTypeKind::Reference { mutability: Mutability::Mut, .. } => false,
1245 RsTypeKind::Record(record) => should_derive_copy(record),
1246 RsTypeKind::TypeAlias { underlying_type, .. } => underlying_type.implements_copy(),
Lukasz Anforowiczd81bea92022-02-11 08:57:58 +00001247 RsTypeKind::Other { type_args, .. } => {
Lukasz Anforowicz20651e32022-02-10 14:52:15 +00001248 // All types that may appear here without `type_args` (e.g.
1249 // primitive types like `i32`) implement `Copy`. Generic types
1250 // that may be present here (e.g. Option<...>) are `Copy` if all
1251 // of their `type_args` are `Copy`.
1252 type_args.iter().all(|t| t.implements_copy())
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00001253 }
1254 }
1255 }
Lukasz Anforowicza94ab702022-01-14 22:40:25 +00001256
Lukasz Anforowiczf9564622022-01-28 14:31:04 +00001257 pub fn is_mut_ptr_to(&self, expected_record: &Record) -> bool {
1258 match self {
1259 RsTypeKind::Pointer { pointee, mutability: Mutability::Mut, .. } => {
1260 pointee.is_record(expected_record)
1261 }
1262 _ => false,
1263 }
1264 }
1265
1266 pub fn is_ref_to(&self, expected_record: &Record) -> bool {
1267 match self {
1268 RsTypeKind::Reference { referent, .. } => referent.is_record(expected_record),
1269 _ => false,
1270 }
1271 }
1272
Lukasz Anforowicza94ab702022-01-14 22:40:25 +00001273 pub fn is_shared_ref_to(&self, expected_record: &Record) -> bool {
1274 match self {
1275 RsTypeKind::Reference { referent, mutability: Mutability::Const, .. } => {
Lukasz Anforowiczf9564622022-01-28 14:31:04 +00001276 referent.is_record(expected_record)
Lukasz Anforowicza94ab702022-01-14 22:40:25 +00001277 }
1278 _ => false,
1279 }
1280 }
Lukasz Anforowiczf9564622022-01-28 14:31:04 +00001281
1282 pub fn is_record(&self, expected_record: &Record) -> bool {
1283 match self {
1284 RsTypeKind::Record(actual_record) => actual_record.id == expected_record.id,
1285 _ => false,
1286 }
1287 }
Lukasz Anforowicz90bdb962022-02-14 21:07:45 +00001288
1289 /// Iterates over `self` and all the nested types (e.g. pointees, generic
1290 /// type args, etc.) in DFS order.
1291 pub fn dfs_iter<'ty>(&'ty self) -> impl Iterator<Item = &'ty RsTypeKind<'ir>> + '_ {
1292 RsTypeKindIter::new(self)
1293 }
1294
1295 /// Iterates over all `LifetimeId`s in `self` and in all the nested types.
1296 /// Note that the results might contain duplicate LifetimeId values (e.g.
1297 /// if the same LifetimeId is used in two `type_args`).
1298 pub fn lifetimes(&self) -> impl Iterator<Item = LifetimeId> + '_ {
1299 self.dfs_iter().filter_map(|t| match t {
1300 RsTypeKind::Reference { lifetime_id, .. } => Some(*lifetime_id),
1301 _ => None,
1302 })
1303 }
1304}
1305
1306struct RsTypeKindIter<'ty, 'ir> {
1307 todo: Vec<&'ty RsTypeKind<'ir>>,
1308}
1309
1310impl<'ty, 'ir> RsTypeKindIter<'ty, 'ir> {
1311 pub fn new(ty: &'ty RsTypeKind<'ir>) -> Self {
1312 Self { todo: vec![ty] }
1313 }
1314}
1315
1316impl<'ty, 'ir> Iterator for RsTypeKindIter<'ty, 'ir> {
1317 type Item = &'ty RsTypeKind<'ir>;
1318
1319 fn next(&mut self) -> Option<Self::Item> {
1320 match self.todo.pop() {
1321 None => None,
1322 Some(curr) => {
1323 match curr {
1324 RsTypeKind::Unit | RsTypeKind::Record(_) => (),
1325 RsTypeKind::Pointer { pointee, .. } => self.todo.push(pointee),
1326 RsTypeKind::Reference { referent, .. } => self.todo.push(referent),
1327 RsTypeKind::TypeAlias { underlying_type: t, .. } => self.todo.push(t),
1328 RsTypeKind::Other { type_args, .. } => self.todo.extend(type_args.iter().rev()),
1329 };
1330 Some(curr)
1331 }
1332 }
1333 }
Lukasz Anforowicz40c2eb82022-01-11 18:22:31 +00001334}
1335
Lukasz Anforowicz95551272022-01-20 00:02:24 +00001336fn format_lifetime_name(lifetime_name: &str) -> TokenStream {
1337 let lifetime =
1338 syn::Lifetime::new(&format!("'{}", lifetime_name), proc_macro2::Span::call_site());
1339 quote! { #lifetime }
1340}
1341
Googler7cced422021-12-06 11:58:39 +00001342fn format_rs_type(
1343 ty: &ir::RsType,
1344 ir: &IR,
1345 lifetime_to_name: &HashMap<LifetimeId, String>,
1346) -> Result<TokenStream> {
Lukasz Anforowicz40c2eb82022-01-11 18:22:31 +00001347 RsTypeKind::new(ty, ir)
1348 .and_then(|kind| kind.format(ir, lifetime_to_name))
1349 .with_context(|| format!("Failed to format Rust type {:?}", ty))
Devin Jeanpierre7a7328e2021-09-17 07:10:08 +00001350}
1351
Googler6a0a5252022-01-11 14:08:09 +00001352fn cc_type_name_for_item(item: &ir::Item) -> Result<TokenStream> {
1353 let (disambiguator_fragment, identifier) = match item {
1354 Item::Record(record) => (quote! { class }, &record.identifier),
1355 Item::TypeAlias(type_alias) => (quote! {}, &type_alias.identifier),
1356 _ => bail!("Item does not define a type: {:?}", item),
1357 };
1358
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +00001359 let ident = format_cc_ident(identifier.identifier.as_str());
Googler6a0a5252022-01-11 14:08:09 +00001360 Ok(quote! { #disambiguator_fragment #ident })
1361}
1362
Marcel Hlopkoc0956cf2021-11-29 08:31:28 +00001363fn format_cc_type(ty: &ir::CcType, ir: &IR) -> Result<TokenStream> {
Devin Jeanpierre09c6f452021-09-29 07:34:24 +00001364 let const_fragment = if ty.is_const {
Devin Jeanpierre184f9ac2021-09-17 13:47:03 +00001365 quote! {const}
1366 } else {
1367 quote! {}
1368 };
Marcel Hlopkoc0956cf2021-11-29 08:31:28 +00001369 if let Some(ref name) = ty.name {
1370 match name.as_str() {
1371 "*" => {
Googlerff7fc232021-12-02 09:43:00 +00001372 if ty.type_args.len() != 1 {
1373 bail!("Invalid pointer type (need exactly 1 type argument): {:?}", ty);
Marcel Hlopkoc0956cf2021-11-29 08:31:28 +00001374 }
Googlerff7fc232021-12-02 09:43:00 +00001375 assert_eq!(ty.type_args.len(), 1);
1376 let nested_type = format_cc_type(&ty.type_args[0], ir)?;
Marcel Hlopkoc0956cf2021-11-29 08:31:28 +00001377 Ok(quote! {#nested_type * #const_fragment})
Devin Jeanpierre7a7328e2021-09-17 07:10:08 +00001378 }
Lukasz Anforowicz275fa922022-01-05 16:13:20 +00001379 "&" => {
1380 if ty.type_args.len() != 1 {
1381 bail!("Invalid reference type (need exactly 1 type argument): {:?}", ty);
1382 }
1383 let nested_type = format_cc_type(&ty.type_args[0], ir)?;
1384 Ok(quote! {#nested_type &})
1385 }
Lukasz Anforowicz957cbf22022-01-05 16:14:05 +00001386 cc_type_name => {
Googlerff7fc232021-12-02 09:43:00 +00001387 if !ty.type_args.is_empty() {
Marcel Hlopkoc0956cf2021-11-29 08:31:28 +00001388 bail!("Type not yet supported: {:?}", ty);
1389 }
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +00001390 let idents = cc_type_name.split_whitespace().map(format_cc_ident);
Lukasz Anforowicz957cbf22022-01-05 16:14:05 +00001391 Ok(quote! {#( #idents )* #const_fragment})
Devin Jeanpierre7a7328e2021-09-17 07:10:08 +00001392 }
Devin Jeanpierre7a7328e2021-09-17 07:10:08 +00001393 }
Marcel Hlopkoc0956cf2021-11-29 08:31:28 +00001394 } else {
Googler6a0a5252022-01-11 14:08:09 +00001395 let item = ir.item_for_type(ty)?;
1396 let type_name = cc_type_name_for_item(item)?;
1397 Ok(quote! {#const_fragment #type_name})
Devin Jeanpierre7a7328e2021-09-17 07:10:08 +00001398 }
1399}
1400
Marcel Hlopkoa0f38662021-12-03 08:45:26 +00001401fn cc_struct_layout_assertion(record: &Record, ir: &IR) -> TokenStream {
Googler6a0a5252022-01-11 14:08:09 +00001402 if !ir.is_current_target(&record.owning_target) && !ir.is_stdlib_target(&record.owning_target) {
Marcel Hlopkoa0f38662021-12-03 08:45:26 +00001403 return quote! {};
1404 }
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +00001405 let record_ident = format_cc_ident(&record.identifier.identifier);
Googler5ea88642021-09-29 08:05:59 +00001406 let size = Literal::usize_unsuffixed(record.size);
1407 let alignment = Literal::usize_unsuffixed(record.alignment);
Lukasz Anforowicz74704712021-12-22 15:30:31 +00001408 let field_assertions =
1409 record.fields.iter().filter(|f| f.access == AccessSpecifier::Public).map(|field| {
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +00001410 let field_ident = format_cc_ident(&field.identifier.identifier);
Lukasz Anforowicz74704712021-12-22 15:30:31 +00001411 let offset = Literal::usize_unsuffixed(field.offset);
1412 // The IR contains the offset in bits, while C++'s offsetof()
1413 // returns the offset in bytes, so we need to convert.
1414 quote! {
Googler972d3582022-01-11 10:17:22 +00001415 static_assert(offsetof(class #record_ident, #field_ident) * 8 == #offset);
Lukasz Anforowicz74704712021-12-22 15:30:31 +00001416 }
1417 });
Googler5ea88642021-09-29 08:05:59 +00001418 quote! {
Googler972d3582022-01-11 10:17:22 +00001419 static_assert(sizeof(class #record_ident) == #size);
1420 static_assert(alignof(class #record_ident) == #alignment);
Googler5ea88642021-09-29 08:05:59 +00001421 #( #field_assertions )*
1422 }
1423}
1424
Devin Jeanpierre58181ac2022-02-14 21:30:05 +00001425// Returns the accessor functions for no_unique_address member variables.
1426fn cc_struct_no_unique_address_impl(record: &Record, ir: &IR) -> Result<TokenStream> {
1427 let mut fields = vec![];
1428 let mut types = vec![];
1429 for field in &record.fields {
1430 if field.access != AccessSpecifier::Public || !field.is_no_unique_address {
1431 continue;
1432 }
1433 fields.push(make_rs_ident(&field.identifier.identifier));
1434 types.push(format_rs_type(&field.type_.rs_type, ir, &HashMap::new()).with_context(
1435 || format!("Failed to format type for field {:?} on record {:?}", field, record),
1436 )?)
1437 }
1438
1439 if fields.is_empty() {
1440 return Ok(quote! {});
1441 }
1442
1443 let ident = make_rs_ident(&record.identifier.identifier);
1444 Ok(quote! {
1445 impl #ident {
1446 #(
1447 pub fn #fields(&self) -> &#types {
1448 unsafe {&* (&self.#fields as *const _ as *const #types)}
1449 }
1450 )*
1451 }
1452 })
1453}
1454
Devin Jeanpierre56777022022-02-03 01:57:15 +00001455/// Returns the implementation of base class conversions, for converting a type
1456/// to its unambiguous public base classes.
1457///
1458/// TODO(b/216195042): Implement this in terms of a supporting trait which casts
1459/// raw pointers. Then, we would have blanket impls for reference, pinned mut
1460/// reference, etc. conversion. The current version is just enough to test the
1461/// logic in importer.
1462//
1463// TODO(b/216195042): Should this use, like, AsRef/AsMut (and some equivalent
1464// for Pin)?
1465fn cc_struct_upcast_impl(record: &Record, ir: &IR) -> Result<TokenStream> {
1466 let mut impls = Vec::with_capacity(record.unambiguous_public_bases.len());
1467 for base in &record.unambiguous_public_bases {
1468 let base_record: &Record = ir.find_decl(base.base_record_id)?.try_into()?;
1469 if let Some(offset) = base.offset {
1470 let offset = Literal::i64_unsuffixed(offset);
1471 // TODO(b/216195042): Correctly handle imported records, lifetimes.
1472 let base_name = make_rs_ident(&base_record.identifier.identifier);
1473 let derived_name = make_rs_ident(&record.identifier.identifier);
1474 impls.push(quote! {
1475 impl<'a> From<&'a #derived_name> for &'a #base_name {
1476 fn from(x: &'a #derived_name) -> Self {
1477 unsafe {
1478 &*((x as *const _ as *const u8).offset(#offset) as *const #base_name)
1479 }
1480 }
1481 }
1482 });
1483 } else {
1484 // TODO(b/216195042): determine offset dynamically / use a dynamic
1485 // cast. This requires a new C++ function to be
1486 // generated, so that we have something to call.
1487 }
1488 }
1489
1490 Ok(quote! {
1491 #(#impls)*
1492 })
1493}
1494
Googlera675ae02021-12-07 08:04:59 +00001495fn thunk_ident(func: &Func) -> Ident {
1496 format_ident!("__rust_thunk__{}", func.mangled_name)
Devin Jeanpierref2ec8712021-10-13 20:47:16 +00001497}
1498
Marcel Hlopko89547752021-12-10 09:39:41 +00001499fn generate_rs_api_impl(ir: &IR) -> Result<TokenStream> {
Michael Forsterbee84482021-10-13 08:35:38 +00001500 // This function uses quote! to generate C++ source code out of convenience.
1501 // This is a bold idea so we have to continously evaluate if it still makes
1502 // sense or the cost of working around differences in Rust and C++ tokens is
1503 // greather than the value added.
Marcel Hlopko3164eee2021-08-24 20:09:22 +00001504 //
Michael Forsterbee84482021-10-13 08:35:38 +00001505 // See rs_bindings_from_cc/
1506 // token_stream_printer.rs for a list of supported placeholders.
Marcel Hlopko3164eee2021-08-24 20:09:22 +00001507 let mut thunks = vec![];
Michael Forster7ef80732021-10-01 18:12:19 +00001508 for func in ir.functions() {
Lukasz Anforowiczdd9ae0f2022-02-17 15:52:53 +00001509 if can_skip_cc_thunk(func) {
Marcel Hlopko3164eee2021-08-24 20:09:22 +00001510 continue;
1511 }
1512
Googlera675ae02021-12-07 08:04:59 +00001513 let thunk_ident = thunk_ident(func);
Devin Jeanpierref2ec8712021-10-13 20:47:16 +00001514 let implementation_function = match &func.name {
Lukasz Anforowicz9c663ca2022-02-09 01:33:31 +00001515 UnqualifiedIdentifier::Operator(op) => {
1516 let name = syn::parse_str::<TokenStream>(&op.name)?;
1517 quote! { operator #name }
1518 }
Devin Jeanpierref2ec8712021-10-13 20:47:16 +00001519 UnqualifiedIdentifier::Identifier(id) => {
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +00001520 let fn_ident = format_cc_ident(&id.identifier);
Lukasz Anforowiczaab8ad22021-12-19 20:29:26 +00001521 let static_method_metadata = func
1522 .member_func_metadata
1523 .as_ref()
1524 .filter(|meta| meta.instance_method_metadata.is_none());
1525 match static_method_metadata {
1526 None => quote! {#fn_ident},
1527 Some(meta) => {
Marcel Hlopkoeaae9b72022-01-21 15:54:11 +00001528 let record_ident =
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +00001529 format_cc_ident(&meta.find_record(ir)?.identifier.identifier);
Lukasz Anforowiczaab8ad22021-12-19 20:29:26 +00001530 quote! { #record_ident :: #fn_ident }
1531 }
1532 }
Devin Jeanpierref2ec8712021-10-13 20:47:16 +00001533 }
Lukasz Anforowicz7b0042d2022-01-06 23:00:19 +00001534 // Use `destroy_at` to avoid needing to spell out the class name. Destructor identiifers
Devin Jeanpierrecc6cf092021-12-16 04:31:14 +00001535 // use the name of the type itself, without namespace qualification, template
1536 // parameters, or aliases. We do not need to use that naming scheme anywhere else in
1537 // the bindings, and it can be difficult (impossible?) to spell in the general case. By
1538 // using destroy_at, we avoid needing to determine or remember what the correct spelling
Lukasz Anforowicz7b0042d2022-01-06 23:00:19 +00001539 // is. Similar arguments apply to `construct_at`.
Lukasz Anforowicze643ec92021-12-22 15:45:15 +00001540 UnqualifiedIdentifier::Constructor => {
Lukasz Anforowicz7b0042d2022-01-06 23:00:19 +00001541 quote! { rs_api_impl_support::construct_at }
Lukasz Anforowicze643ec92021-12-22 15:45:15 +00001542 }
Devin Jeanpierref2ec8712021-10-13 20:47:16 +00001543 UnqualifiedIdentifier::Destructor => quote! {std::destroy_at},
Devin Jeanpierref2ec8712021-10-13 20:47:16 +00001544 };
Marcel Hlopkoc0956cf2021-11-29 08:31:28 +00001545 let return_type_name = format_cc_type(&func.return_type.cc_type, ir)?;
Lukasz Anforowicze643ec92021-12-22 15:45:15 +00001546 let return_stmt = if func.return_type.cc_type.is_void() {
1547 quote! {}
1548 } else {
1549 quote! { return }
1550 };
Marcel Hlopko3164eee2021-08-24 20:09:22 +00001551
1552 let param_idents =
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +00001553 func.params.iter().map(|p| format_cc_ident(&p.identifier.identifier)).collect_vec();
Marcel Hlopko3164eee2021-08-24 20:09:22 +00001554
Devin Jeanpierre09c6f452021-09-29 07:34:24 +00001555 let param_types = func
1556 .params
1557 .iter()
Marcel Hlopkoc0956cf2021-11-29 08:31:28 +00001558 .map(|p| format_cc_type(&p.type_.cc_type, ir))
Devin Jeanpierre09c6f452021-09-29 07:34:24 +00001559 .collect::<Result<Vec<_>>>()?;
Marcel Hlopko3164eee2021-08-24 20:09:22 +00001560
Lukasz Anforowiczb3d89aa2022-01-12 14:35:52 +00001561 let needs_this_deref = match &func.member_func_metadata {
1562 None => false,
1563 Some(meta) => match &func.name {
1564 UnqualifiedIdentifier::Constructor | UnqualifiedIdentifier::Destructor => false,
Marcel Hlopko14ee3c82022-02-09 09:46:23 +00001565 UnqualifiedIdentifier::Identifier(_) | UnqualifiedIdentifier::Operator(_) => {
1566 meta.instance_method_metadata.is_some()
1567 }
Lukasz Anforowiczb3d89aa2022-01-12 14:35:52 +00001568 },
1569 };
1570 let (implementation_function, arg_expressions) = if !needs_this_deref {
1571 (implementation_function, param_idents.clone())
1572 } else {
1573 let this_param = func
1574 .params
1575 .first()
1576 .ok_or_else(|| anyhow!("Instance methods must have `__this` param."))?;
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +00001577 let this_arg = format_cc_ident(&this_param.identifier.identifier);
Lukasz Anforowiczb3d89aa2022-01-12 14:35:52 +00001578 (
1579 quote! { #this_arg -> #implementation_function},
1580 param_idents.iter().skip(1).cloned().collect_vec(),
1581 )
1582 };
1583
Marcel Hlopko3164eee2021-08-24 20:09:22 +00001584 thunks.push(quote! {
1585 extern "C" #return_type_name #thunk_ident( #( #param_types #param_idents ),* ) {
Lukasz Anforowiczb3d89aa2022-01-12 14:35:52 +00001586 #return_stmt #implementation_function( #( #arg_expressions ),* );
Marcel Hlopko3164eee2021-08-24 20:09:22 +00001587 }
1588 });
1589 }
1590
Marcel Hlopkoa0f38662021-12-03 08:45:26 +00001591 let layout_assertions = ir.records().map(|record| cc_struct_layout_assertion(record, ir));
Googler5ea88642021-09-29 08:05:59 +00001592
Devin Jeanpierre231ef8d2021-10-27 10:50:44 +00001593 let mut standard_headers = <BTreeSet<Ident>>::new();
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +00001594 standard_headers.insert(format_ident!("memory")); // ubiquitous.
Devin Jeanpierre231ef8d2021-10-27 10:50:44 +00001595 if ir.records().next().is_some() {
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +00001596 standard_headers.insert(format_ident!("cstddef"));
Devin Jeanpierre231ef8d2021-10-27 10:50:44 +00001597 };
Googler5ea88642021-09-29 08:05:59 +00001598
Lukasz Anforowicz4457baf2021-12-23 17:24:04 +00001599 let mut includes =
1600 vec!["rs_bindings_from_cc/support/cxx20_backports.h"];
1601
Michael Forsterbee84482021-10-13 08:35:38 +00001602 // In order to generate C++ thunk in all the cases Clang needs to be able to
1603 // access declarations from public headers of the C++ library.
Lukasz Anforowicz4457baf2021-12-23 17:24:04 +00001604 includes.extend(ir.used_headers().map(|i| &i.name as &str));
Marcel Hlopko3164eee2021-08-24 20:09:22 +00001605
Marcel Hlopko89547752021-12-10 09:39:41 +00001606 Ok(quote! {
Googler5ea88642021-09-29 08:05:59 +00001607 #( __HASH_TOKEN__ include <#standard_headers> __NEWLINE__)*
Devin Jeanpierre7c74f842022-02-03 07:08:06 +00001608 __NEWLINE__
Michael Forsterdb8101a2021-10-08 06:56:03 +00001609 #( __HASH_TOKEN__ include #includes __NEWLINE__)* __NEWLINE__
Marcel Hlopko3164eee2021-08-24 20:09:22 +00001610
Michael Forsterdb8101a2021-10-08 06:56:03 +00001611 #( #thunks )* __NEWLINE__ __NEWLINE__
Googler5ea88642021-09-29 08:05:59 +00001612
Michael Forsterdb8101a2021-10-08 06:56:03 +00001613 #( #layout_assertions __NEWLINE__ __NEWLINE__ )*
Marcel Hlopkoc6b726c2021-10-07 06:53:09 +00001614
1615 // To satisfy http://cs/symbol:devtools.metadata.Presubmit.CheckTerminatingNewline check.
1616 __NEWLINE__
Marcel Hlopko89547752021-12-10 09:39:41 +00001617 })
Marcel Hlopko3164eee2021-08-24 20:09:22 +00001618}
1619
Marcel Hlopko42abfc82021-08-09 07:03:17 +00001620#[cfg(test)]
1621mod tests {
Devin Jeanpierre45cb1162021-10-27 10:54:28 +00001622 use super::*;
Michael Forstered642022021-10-04 09:48:25 +00001623 use anyhow::anyhow;
Lukasz Anforowicz9c663ca2022-02-09 01:33:31 +00001624 use ir_testing::{ir_from_cc, ir_from_cc_dependency, ir_func, ir_record, retrieve_func};
Marcel Hlopko89547752021-12-10 09:39:41 +00001625 use token_stream_matchers::{
1626 assert_cc_matches, assert_cc_not_matches, assert_rs_matches, assert_rs_not_matches,
Devin Jeanpierre7e9a1de2021-12-03 08:04:22 +00001627 };
Michael Forsterdb8101a2021-10-08 06:56:03 +00001628 use token_stream_printer::tokens_to_string;
Marcel Hlopko42abfc82021-08-09 07:03:17 +00001629
1630 #[test]
Marcel Hlopko3b9bf9e2021-11-29 08:25:14 +00001631 // TODO(hlopko): Move this test to a more principled place where it can access
1632 // `ir_testing`.
1633 fn test_duplicate_decl_ids_err() {
1634 let mut r1 = ir_record("R1");
Marcel Hlopko264b9ad2021-12-02 21:06:44 +00001635 r1.id = DeclId(42);
Marcel Hlopko3b9bf9e2021-11-29 08:25:14 +00001636 let mut r2 = ir_record("R2");
Marcel Hlopko264b9ad2021-12-02 21:06:44 +00001637 r2.id = DeclId(42);
Marcel Hlopko3b9bf9e2021-11-29 08:25:14 +00001638 let result = make_ir_from_items([r1.into(), r2.into()]);
1639 assert!(result.is_err());
1640 assert!(result.unwrap_err().to_string().contains("Duplicate decl_id found in"));
1641 }
1642
1643 #[test]
Marcel Hlopko45fba972021-08-23 19:52:20 +00001644 fn test_simple_function() -> Result<()> {
Marcel Hlopko89547752021-12-10 09:39:41 +00001645 let ir = ir_from_cc("int Add(int a, int b);")?;
1646 let rs_api = generate_rs_api(&ir)?;
1647 assert_rs_matches!(
1648 rs_api,
1649 quote! {
Michael Forsterdb8101a2021-10-08 06:56:03 +00001650 #[inline(always)]
Marcel Hlopko89547752021-12-10 09:39:41 +00001651 pub fn Add(a: i32, b: i32) -> i32 {
Googlera675ae02021-12-07 08:04:59 +00001652 unsafe { crate::detail::__rust_thunk___Z3Addii(a, b) }
Marcel Hlopko89547752021-12-10 09:39:41 +00001653 }
1654 }
1655 );
1656 assert_rs_matches!(
1657 rs_api,
1658 quote! {
Michael Forsterdb8101a2021-10-08 06:56:03 +00001659 mod detail {
Googler55647142022-01-11 12:37:39 +00001660 #[allow(unused_imports)]
Devin Jeanpierred4dde0e2021-10-13 20:48:25 +00001661 use super::*;
Michael Forsterdb8101a2021-10-08 06:56:03 +00001662 extern "C" {
1663 #[link_name = "_Z3Addii"]
Googlera675ae02021-12-07 08:04:59 +00001664 pub(crate) fn __rust_thunk___Z3Addii(a: i32, b: i32) -> i32;
Marcel Hlopko89547752021-12-10 09:39:41 +00001665 }
1666 }
1667 }
Marcel Hlopko42abfc82021-08-09 07:03:17 +00001668 );
Michael Forsterdb8101a2021-10-08 06:56:03 +00001669
Marcel Hlopko89547752021-12-10 09:39:41 +00001670 assert_cc_not_matches!(generate_rs_api_impl(&ir)?, quote! {__rust_thunk___Z3Addii});
Michael Forsterdb8101a2021-10-08 06:56:03 +00001671
Marcel Hlopko3164eee2021-08-24 20:09:22 +00001672 Ok(())
1673 }
1674
1675 #[test]
1676 fn test_inline_function() -> Result<()> {
Marcel Hlopko89547752021-12-10 09:39:41 +00001677 let ir = ir_from_cc("inline int Add(int a, int b);")?;
1678 let rs_api = generate_rs_api(&ir)?;
1679 assert_rs_matches!(
1680 rs_api,
1681 quote! {
Michael Forsterdb8101a2021-10-08 06:56:03 +00001682 #[inline(always)]
Marcel Hlopko89547752021-12-10 09:39:41 +00001683 pub fn Add(a: i32, b: i32) -> i32 {
Googlera675ae02021-12-07 08:04:59 +00001684 unsafe { crate::detail::__rust_thunk___Z3Addii(a, b) }
Marcel Hlopko89547752021-12-10 09:39:41 +00001685 }
1686 }
1687 );
1688 assert_rs_matches!(
1689 rs_api,
1690 quote! {
Michael Forsterdb8101a2021-10-08 06:56:03 +00001691 mod detail {
Googler55647142022-01-11 12:37:39 +00001692 #[allow(unused_imports)]
Devin Jeanpierred4dde0e2021-10-13 20:48:25 +00001693 use super::*;
Michael Forsterdb8101a2021-10-08 06:56:03 +00001694 extern "C" {
Googlera675ae02021-12-07 08:04:59 +00001695 pub(crate) fn __rust_thunk___Z3Addii(a: i32, b: i32) -> i32;
Marcel Hlopko89547752021-12-10 09:39:41 +00001696 }
1697 }
1698 }
Marcel Hlopko3164eee2021-08-24 20:09:22 +00001699 );
1700
Marcel Hlopko89547752021-12-10 09:39:41 +00001701 assert_cc_matches!(
1702 generate_rs_api_impl(&ir)?,
1703 quote! {
Googlera675ae02021-12-07 08:04:59 +00001704 extern "C" int __rust_thunk___Z3Addii(int a, int b) {
Marcel Hlopko89547752021-12-10 09:39:41 +00001705 return Add(a, b);
Marcel Hlopko3164eee2021-08-24 20:09:22 +00001706 }
Marcel Hlopko89547752021-12-10 09:39:41 +00001707 }
Marcel Hlopko3164eee2021-08-24 20:09:22 +00001708 );
Marcel Hlopko42abfc82021-08-09 07:03:17 +00001709 Ok(())
1710 }
Marcel Hlopkob4b28742021-09-15 12:45:20 +00001711
1712 #[test]
Marcel Hlopkoa0f38662021-12-03 08:45:26 +00001713 fn test_simple_function_with_types_from_other_target() -> Result<()> {
1714 let ir = ir_from_cc_dependency(
1715 "inline ReturnStruct DoSomething(ParamStruct param);",
1716 "struct ReturnStruct {}; struct ParamStruct {};",
1717 )?;
1718
Marcel Hlopko89547752021-12-10 09:39:41 +00001719 let rs_api = generate_rs_api(&ir)?;
1720 assert_rs_matches!(
1721 rs_api,
1722 quote! {
Marcel Hlopkoa0f38662021-12-03 08:45:26 +00001723 #[inline(always)]
1724 pub fn DoSomething(param: dependency::ParamStruct)
Marcel Hlopko89547752021-12-10 09:39:41 +00001725 -> dependency::ReturnStruct {
Googlera675ae02021-12-07 08:04:59 +00001726 unsafe { crate::detail::__rust_thunk___Z11DoSomething11ParamStruct(param) }
Marcel Hlopko89547752021-12-10 09:39:41 +00001727 }
1728 }
1729 );
1730 assert_rs_matches!(
1731 rs_api,
1732 quote! {
1733 mod detail {
Googler55647142022-01-11 12:37:39 +00001734 #[allow(unused_imports)]
Marcel Hlopko89547752021-12-10 09:39:41 +00001735 use super::*;
1736 extern "C" {
1737 pub(crate) fn __rust_thunk___Z11DoSomething11ParamStruct(param: dependency::ParamStruct)
1738 -> dependency::ReturnStruct;
1739 }
1740 }}
Marcel Hlopkoa0f38662021-12-03 08:45:26 +00001741 );
1742
Marcel Hlopko89547752021-12-10 09:39:41 +00001743 assert_cc_matches!(
1744 generate_rs_api_impl(&ir)?,
1745 quote! {
Googler972d3582022-01-11 10:17:22 +00001746 extern "C" class ReturnStruct __rust_thunk___Z11DoSomething11ParamStruct(class ParamStruct param) {
Marcel Hlopkoa0f38662021-12-03 08:45:26 +00001747 return DoSomething(param);
1748 }
Marcel Hlopko89547752021-12-10 09:39:41 +00001749 }
Marcel Hlopkoa0f38662021-12-03 08:45:26 +00001750 );
1751 Ok(())
1752 }
1753
1754 #[test]
Marcel Hlopkob4b28742021-09-15 12:45:20 +00001755 fn test_simple_struct() -> Result<()> {
Marcel Hlopko89547752021-12-10 09:39:41 +00001756 let ir = ir_from_cc(&tokens_to_string(quote! {
Devin Jeanpierre88343c72022-01-15 01:10:23 +00001757 struct SomeStruct final {
Marcel Hlopko89547752021-12-10 09:39:41 +00001758 int public_int;
1759 protected:
1760 int protected_int;
1761 private:
1762 int private_int;
1763 };
1764 })?)?;
Michael Forster028800b2021-10-05 12:39:59 +00001765
Marcel Hlopko89547752021-12-10 09:39:41 +00001766 let rs_api = generate_rs_api(&ir)?;
1767 assert_rs_matches!(
1768 rs_api,
1769 quote! {
Michael Forsterdb8101a2021-10-08 06:56:03 +00001770 #[derive(Clone, Copy)]
1771 #[repr(C)]
1772 pub struct SomeStruct {
1773 pub public_int: i32,
1774 protected_int: i32,
1775 private_int: i32,
Marcel Hlopko89547752021-12-10 09:39:41 +00001776 }
1777 }
1778 );
1779 assert_rs_matches!(
1780 rs_api,
1781 quote! {
Googler454f2652021-12-06 12:53:12 +00001782 const _: () = assert!(std::mem::size_of::<Option<&i32>>() == std::mem::size_of::<&i32>());
Googler209b10a2021-12-06 09:11:57 +00001783 const _: () = assert!(std::mem::size_of::<SomeStruct>() == 12usize);
1784 const _: () = assert!(std::mem::align_of::<SomeStruct>() == 4usize);
1785 const _: () = assert!(offset_of!(SomeStruct, public_int) * 8 == 0usize);
1786 const _: () = assert!(offset_of!(SomeStruct, protected_int) * 8 == 32usize);
1787 const _: () = assert!(offset_of!(SomeStruct, private_int) * 8 == 64usize);
Marcel Hlopko89547752021-12-10 09:39:41 +00001788 }
Marcel Hlopkob4b28742021-09-15 12:45:20 +00001789 );
Marcel Hlopko89547752021-12-10 09:39:41 +00001790 let rs_api_impl = generate_rs_api_impl(&ir)?;
1791 assert_cc_matches!(
1792 rs_api_impl,
1793 quote! {
Googler972d3582022-01-11 10:17:22 +00001794 extern "C" void __rust_thunk___ZN10SomeStructD1Ev(class SomeStruct * __this) {
Lukasz Anforowicze643ec92021-12-22 15:45:15 +00001795 std :: destroy_at (__this) ;
Marcel Hlopko89547752021-12-10 09:39:41 +00001796 }
1797 }
1798 );
1799 assert_cc_matches!(
1800 rs_api_impl,
1801 quote! {
Googler972d3582022-01-11 10:17:22 +00001802 static_assert(sizeof(class SomeStruct) == 12);
1803 static_assert(alignof(class SomeStruct) == 4);
1804 static_assert(offsetof(class SomeStruct, public_int) * 8 == 0);
Marcel Hlopko89547752021-12-10 09:39:41 +00001805 }
Googler5ea88642021-09-29 08:05:59 +00001806 );
Marcel Hlopkob4b28742021-09-15 12:45:20 +00001807 Ok(())
1808 }
Devin Jeanpierre7a7328e2021-09-17 07:10:08 +00001809
Devin Jeanpierre2ed14ec2021-10-06 11:32:19 +00001810 #[test]
Lukasz Anforowicz275fa922022-01-05 16:13:20 +00001811 fn test_ref_to_struct_in_thunk_impls() -> Result<()> {
Googler972d3582022-01-11 10:17:22 +00001812 let ir = ir_from_cc("struct S{}; inline void foo(class S& s) {} ")?;
Lukasz Anforowicz275fa922022-01-05 16:13:20 +00001813 let rs_api_impl = generate_rs_api_impl(&ir)?;
1814 assert_cc_matches!(
1815 rs_api_impl,
1816 quote! {
Googler972d3582022-01-11 10:17:22 +00001817 extern "C" void __rust_thunk___Z3fooR1S(class S& s) {
Lukasz Anforowicz275fa922022-01-05 16:13:20 +00001818 foo(s);
1819 }
1820 }
1821 );
1822 Ok(())
1823 }
1824
1825 #[test]
1826 fn test_const_ref_to_struct_in_thunk_impls() -> Result<()> {
Googler972d3582022-01-11 10:17:22 +00001827 let ir = ir_from_cc("struct S{}; inline void foo(const class S& s) {} ")?;
Lukasz Anforowicz275fa922022-01-05 16:13:20 +00001828 let rs_api_impl = generate_rs_api_impl(&ir)?;
1829 assert_cc_matches!(
1830 rs_api_impl,
1831 quote! {
Googler972d3582022-01-11 10:17:22 +00001832 extern "C" void __rust_thunk___Z3fooRK1S(const class S& s) {
Lukasz Anforowicz275fa922022-01-05 16:13:20 +00001833 foo(s);
1834 }
1835 }
1836 );
1837 Ok(())
1838 }
1839
1840 #[test]
Lukasz Anforowicz957cbf22022-01-05 16:14:05 +00001841 fn test_unsigned_int_in_thunk_impls() -> Result<()> {
1842 let ir = ir_from_cc("inline void foo(unsigned int i) {} ")?;
1843 let rs_api_impl = generate_rs_api_impl(&ir)?;
1844 assert_cc_matches!(
1845 rs_api_impl,
1846 quote! {
1847 extern "C" void __rust_thunk___Z3fooj(unsigned int i) {
1848 foo(i);
1849 }
1850 }
1851 );
1852 Ok(())
1853 }
1854
1855 #[test]
Marcel Hlopkodd1fcb12021-12-22 14:13:59 +00001856 fn test_record_static_methods_qualify_call_in_thunk() -> Result<()> {
1857 let ir = ir_from_cc(&tokens_to_string(quote! {
1858 struct SomeStruct {
1859 static inline int some_func() { return 42; }
1860 };
1861 })?)?;
1862
1863 assert_cc_matches!(
1864 generate_rs_api_impl(&ir)?,
1865 quote! {
1866 extern "C" int __rust_thunk___ZN10SomeStruct9some_funcEv() {
1867 return SomeStruct::some_func();
1868 }
1869 }
1870 );
1871 Ok(())
1872 }
1873
1874 #[test]
Lukasz Anforowiczb3d89aa2022-01-12 14:35:52 +00001875 fn test_record_instance_methods_deref_this_in_thunk() -> Result<()> {
1876 let ir = ir_from_cc(&tokens_to_string(quote! {
1877 struct SomeStruct {
1878 inline int some_func(int arg) const { return 42 + arg; }
1879 };
1880 })?)?;
1881
1882 assert_cc_matches!(
1883 generate_rs_api_impl(&ir)?,
1884 quote! {
1885 extern "C" int __rust_thunk___ZNK10SomeStruct9some_funcEi(
1886 const class SomeStruct* __this, int arg) {
1887 return __this->some_func(arg);
1888 }
1889 }
1890 );
1891 Ok(())
1892 }
1893
1894 #[test]
Marcel Hlopkoa0f38662021-12-03 08:45:26 +00001895 fn test_struct_from_other_target() -> Result<()> {
1896 let ir = ir_from_cc_dependency("// intentionally empty", "struct SomeStruct {};")?;
Marcel Hlopko89547752021-12-10 09:39:41 +00001897 assert_rs_not_matches!(generate_rs_api(&ir)?, quote! { SomeStruct });
1898 assert_cc_not_matches!(generate_rs_api_impl(&ir)?, quote! { SomeStruct });
Marcel Hlopkoa0f38662021-12-03 08:45:26 +00001899 Ok(())
1900 }
1901
1902 #[test]
Devin Jeanpierre2ed14ec2021-10-06 11:32:19 +00001903 fn test_copy_derives() {
Devin Jeanpierreccfefc82021-10-27 10:54:00 +00001904 let record = ir_record("S");
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00001905 assert_eq!(generate_derives(&record), &["Clone", "Copy"]);
Devin Jeanpierre2ed14ec2021-10-06 11:32:19 +00001906 }
1907
1908 #[test]
1909 fn test_copy_derives_not_is_trivial_abi() {
Devin Jeanpierreccfefc82021-10-27 10:54:00 +00001910 let mut record = ir_record("S");
Devin Jeanpierre2ed14ec2021-10-06 11:32:19 +00001911 record.is_trivial_abi = false;
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00001912 assert_eq!(generate_derives(&record), &[""; 0]);
Devin Jeanpierre2ed14ec2021-10-06 11:32:19 +00001913 }
1914
Devin Jeanpierre88343c72022-01-15 01:10:23 +00001915 /// Even if it's trivially relocatable, !Unpin C++ type cannot be
1916 /// cloned/copied or otherwise used by value, because values would allow
1917 /// assignment into the Pin.
1918 ///
1919 /// All !Unpin C++ types, not just non trivially relocatable ones, are
1920 /// unsafe to assign in the Rust sense.
Devin Jeanpierree6e16652021-12-22 15:54:46 +00001921 #[test]
Devin Jeanpierre88343c72022-01-15 01:10:23 +00001922 fn test_copy_derives_not_final() {
Devin Jeanpierree6e16652021-12-22 15:54:46 +00001923 let mut record = ir_record("S");
1924 record.is_final = false;
Devin Jeanpierre88343c72022-01-15 01:10:23 +00001925 assert_eq!(generate_derives(&record), &[""; 0]);
Devin Jeanpierree6e16652021-12-22 15:54:46 +00001926 }
1927
Devin Jeanpierre2ed14ec2021-10-06 11:32:19 +00001928 #[test]
1929 fn test_copy_derives_ctor_nonpublic() {
Devin Jeanpierreccfefc82021-10-27 10:54:00 +00001930 let mut record = ir_record("S");
Devin Jeanpierre2ed14ec2021-10-06 11:32:19 +00001931 for access in [ir::AccessSpecifier::Protected, ir::AccessSpecifier::Private] {
1932 record.copy_constructor.access = access;
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00001933 assert_eq!(generate_derives(&record), &[""; 0]);
Devin Jeanpierre2ed14ec2021-10-06 11:32:19 +00001934 }
1935 }
1936
1937 #[test]
1938 fn test_copy_derives_ctor_deleted() {
Devin Jeanpierreccfefc82021-10-27 10:54:00 +00001939 let mut record = ir_record("S");
Devin Jeanpierre2ed14ec2021-10-06 11:32:19 +00001940 record.copy_constructor.definition = ir::SpecialMemberDefinition::Deleted;
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00001941 assert_eq!(generate_derives(&record), &[""; 0]);
Devin Jeanpierre2ed14ec2021-10-06 11:32:19 +00001942 }
1943
1944 #[test]
Devin Jeanpierrebe2f33b2021-10-21 12:54:19 +00001945 fn test_copy_derives_ctor_nontrivial_members() {
Devin Jeanpierreccfefc82021-10-27 10:54:00 +00001946 let mut record = ir_record("S");
Devin Jeanpierrebe2f33b2021-10-21 12:54:19 +00001947 record.copy_constructor.definition = ir::SpecialMemberDefinition::NontrivialMembers;
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00001948 assert_eq!(generate_derives(&record), &[""; 0]);
Devin Jeanpierrebe2f33b2021-10-21 12:54:19 +00001949 }
1950
1951 #[test]
1952 fn test_copy_derives_ctor_nontrivial_self() {
Devin Jeanpierreccfefc82021-10-27 10:54:00 +00001953 let mut record = ir_record("S");
Devin Jeanpierre7b62e952021-12-08 21:43:30 +00001954 record.copy_constructor.definition = ir::SpecialMemberDefinition::NontrivialUserDefined;
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00001955 assert_eq!(generate_derives(&record), &[""; 0]);
Devin Jeanpierre2ed14ec2021-10-06 11:32:19 +00001956 }
1957
Devin Jeanpierre7a7328e2021-09-17 07:10:08 +00001958 #[test]
1959 fn test_ptr_func() -> Result<()> {
Marcel Hlopko89547752021-12-10 09:39:41 +00001960 let ir = ir_from_cc(&tokens_to_string(quote! {
1961 inline int* Deref(int*const* p);
1962 })?)?;
Devin Jeanpierred6da7002021-10-21 12:55:20 +00001963
Marcel Hlopko89547752021-12-10 09:39:41 +00001964 let rs_api = generate_rs_api(&ir)?;
1965 assert_rs_matches!(
1966 rs_api,
1967 quote! {
Michael Forsterdb8101a2021-10-08 06:56:03 +00001968 #[inline(always)]
Lukasz Anforowiczf7bdd392022-01-21 00:33:39 +00001969 pub unsafe fn Deref(p: *const *mut i32) -> *mut i32 {
1970 crate::detail::__rust_thunk___Z5DerefPKPi(p)
Marcel Hlopko89547752021-12-10 09:39:41 +00001971 }
1972 }
1973 );
1974 assert_rs_matches!(
1975 rs_api,
1976 quote! {
Michael Forsterdb8101a2021-10-08 06:56:03 +00001977 mod detail {
Googler55647142022-01-11 12:37:39 +00001978 #[allow(unused_imports)]
Devin Jeanpierred4dde0e2021-10-13 20:48:25 +00001979 use super::*;
Michael Forsterdb8101a2021-10-08 06:56:03 +00001980 extern "C" {
Googlera675ae02021-12-07 08:04:59 +00001981 pub(crate) fn __rust_thunk___Z5DerefPKPi(p: *const *mut i32) -> *mut i32;
Marcel Hlopko89547752021-12-10 09:39:41 +00001982 }
1983 }
1984 }
Devin Jeanpierre7a7328e2021-09-17 07:10:08 +00001985 );
Devin Jeanpierre184f9ac2021-09-17 13:47:03 +00001986
Marcel Hlopko89547752021-12-10 09:39:41 +00001987 assert_cc_matches!(
1988 generate_rs_api_impl(&ir)?,
1989 quote! {
Googlera675ae02021-12-07 08:04:59 +00001990 extern "C" int* __rust_thunk___Z5DerefPKPi(int* const * p) {
Devin Jeanpierre184f9ac2021-09-17 13:47:03 +00001991 return Deref(p);
1992 }
Marcel Hlopko89547752021-12-10 09:39:41 +00001993 }
Devin Jeanpierre184f9ac2021-09-17 13:47:03 +00001994 );
Devin Jeanpierre7a7328e2021-09-17 07:10:08 +00001995 Ok(())
1996 }
Michael Forstered642022021-10-04 09:48:25 +00001997
1998 #[test]
Googlerdb111532022-01-05 06:12:13 +00001999 fn test_const_char_ptr_func() -> Result<()> {
2000 // This is a regression test: We used to include the "const" in the name
2001 // of the CcType, which caused a panic in the code generator
2002 // ('"const char" is not a valid Ident').
2003 // It's therefore important that f() is inline so that we need to
2004 // generate a thunk for it (where we then process the CcType).
2005 let ir = ir_from_cc(&tokens_to_string(quote! {
2006 inline void f(const char *str);
2007 })?)?;
2008
2009 let rs_api = generate_rs_api(&ir)?;
2010 assert_rs_matches!(
2011 rs_api,
2012 quote! {
2013 #[inline(always)]
Lukasz Anforowiczf7bdd392022-01-21 00:33:39 +00002014 pub unsafe fn f(str: *const i8) {
2015 crate::detail::__rust_thunk___Z1fPKc(str)
Googlerdb111532022-01-05 06:12:13 +00002016 }
2017 }
2018 );
2019 assert_rs_matches!(
2020 rs_api,
2021 quote! {
2022 extern "C" {
2023 pub(crate) fn __rust_thunk___Z1fPKc(str: *const i8);
2024 }
2025 }
2026 );
2027
2028 assert_cc_matches!(
2029 generate_rs_api_impl(&ir)?,
2030 quote! {
2031 extern "C" void __rust_thunk___Z1fPKc(char const * str){ f(str) ; }
2032 }
2033 );
2034 Ok(())
2035 }
2036
2037 #[test]
Michael Forstered642022021-10-04 09:48:25 +00002038 fn test_item_order() -> Result<()> {
Marcel Hlopko89547752021-12-10 09:39:41 +00002039 let ir = ir_from_cc(
2040 "int first_func();
2041 struct FirstStruct {};
2042 int second_func();
2043 struct SecondStruct {};",
2044 )?;
Michael Forstered642022021-10-04 09:48:25 +00002045
Marcel Hlopko89547752021-12-10 09:39:41 +00002046 let rs_api = rs_tokens_to_formatted_string(generate_rs_api(&ir)?)?;
2047
Lukasz Anforowiczdd9ae0f2022-02-17 15:52:53 +00002048 let idx = |s: &str| rs_api.find(s).ok_or_else(|| anyhow!("'{}' missing", s));
Michael Forstered642022021-10-04 09:48:25 +00002049
2050 let f1 = idx("fn first_func")?;
2051 let f2 = idx("fn second_func")?;
2052 let s1 = idx("struct FirstStruct")?;
2053 let s2 = idx("struct SecondStruct")?;
Googlera675ae02021-12-07 08:04:59 +00002054 let t1 = idx("fn __rust_thunk___Z10first_funcv")?;
2055 let t2 = idx("fn __rust_thunk___Z11second_funcv")?;
Michael Forstered642022021-10-04 09:48:25 +00002056
2057 assert!(f1 < s1);
2058 assert!(s1 < f2);
2059 assert!(f2 < s2);
2060 assert!(s2 < t1);
2061 assert!(t1 < t2);
2062
2063 Ok(())
2064 }
Michael Forster028800b2021-10-05 12:39:59 +00002065
2066 #[test]
Devin Jeanpierrec80e6242022-02-03 01:56:40 +00002067 fn test_base_class_subobject_layout() -> Result<()> {
2068 let ir = ir_from_cc(
2069 r#"
2070 // We use a class here to force `Derived::z` to live inside the tail padding of `Base`.
2071 // On the Itanium ABI, this would not happen if `Base` were a POD type.
Devin Jeanpierre56777022022-02-03 01:57:15 +00002072 class Base {__INT64_TYPE__ x; char y;};
2073 struct Derived final : Base {__INT16_TYPE__ z;};
Devin Jeanpierrec80e6242022-02-03 01:56:40 +00002074 "#,
2075 )?;
2076 let rs_api = generate_rs_api(&ir)?;
2077 assert_rs_matches!(
2078 rs_api,
2079 quote! {
2080 #[repr(C, align(8))]
2081 pub struct Derived {
2082 __base_class_subobjects: [std::mem::MaybeUninit<u8>; 10],
2083 pub z: i16,
2084 }
2085 }
2086 );
2087 Ok(())
2088 }
2089
2090 /// The same as test_base_class_subobject_layout, but with multiple
2091 /// inheritance.
2092 #[test]
2093 fn test_base_class_multiple_inheritance_subobject_layout() -> Result<()> {
2094 let ir = ir_from_cc(
2095 r#"
Devin Jeanpierre56777022022-02-03 01:57:15 +00002096 class Base1 {__INT64_TYPE__ x;};
Devin Jeanpierrec80e6242022-02-03 01:56:40 +00002097 class Base2 {char y;};
Devin Jeanpierre56777022022-02-03 01:57:15 +00002098 struct Derived final : Base1, Base2 {__INT16_TYPE__ z;};
Devin Jeanpierrec80e6242022-02-03 01:56:40 +00002099 "#,
2100 )?;
2101 let rs_api = generate_rs_api(&ir)?;
2102 assert_rs_matches!(
2103 rs_api,
2104 quote! {
2105 #[repr(C, align(8))]
2106 pub struct Derived {
2107 __base_class_subobjects: [std::mem::MaybeUninit<u8>; 10],
2108 pub z: i16,
2109 }
2110 }
2111 );
2112 Ok(())
2113 }
2114
2115 /// The same as test_base_class_subobject_layout, but with a chain of
2116 /// inheritance.
2117 #[test]
2118 fn test_base_class_deep_inheritance_subobject_layout() -> Result<()> {
2119 let ir = ir_from_cc(
2120 r#"
Devin Jeanpierre56777022022-02-03 01:57:15 +00002121 class Base1 {__INT64_TYPE__ x;};
Devin Jeanpierrec80e6242022-02-03 01:56:40 +00002122 class Base2 : Base1 {char y;};
Devin Jeanpierre56777022022-02-03 01:57:15 +00002123 struct Derived final : Base2 {__INT16_TYPE__ z;};
Devin Jeanpierrec80e6242022-02-03 01:56:40 +00002124 "#,
2125 )?;
2126 let rs_api = generate_rs_api(&ir)?;
2127 assert_rs_matches!(
2128 rs_api,
2129 quote! {
2130 #[repr(C, align(8))]
2131 pub struct Derived {
2132 __base_class_subobjects: [std::mem::MaybeUninit<u8>; 10],
2133 pub z: i16,
2134 }
2135 }
2136 );
2137 Ok(())
2138 }
2139
2140 /// For derived classes with no data members, we can't use the offset of the
2141 /// first member to determine the size of the base class subobjects.
2142 #[test]
2143 fn test_base_class_subobject_fieldless_layout() -> Result<()> {
2144 let ir = ir_from_cc(
2145 r#"
Devin Jeanpierre56777022022-02-03 01:57:15 +00002146 class Base {__INT64_TYPE__ x; char y;};
Devin Jeanpierrec80e6242022-02-03 01:56:40 +00002147 struct Derived final : Base {};
2148 "#,
2149 )?;
2150 let rs_api = generate_rs_api(&ir)?;
2151 assert_rs_matches!(
2152 rs_api,
2153 quote! {
2154 #[repr(C, align(8))]
2155 pub struct Derived {
2156 __base_class_subobjects: [std::mem::MaybeUninit<u8>; 9],
2157 }
2158 }
2159 );
2160 Ok(())
2161 }
2162
2163 #[test]
2164 fn test_base_class_subobject_empty_fieldless() -> Result<()> {
2165 let ir = ir_from_cc(
2166 r#"
2167 class Base {};
2168 struct Derived final : Base {};
2169 "#,
2170 )?;
2171 let rs_api = generate_rs_api(&ir)?;
2172 assert_rs_matches!(
2173 rs_api,
2174 quote! {
2175 #[repr(C)]
2176 pub struct Derived {
2177 __base_class_subobjects: [std::mem::MaybeUninit<u8>; 0],
2178 /// Prevent empty C++ struct being zero-size in Rust.
2179 placeholder: std::mem::MaybeUninit<u8>,
2180 }
2181 }
2182 );
2183 Ok(())
2184 }
2185
2186 #[test]
2187 fn test_base_class_subobject_empty() -> Result<()> {
2188 let ir = ir_from_cc(
2189 r#"
2190 class Base {};
2191 struct Derived final : Base {};
2192 "#,
2193 )?;
2194 let rs_api = generate_rs_api(&ir)?;
2195 assert_rs_matches!(
2196 rs_api,
2197 quote! {
2198 #[repr(C)]
2199 pub struct Derived {
2200 __base_class_subobjects: [std::mem::MaybeUninit<u8>; 0],
2201 /// Prevent empty C++ struct being zero-size in Rust.
2202 placeholder: std::mem::MaybeUninit<u8>,
2203 }
2204 }
2205 );
2206 Ok(())
2207 }
2208
Devin Jeanpierreb69bcae2022-02-03 09:45:50 +00002209 /// When a field is [[no_unique_address]], it occupies the space up to the
2210 /// next field.
2211 #[test]
2212 fn test_no_unique_address() -> Result<()> {
2213 let ir = ir_from_cc(
2214 r#"
2215 class Field1 {__INT64_TYPE__ x;};
2216 class Field2 {char y;};
2217 struct Struct final {
2218 [[no_unique_address]] Field1 field1;
2219 [[no_unique_address]] Field2 field2;
2220 __INT16_TYPE__ z;
2221 };
2222 "#,
2223 )?;
2224 let rs_api = generate_rs_api(&ir)?;
2225 assert_rs_matches!(
2226 rs_api,
2227 quote! {
2228 #[derive(Clone, Copy)]
2229 #[repr(C, align(8))]
2230 pub struct Struct {
2231 field1: [std::mem::MaybeUninit<u8>; 8],
2232 field2: [std::mem::MaybeUninit<u8>; 2],
2233 pub z: i16,
2234 }
Devin Jeanpierre58181ac2022-02-14 21:30:05 +00002235
2236 impl Struct {
2237 pub fn field1(&self) -> &Field1 {
2238 unsafe {&* (&self.field1 as *const _ as *const Field1)}
2239 }
2240 pub fn field2(&self) -> &Field2 {
2241 unsafe {&* (&self.field2 as *const _ as *const Field2)}
2242 }
2243 }
Devin Jeanpierreb69bcae2022-02-03 09:45:50 +00002244 }
2245 );
2246 Ok(())
2247 }
2248
2249 /// When a [[no_unique_address]] field is the last one, it occupies the rest
2250 /// of the object.
2251 #[test]
2252 fn test_no_unique_address_last_field() -> Result<()> {
2253 let ir = ir_from_cc(
2254 r#"
2255 class Field1 {__INT64_TYPE__ x;};
2256 class Field2 {char y;};
2257 struct Struct final {
2258 [[no_unique_address]] Field1 field1;
2259 [[no_unique_address]] Field2 field2;
2260 };
2261 "#,
2262 )?;
2263 let rs_api = generate_rs_api(&ir)?;
2264 assert_rs_matches!(
2265 rs_api,
2266 quote! {
2267 #[derive(Clone, Copy)]
2268 #[repr(C, align(8))]
2269 pub struct Struct {
2270 field1: [std::mem::MaybeUninit<u8>; 8],
2271 field2: [std::mem::MaybeUninit<u8>; 8],
2272 }
2273 }
2274 );
2275 Ok(())
2276 }
2277
2278 #[test]
2279 fn test_no_unique_address_empty() -> Result<()> {
2280 let ir = ir_from_cc(
2281 r#"
2282 class Field {};
2283 struct Struct final {
2284 [[no_unique_address]] Field field;
2285 int x;
2286 };
2287 "#,
2288 )?;
2289 let rs_api = generate_rs_api(&ir)?;
2290 assert_rs_matches!(
2291 rs_api,
2292 quote! {
2293 #[repr(C, align(4))]
2294 pub struct Struct {
2295 field: [std::mem::MaybeUninit<u8>; 0],
2296 pub x: i32,
2297 }
2298 }
2299 );
2300 Ok(())
2301 }
2302
2303 #[test]
2304 fn test_base_class_subobject_empty_last_field() -> Result<()> {
2305 let ir = ir_from_cc(
2306 r#"
2307 class Field {};
2308 struct Struct final {
2309 [[no_unique_address]] Field field;
2310 };
2311 "#,
2312 )?;
2313 let rs_api = generate_rs_api(&ir)?;
2314 assert_rs_matches!(
2315 rs_api,
2316 quote! {
2317 #[repr(C)]
2318 pub struct Struct {
2319 field: [std::mem::MaybeUninit<u8>; 1],
2320 }
2321 }
2322 );
2323 Ok(())
2324 }
2325
Devin Jeanpierrec80e6242022-02-03 01:56:40 +00002326 #[test]
Michael Forster409d9412021-10-07 08:35:29 +00002327 fn test_doc_comment_func() -> Result<()> {
Marcel Hlopko89547752021-12-10 09:39:41 +00002328 let ir = ir_from_cc(
2329 "
2330 // Doc Comment
2331 // with two lines
2332 int func();",
2333 )?;
Michael Forster409d9412021-10-07 08:35:29 +00002334
Marcel Hlopko89547752021-12-10 09:39:41 +00002335 assert_rs_matches!(
2336 generate_rs_api(&ir)?,
2337 // leading space is intentional so there is a space between /// and the text of the
2338 // comment
2339 quote! {
2340 #[doc = " Doc Comment\n with two lines"]
2341 #[inline(always)]
2342 pub fn func
2343 }
Michael Forster409d9412021-10-07 08:35:29 +00002344 );
2345
2346 Ok(())
2347 }
2348
2349 #[test]
2350 fn test_doc_comment_record() -> Result<()> {
Marcel Hlopko89547752021-12-10 09:39:41 +00002351 let ir = ir_from_cc(
2352 "// Doc Comment\n\
2353 //\n\
2354 // * with bullet\n\
Devin Jeanpierre88343c72022-01-15 01:10:23 +00002355 struct SomeStruct final {\n\
Marcel Hlopko89547752021-12-10 09:39:41 +00002356 // Field doc\n\
2357 int field;\
2358 };",
2359 )?;
Michael Forster028800b2021-10-05 12:39:59 +00002360
Marcel Hlopko89547752021-12-10 09:39:41 +00002361 assert_rs_matches!(
2362 generate_rs_api(&ir)?,
2363 quote! {
2364 #[doc = " Doc Comment\n \n * with bullet"]
2365 #[derive(Clone, Copy)]
2366 #[repr(C)]
2367 pub struct SomeStruct {
2368 # [doc = " Field doc"]
2369 pub field: i32,
2370 }
2371 }
Michael Forstercc5941a2021-10-07 07:12:24 +00002372 );
Michael Forster028800b2021-10-05 12:39:59 +00002373 Ok(())
2374 }
Devin Jeanpierre91de7012021-10-21 12:53:51 +00002375
Devin Jeanpierre96839c12021-12-14 00:27:38 +00002376 #[test]
Devin Jeanpierre56777022022-02-03 01:57:15 +00002377 fn test_unambiguous_public_bases() -> Result<()> {
2378 let ir = ir_from_cc_dependency(
2379 "
2380 struct VirtualBase {};
2381 struct PrivateBase {};
2382 struct ProtectedBase {};
2383 struct UnambiguousPublicBase {};
2384 struct AmbiguousPublicBase {};
2385 struct MultipleInheritance : UnambiguousPublicBase, AmbiguousPublicBase {};
2386 struct Derived : private PrivateBase, protected ProtectedBase, MultipleInheritance, AmbiguousPublicBase, virtual VirtualBase {};
2387 ",
2388 "",
2389 )?;
2390 let rs_api = generate_rs_api(&ir)?;
2391 // TODO(b/216195042): virtual bases.
2392 assert_rs_not_matches!(rs_api, quote! { From<&'a Derived> for &'a VirtualBase });
2393 assert_rs_matches!(rs_api, quote! { From<&'a Derived> for &'a UnambiguousPublicBase });
2394 assert_rs_matches!(rs_api, quote! { From<&'a Derived> for &'a MultipleInheritance });
2395 assert_rs_not_matches!(rs_api, quote! {From<&'a Derived> for &'a PrivateBase});
2396 assert_rs_not_matches!(rs_api, quote! {From<&'a Derived> for &'a ProtectedBase});
2397 assert_rs_not_matches!(rs_api, quote! {From<&'a Derived> for &'a AmbiguousPublicBase});
2398 Ok(())
2399 }
2400
2401 /// Contrary to intuitions: a base class conversion is ambiguous even if the
2402 /// ambiguity is from a private base class cast that you can't even
2403 /// perform.
2404 ///
2405 /// Explanation (courtesy James Dennett):
2406 ///
2407 /// > Once upon a time, there was a rule in C++ that changing all access
2408 /// > specifiers to "public" would not change the meaning of code.
2409 /// > That's no longer true, but some of its effects can still be seen.
2410 ///
2411 /// So, we need to be sure to not allow casting to privately-ambiguous
2412 /// bases.
2413 #[test]
2414 fn test_unambiguous_public_bases_private_ambiguity() -> Result<()> {
2415 let ir = ir_from_cc_dependency(
2416 "
2417 struct Base {};
2418 struct Intermediate : public Base {};
2419 struct Derived : Base, private Intermediate {};
2420 ",
2421 "",
2422 )?;
2423 let rs_api = generate_rs_api(&ir)?;
2424 assert_rs_not_matches!(rs_api, quote! { From<&'a Derived> for &'a Base });
2425 Ok(())
2426 }
2427
2428 #[test]
Devin Jeanpierre96839c12021-12-14 00:27:38 +00002429 fn test_virtual_thunk() -> Result<()> {
2430 let ir = ir_from_cc("struct Polymorphic { virtual void Foo(); };")?;
2431
2432 assert_cc_matches!(
2433 generate_rs_api_impl(&ir)?,
2434 quote! {
Googler972d3582022-01-11 10:17:22 +00002435 extern "C" void __rust_thunk___ZN11Polymorphic3FooEv(class Polymorphic * __this)
Devin Jeanpierre96839c12021-12-14 00:27:38 +00002436 }
2437 );
2438 Ok(())
2439 }
2440
Devin Jeanpierree6e16652021-12-22 15:54:46 +00002441 /// A trivially relocatable final struct is safe to use in Rust as normal,
2442 /// and is Unpin.
2443 #[test]
2444 fn test_no_negative_impl_unpin() -> Result<()> {
2445 let ir = ir_from_cc("struct Trivial final {};")?;
2446 let rs_api = generate_rs_api(&ir)?;
2447 assert_rs_not_matches!(rs_api, quote! {impl !Unpin});
2448 Ok(())
2449 }
2450
2451 /// A non-final struct, even if it's trivial, is not usable by mut
2452 /// reference, and so is !Unpin.
2453 #[test]
2454 fn test_negative_impl_unpin_nonfinal() -> Result<()> {
2455 let ir = ir_from_cc("struct Nonfinal {};")?;
2456 let rs_api = generate_rs_api(&ir)?;
2457 assert_rs_matches!(rs_api, quote! {impl !Unpin for Nonfinal {}});
2458 Ok(())
2459 }
2460
Devin Jeanpierre91de7012021-10-21 12:53:51 +00002461 /// At the least, a trivial type should have no drop impl if or until we add
2462 /// empty drop impls.
2463 #[test]
2464 fn test_no_impl_drop() -> Result<()> {
Googler7cced422021-12-06 11:58:39 +00002465 let ir = ir_from_cc("struct Trivial {};")?;
Marcel Hlopko89547752021-12-10 09:39:41 +00002466 let rs_api = rs_tokens_to_formatted_string(generate_rs_api(&ir)?)?;
Devin Jeanpierre91de7012021-10-21 12:53:51 +00002467 assert!(!rs_api.contains("impl Drop"));
2468 Ok(())
2469 }
2470
Devin Jeanpierre7e9a1de2021-12-03 08:04:22 +00002471 /// User-defined destructors *must* become Drop impls with ManuallyDrop
2472 /// fields
Devin Jeanpierre91de7012021-10-21 12:53:51 +00002473 #[test]
Devin Jeanpierre7e9a1de2021-12-03 08:04:22 +00002474 fn test_impl_drop_user_defined_destructor() -> Result<()> {
Googler7cced422021-12-06 11:58:39 +00002475 let ir = ir_from_cc(
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00002476 r#" struct NontrivialStruct { ~NontrivialStruct(); };
2477 struct UserDefinedDestructor {
Devin Jeanpierre91de7012021-10-21 12:53:51 +00002478 ~UserDefinedDestructor();
Devin Jeanpierre7e9a1de2021-12-03 08:04:22 +00002479 int x;
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00002480 NontrivialStruct nts;
Devin Jeanpierre91de7012021-10-21 12:53:51 +00002481 };"#,
2482 )?;
2483 let rs_api = generate_rs_api(&ir)?;
Lukasz Anforowicz6d553632022-01-06 21:36:14 +00002484 assert_rs_matches!(
2485 rs_api,
2486 quote! {
2487 impl Drop for UserDefinedDestructor {
2488 #[inline(always)]
2489 fn drop(&mut self) {
2490 unsafe { crate::detail::__rust_thunk___ZN21UserDefinedDestructorD1Ev(self) }
2491 }
2492 }
2493 }
2494 );
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00002495 assert_rs_matches!(rs_api, quote! {pub x: i32,});
2496 assert_rs_matches!(rs_api, quote! {pub nts: std::mem::ManuallyDrop<NontrivialStruct>,});
Devin Jeanpierre7e9a1de2021-12-03 08:04:22 +00002497 Ok(())
2498 }
2499
Lukasz Anforowicz6d553632022-01-06 21:36:14 +00002500 /// nontrivial types without user-defined destructors should invoke
2501 /// the C++ destructor to preserve the order of field destructions.
Devin Jeanpierre7e9a1de2021-12-03 08:04:22 +00002502 #[test]
2503 fn test_impl_drop_nontrivial_member_destructor() -> Result<()> {
2504 // TODO(jeanpierreda): This would be cleaner if the UserDefinedDestructor code were
2505 // omitted. For example, we simulate it so that UserDefinedDestructor
2506 // comes from another library.
Googler7cced422021-12-06 11:58:39 +00002507 let ir = ir_from_cc(
Devin Jeanpierre88343c72022-01-15 01:10:23 +00002508 r#"struct UserDefinedDestructor final {
Devin Jeanpierre7e9a1de2021-12-03 08:04:22 +00002509 ~UserDefinedDestructor();
2510 };
Devin Jeanpierre88343c72022-01-15 01:10:23 +00002511 struct TrivialStruct final { int i; };
2512 struct NontrivialMembers final {
Devin Jeanpierre7e9a1de2021-12-03 08:04:22 +00002513 UserDefinedDestructor udd;
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00002514 TrivialStruct ts;
Devin Jeanpierre7e9a1de2021-12-03 08:04:22 +00002515 int x;
2516 };"#,
2517 )?;
2518 let rs_api = generate_rs_api(&ir)?;
Lukasz Anforowicz6d553632022-01-06 21:36:14 +00002519 assert_rs_matches!(
2520 rs_api,
2521 quote! {
2522 impl Drop for NontrivialMembers {
2523 #[inline(always)]
2524 fn drop(&mut self) {
2525 unsafe { crate::detail::__rust_thunk___ZN17NontrivialMembersD1Ev(self) }
2526 }
2527 }
2528 }
2529 );
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00002530 assert_rs_matches!(rs_api, quote! {pub x: i32,});
2531 assert_rs_matches!(rs_api, quote! {pub ts: TrivialStruct,});
Lukasz Anforowicz6d553632022-01-06 21:36:14 +00002532 assert_rs_matches!(
2533 rs_api,
2534 quote! {pub udd: std::mem::ManuallyDrop<UserDefinedDestructor>,}
2535 );
Devin Jeanpierre7e9a1de2021-12-03 08:04:22 +00002536 Ok(())
2537 }
2538
2539 /// Trivial types (at least those that are mapped to Copy rust types) do not
2540 /// get a Drop impl.
2541 #[test]
2542 fn test_impl_drop_trivial() -> Result<()> {
Googler7cced422021-12-06 11:58:39 +00002543 let ir = ir_from_cc(
Devin Jeanpierre88343c72022-01-15 01:10:23 +00002544 r#"struct Trivial final {
Devin Jeanpierre7e9a1de2021-12-03 08:04:22 +00002545 ~Trivial() = default;
2546 int x;
2547 };"#,
2548 )?;
2549 let rs_api = generate_rs_api(&ir)?;
Marcel Hlopko89547752021-12-10 09:39:41 +00002550 assert_rs_not_matches!(rs_api, quote! {impl Drop});
2551 assert_rs_matches!(rs_api, quote! {pub x: i32});
Lukasz Anforowicz2f074162022-01-06 22:50:51 +00002552 let rs_api_impl = generate_rs_api_impl(&ir)?;
2553 // TODO(b/213326125): Avoid generating thunk impls that are never called.
2554 // (The test assertion below should be reversed once this bug is fixed.)
2555 assert_cc_matches!(rs_api_impl, quote! { std::destroy_at });
Devin Jeanpierre91de7012021-10-21 12:53:51 +00002556 Ok(())
2557 }
Devin Jeanpierre45cb1162021-10-27 10:54:28 +00002558
2559 #[test]
Lukasz Anforowicze643ec92021-12-22 15:45:15 +00002560 fn test_impl_default_explicitly_defaulted_constructor() -> Result<()> {
2561 let ir = ir_from_cc(
Lukasz Anforowicz95551272022-01-20 00:02:24 +00002562 r#"#pragma clang lifetime_elision
2563 struct DefaultedConstructor final {
Lukasz Anforowicze643ec92021-12-22 15:45:15 +00002564 DefaultedConstructor() = default;
2565 };"#,
2566 )?;
2567 let rs_api = generate_rs_api(&ir)?;
2568 assert_rs_matches!(
2569 rs_api,
2570 quote! {
2571 impl Default for DefaultedConstructor {
2572 #[inline(always)]
2573 fn default() -> Self {
Lukasz Anforowiczbedbdee2022-01-05 01:14:52 +00002574 let mut tmp = std::mem::MaybeUninit::<Self>::zeroed();
Lukasz Anforowicze643ec92021-12-22 15:45:15 +00002575 unsafe {
Lukasz Anforowicz40c2eb82022-01-11 18:22:31 +00002576 crate::detail::__rust_thunk___ZN20DefaultedConstructorC1Ev(&mut tmp);
Lukasz Anforowicze643ec92021-12-22 15:45:15 +00002577 tmp.assume_init()
2578 }
2579 }
2580 }
2581 }
2582 );
2583 let rs_api_impl = generate_rs_api_impl(&ir)?;
2584 assert_cc_matches!(
2585 rs_api_impl,
2586 quote! {
2587 extern "C" void __rust_thunk___ZN20DefaultedConstructorC1Ev(
Googler972d3582022-01-11 10:17:22 +00002588 class DefaultedConstructor* __this) {
Lukasz Anforowicz4457baf2021-12-23 17:24:04 +00002589 rs_api_impl_support::construct_at (__this) ;
Lukasz Anforowicze643ec92021-12-22 15:45:15 +00002590 }
2591 }
2592 );
2593 Ok(())
2594 }
2595
2596 #[test]
Lukasz Anforowicz326c4e42022-01-27 14:43:00 +00002597 fn test_impl_clone_that_propagates_lifetime() -> Result<()> {
2598 // This test covers the case where a single lifetime applies to 1)
2599 // the `__this` parameter and 2) other constructor parameters. For
2600 // example, maybe the newly constructed object needs to have the
2601 // same lifetime as the constructor's parameter. (This might require
2602 // annotating the whole C++ struct with a lifetime, so maybe the
2603 // example below is not fully realistic/accurate...).
2604 let mut ir = ir_from_cc(
2605 r#"#pragma clang lifetime_elision
2606 struct Foo final {
2607 [[clang::annotate("lifetimes = a: a")]]
2608 Foo(const int& i);
2609 };"#,
2610 )?;
2611 let ctor: &mut Func = ir
2612 .items_mut()
2613 .filter_map(|item| match item {
2614 Item::Func(func) => Some(func),
2615 _ => None,
2616 })
2617 .find(|f| {
2618 matches!(&f.name, UnqualifiedIdentifier::Constructor)
2619 && f.params.get(1).map(|p| p.identifier.identifier == "i").unwrap_or_default()
2620 })
2621 .unwrap();
2622 {
2623 // Double-check that the test scenario set up above uses the same lifetime
2624 // for both of the constructor's parameters: `__this` and `i`.
2625 assert_eq!(ctor.params.len(), 2);
2626 let this_lifetime: LifetimeId =
2627 *ctor.params[0].type_.rs_type.lifetime_args.first().unwrap();
2628 let i_lifetime: LifetimeId =
2629 *ctor.params[1].type_.rs_type.lifetime_args.first_mut().unwrap();
2630 assert_eq!(i_lifetime, this_lifetime);
2631 }
2632
2633 // Before cl/423346348 the generated Rust code would incorrectly look
2634 // like this (note the mismatched 'a and 'b lifetimes):
2635 // fn from<'b>(i: &'a i32) -> Self
2636 // After this CL, this scenario will result in an explicit error.
2637 let err = generate_rs_api(&ir).unwrap_err();
2638 let msg = format!("{}", err);
2639 assert!(
2640 msg.contains("The lifetime of `__this` is unexpectedly also used by another parameter")
2641 );
2642 Ok(())
2643 }
2644
2645 #[test]
Lukasz Anforowicz9bab8352021-12-22 17:35:31 +00002646 fn test_impl_default_non_trivial_struct() -> Result<()> {
2647 let ir = ir_from_cc(
Lukasz Anforowicz71716b72022-01-26 17:05:05 +00002648 r#"#pragma clang lifetime_elision
2649 struct NonTrivialStructWithConstructors final {
Lukasz Anforowicz9bab8352021-12-22 17:35:31 +00002650 NonTrivialStructWithConstructors();
2651 ~NonTrivialStructWithConstructors(); // Non-trivial
2652 };"#,
2653 )?;
2654 let rs_api = generate_rs_api(&ir)?;
2655 assert_rs_not_matches!(rs_api, quote! {impl Default});
2656 Ok(())
2657 }
2658
2659 #[test]
Lukasz Anforowicz71716b72022-01-26 17:05:05 +00002660 fn test_impl_from_for_explicit_conversion_constructor() -> Result<()> {
2661 let ir = ir_from_cc(
2662 r#"#pragma clang lifetime_elision
2663 struct SomeStruct final {
2664 explicit SomeStruct(int i);
2665 };"#,
2666 )?;
2667 let rs_api = generate_rs_api(&ir)?;
2668 // As discussed in b/214020567 for now we only generate `From::from` bindings
2669 // for *implicit* C++ conversion constructors.
2670 assert_rs_not_matches!(rs_api, quote! {impl From});
2671 Ok(())
2672 }
2673
2674 #[test]
2675 fn test_impl_from_for_implicit_conversion_constructor() -> Result<()> {
2676 let ir = ir_from_cc(
2677 r#"#pragma clang lifetime_elision
2678 struct SomeStruct final {
2679 SomeStruct(int i); // implicit - no `explicit` keyword
2680 };"#,
2681 )?;
2682 let rs_api = generate_rs_api(&ir)?;
2683 // As discussed in b/214020567 we generate `From::from` bindings for
2684 // *implicit* C++ conversion constructors.
2685 assert_rs_matches!(
2686 rs_api,
2687 quote! {
2688 impl From<i32> for SomeStruct {
2689 #[inline(always)]
2690 fn from(i: i32) -> Self {
2691 let mut tmp = std::mem::MaybeUninit::<Self>::zeroed();
2692 unsafe {
2693 crate::detail::__rust_thunk___ZN10SomeStructC1Ei(&mut tmp, i);
2694 tmp.assume_init()
2695 }
2696 }
2697 }
2698 }
2699 );
2700 Ok(())
2701 }
2702
2703 #[test]
Lukasz Anforowicz732ca642022-02-03 20:58:38 +00002704 fn test_impl_eq_for_member_function() -> Result<()> {
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +00002705 let ir = ir_from_cc(
2706 r#"#pragma clang lifetime_elision
2707 struct SomeStruct final {
2708 inline bool operator==(const SomeStruct& other) const {
2709 return i == other.i;
2710 }
2711 int i;
2712 };"#,
2713 )?;
2714 let rs_api = generate_rs_api(&ir)?;
2715 assert_rs_matches!(
2716 rs_api,
2717 quote! {
2718 impl PartialEq<SomeStruct> for SomeStruct {
2719 #[inline(always)]
2720 fn eq<'a, 'b>(&'a self, other: &'b SomeStruct) -> bool {
2721 unsafe { crate::detail::__rust_thunk___ZNK10SomeStructeqERKS_(self, other) }
2722 }
2723 }
2724 }
2725 );
2726 let rs_api_impl = generate_rs_api_impl(&ir)?;
2727 assert_cc_matches!(
2728 rs_api_impl,
2729 quote! {
2730 extern "C" bool __rust_thunk___ZNK10SomeStructeqERKS_(
2731 const class SomeStruct* __this, const class SomeStruct& other) {
2732 return __this->operator==(other);
2733 }
2734 }
2735 );
2736 Ok(())
2737 }
2738
2739 #[test]
Lukasz Anforowicz732ca642022-02-03 20:58:38 +00002740 fn test_impl_eq_for_free_function() -> Result<()> {
2741 let ir = ir_from_cc(
2742 r#"#pragma clang lifetime_elision
2743 struct SomeStruct final { int i; };
2744 bool operator==(const SomeStruct& lhs, const SomeStruct& rhs) {
2745 return lhs.i == rhs.i;
2746 }"#,
2747 )?;
2748 let rs_api = generate_rs_api(&ir)?;
2749 assert_rs_matches!(
2750 rs_api,
2751 quote! {
2752 impl PartialEq<SomeStruct> for SomeStruct {
2753 #[inline(always)]
2754 fn eq<'a, 'b>(&'a self, rhs: &'b SomeStruct) -> bool {
2755 unsafe { crate::detail::__rust_thunk___ZeqRK10SomeStructS1_(self, rhs) }
2756 }
2757 }
2758 }
2759 );
2760 Ok(())
2761 }
2762
2763 #[test]
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +00002764 fn test_impl_eq_non_const_member_function() -> Result<()> {
2765 let ir = ir_from_cc(
2766 r#"#pragma clang lifetime_elision
2767 struct SomeStruct final {
2768 bool operator==(const SomeStruct& other) /* no `const` here */;
2769 };"#,
2770 )?;
2771 let rs_api = generate_rs_api(&ir)?;
2772 assert_rs_not_matches!(rs_api, quote! {impl PartialEq});
2773 Ok(())
2774 }
2775
2776 #[test]
2777 fn test_impl_eq_rhs_by_value() -> Result<()> {
2778 let ir = ir_from_cc(
2779 r#"#pragma clang lifetime_elision
2780 struct SomeStruct final {
2781 bool operator==(SomeStruct other) const;
2782 };"#,
2783 )?;
2784 let rs_api = generate_rs_api(&ir)?;
2785 assert_rs_not_matches!(rs_api, quote! {impl PartialEq});
2786 Ok(())
2787 }
2788
2789 #[test]
Devin Jeanpierre45cb1162021-10-27 10:54:28 +00002790 fn test_thunk_ident_function() {
2791 let func = ir_func("foo");
Marcel Hlopkoeaae9b72022-01-21 15:54:11 +00002792 assert_eq!(thunk_ident(&func), make_rs_ident("__rust_thunk___Z3foov"));
Devin Jeanpierre45cb1162021-10-27 10:54:28 +00002793 }
2794
2795 #[test]
2796 fn test_thunk_ident_special_names() {
Marcel Hlopko4b13b962021-12-06 12:40:56 +00002797 let ir = ir_from_cc("struct Class {};").unwrap();
Devin Jeanpierre45cb1162021-10-27 10:54:28 +00002798
Googler45ad2752021-12-06 12:12:35 +00002799 let destructor =
2800 ir.functions().find(|f| f.name == UnqualifiedIdentifier::Destructor).unwrap();
Lukasz Anforowiczdd9ae0f2022-02-17 15:52:53 +00002801 assert_eq!(thunk_ident(destructor), make_rs_ident("__rust_thunk___ZN5ClassD1Ev"));
Devin Jeanpierre45cb1162021-10-27 10:54:28 +00002802
Lukasz Anforowicz49b5bbc2022-02-04 23:40:10 +00002803 let default_constructor = ir
2804 .functions()
2805 .find(|f| f.name == UnqualifiedIdentifier::Constructor && f.params.len() == 1)
2806 .unwrap();
Lukasz Anforowiczdd9ae0f2022-02-17 15:52:53 +00002807 assert_eq!(thunk_ident(default_constructor), make_rs_ident("__rust_thunk___ZN5ClassC1Ev"));
Devin Jeanpierre45cb1162021-10-27 10:54:28 +00002808 }
Googler7cced422021-12-06 11:58:39 +00002809
2810 #[test]
Marcel Hlopko89547752021-12-10 09:39:41 +00002811 fn test_elided_lifetimes() -> Result<()> {
Googler7cced422021-12-06 11:58:39 +00002812 let ir = ir_from_cc(
2813 r#"#pragma clang lifetime_elision
Devin Jeanpierre88343c72022-01-15 01:10:23 +00002814 struct S final {
Googler7cced422021-12-06 11:58:39 +00002815 int& f(int& i);
2816 };"#,
Marcel Hlopko89547752021-12-10 09:39:41 +00002817 )?;
2818 let rs_api = generate_rs_api(&ir)?;
2819 assert_rs_matches!(
2820 rs_api,
2821 quote! {
Lukasz Anforowicz231a3bb2022-01-12 14:05:59 +00002822 pub fn f<'a, 'b>(&'a mut self, i: &'b mut i32) -> &'a mut i32 { ... }
Marcel Hlopko89547752021-12-10 09:39:41 +00002823 }
Googler7cced422021-12-06 11:58:39 +00002824 );
Marcel Hlopko89547752021-12-10 09:39:41 +00002825 assert_rs_matches!(
2826 rs_api,
2827 quote! {
Googler6804a012022-01-05 07:04:36 +00002828 pub(crate) fn __rust_thunk___ZN1S1fERi<'a, 'b>(__this: &'a mut S, i: &'b mut i32)
2829 -> &'a mut i32;
Marcel Hlopko89547752021-12-10 09:39:41 +00002830 }
Googler7cced422021-12-06 11:58:39 +00002831 );
Marcel Hlopko89547752021-12-10 09:39:41 +00002832 Ok(())
Googler7cced422021-12-06 11:58:39 +00002833 }
Lukasz Anforowiczdaff0402021-12-23 00:37:50 +00002834
2835 #[test]
2836 fn test_format_generic_params() -> Result<()> {
2837 assert_rs_matches!(format_generic_params(std::iter::empty::<syn::Ident>()), quote! {});
2838
Marcel Hlopkoeaae9b72022-01-21 15:54:11 +00002839 let idents = ["T1", "T2"].iter().map(|s| make_rs_ident(s));
Lukasz Anforowiczdaff0402021-12-23 00:37:50 +00002840 assert_rs_matches!(format_generic_params(idents), quote! { < T1, T2 > });
2841
2842 let lifetimes = ["a", "b"]
2843 .iter()
2844 .map(|s| syn::Lifetime::new(&format!("'{}", s), proc_macro2::Span::call_site()));
2845 assert_rs_matches!(format_generic_params(lifetimes), quote! { < 'a, 'b > });
2846
2847 Ok(())
2848 }
Googlerd03d05b2022-01-07 10:10:57 +00002849
2850 #[test]
2851 fn test_overloaded_functions() -> Result<()> {
2852 // TODO(b/213280424): We don't support creating bindings for overloaded
2853 // functions yet, except in the case of overloaded constructors with a
2854 // single parameter.
2855 let ir = ir_from_cc(
Lukasz Anforowicz55673c92022-01-27 19:37:26 +00002856 r#" #pragma clang lifetime_elision
2857 void f();
Googlerd03d05b2022-01-07 10:10:57 +00002858 void f(int i);
Devin Jeanpierre88343c72022-01-15 01:10:23 +00002859 struct S1 final {
Googlerd03d05b2022-01-07 10:10:57 +00002860 void f();
2861 void f(int i);
2862 };
Devin Jeanpierre88343c72022-01-15 01:10:23 +00002863 struct S2 final {
Googlerd03d05b2022-01-07 10:10:57 +00002864 void f();
2865 };
Devin Jeanpierre88343c72022-01-15 01:10:23 +00002866 struct S3 final {
Googlerd03d05b2022-01-07 10:10:57 +00002867 S3(int i);
2868 S3(double d);
2869 };
2870 "#,
2871 )?;
2872 let rs_api = generate_rs_api(&ir)?;
2873 let rs_api_str = tokens_to_string(rs_api.clone())?;
2874
2875 // Cannot overload free functions.
2876 assert!(rs_api_str.contains("Error while generating bindings for item 'f'"));
2877 assert_rs_not_matches!(rs_api, quote! {pub fn f()});
2878 assert_rs_not_matches!(rs_api, quote! {pub fn f(i: i32)});
2879
2880 // Cannot overload member functions.
2881 assert!(rs_api_str.contains("Error while generating bindings for item 'S1::f'"));
2882 assert_rs_not_matches!(rs_api, quote! {pub fn f(... S1 ...)});
2883
2884 // But we can import member functions that have the same name as a free
2885 // function.
Lukasz Anforowicz55673c92022-01-27 19:37:26 +00002886 assert_rs_matches!(rs_api, quote! {pub fn f<'a>(&'a mut self)});
Googlerd03d05b2022-01-07 10:10:57 +00002887
2888 // We can also import overloaded single-parameter constructors.
2889 assert_rs_matches!(rs_api, quote! {impl From<i32> for S3});
2890 assert_rs_matches!(rs_api, quote! {impl From<f64> for S3});
2891 Ok(())
2892 }
Googlerdcca7f72022-01-10 12:30:43 +00002893
2894 #[test]
2895 fn test_type_alias() -> Result<()> {
2896 let ir = ir_from_cc(
2897 r#"
2898 typedef int MyTypedefDecl;
2899 using MyTypeAliasDecl = int;
Googler6a0a5252022-01-11 14:08:09 +00002900 using MyTypeAliasDecl_Alias = MyTypeAliasDecl;
2901
Devin Jeanpierre88343c72022-01-15 01:10:23 +00002902 struct S final {};
Googler6a0a5252022-01-11 14:08:09 +00002903 using S_Alias = S;
2904 using S_Alias_Alias = S_Alias;
2905
2906 inline void f(MyTypedefDecl t) {}
Googlerdcca7f72022-01-10 12:30:43 +00002907 "#,
2908 )?;
2909 let rs_api = generate_rs_api(&ir)?;
Googler6a0a5252022-01-11 14:08:09 +00002910 assert_rs_matches!(rs_api, quote! { pub type MyTypedefDecl = i32; });
2911 assert_rs_matches!(rs_api, quote! { pub type MyTypeAliasDecl = i32; });
2912 assert_rs_matches!(rs_api, quote! { pub type MyTypeAliasDecl_Alias = MyTypeAliasDecl; });
2913 assert_rs_matches!(rs_api, quote! { pub type S_Alias = S; });
2914 assert_rs_matches!(rs_api, quote! { pub type S_Alias_Alias = S_Alias; });
2915 assert_rs_matches!(rs_api, quote! { pub fn f(t: MyTypedefDecl) });
2916 assert_cc_matches!(
2917 generate_rs_api_impl(&ir)?,
2918 quote! {
2919 extern "C" void __rust_thunk___Z1fi(MyTypedefDecl t){ f (t) ; }
2920 }
2921 );
Googlerdcca7f72022-01-10 12:30:43 +00002922 Ok(())
2923 }
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00002924
2925 #[test]
2926 fn test_rs_type_kind_implements_copy() -> Result<()> {
Lukasz Anforowicz20651e32022-02-10 14:52:15 +00002927 let template = r#" LIFETIMES
Devin Jeanpierre88343c72022-01-15 01:10:23 +00002928 struct [[clang::trivial_abi]] TrivialStruct final { int i; };
2929 struct [[clang::trivial_abi]] UserDefinedCopyConstructor final {
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00002930 UserDefinedCopyConstructor(const UserDefinedCopyConstructor&);
2931 };
2932 using IntAlias = int;
2933 using TrivialAlias = TrivialStruct;
2934 using NonTrivialAlias = UserDefinedCopyConstructor;
2935 void func(PARAM_TYPE some_param);
2936 "#;
2937 assert_impl_all!(i32: Copy);
2938 assert_impl_all!(&i32: Copy);
2939 assert_not_impl_all!(&mut i32: Copy);
Lukasz Anforowicz20651e32022-02-10 14:52:15 +00002940 assert_impl_all!(Option<&i32>: Copy);
2941 assert_not_impl_all!(Option<&mut i32>: Copy);
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00002942 assert_impl_all!(*const i32: Copy);
2943 assert_impl_all!(*mut i32: Copy);
Lukasz Anforowicz20651e32022-02-10 14:52:15 +00002944 struct Test {
2945 // Test inputs:
2946 cc: &'static str,
2947 lifetimes: bool,
2948 // Expected test outputs:
2949 rs: &'static str,
2950 is_copy: bool,
2951 }
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00002952 let tests = vec![
2953 // Validity of the next few tests is verified via
2954 // `assert_[not_]impl_all!` static assertions above.
Lukasz Anforowicz20651e32022-02-10 14:52:15 +00002955 Test { cc: "int", lifetimes: true, rs: "i32", is_copy: true },
2956 Test { cc: "const int&", lifetimes: true, rs: "&'a i32", is_copy: true },
2957 Test { cc: "int&", lifetimes: true, rs: "&'a mut i32", is_copy: false },
2958 Test { cc: "const int*", lifetimes: true, rs: "Option<&'a i32>", is_copy: true },
2959 Test { cc: "int*", lifetimes: true, rs: "Option<&'a mut i32>", is_copy: false },
2960 Test { cc: "const int*", lifetimes: false, rs: "*const i32", is_copy: true },
2961 Test { cc: "int*", lifetimes: false, rs: "*mut i32", is_copy: true },
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00002962 // Tests below have been thought-through and verified "manually".
Lukasz Anforowicz20651e32022-02-10 14:52:15 +00002963 // TrivialStruct is expected to derive Copy.
2964 Test { cc: "TrivialStruct", lifetimes: true, rs: "TrivialStruct", is_copy: true },
2965 Test {
2966 cc: "UserDefinedCopyConstructor",
2967 lifetimes: true,
2968 rs: "UserDefinedCopyConstructor",
2969 is_copy: false,
2970 },
2971 Test { cc: "IntAlias", lifetimes: true, rs: "IntAlias", is_copy: true },
2972 Test { cc: "TrivialAlias", lifetimes: true, rs: "TrivialAlias", is_copy: true },
2973 Test { cc: "NonTrivialAlias", lifetimes: true, rs: "NonTrivialAlias", is_copy: false },
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00002974 ];
Lukasz Anforowicz20651e32022-02-10 14:52:15 +00002975 for test in tests.iter() {
2976 let test_name = format!("cc='{}', lifetimes={}", test.cc, test.lifetimes);
2977 let cc_input = template.replace("PARAM_TYPE", test.cc).replace(
2978 "LIFETIMES",
2979 if test.lifetimes { "#pragma clang lifetime_elision" } else { "" },
2980 );
2981 let ir = ir_from_cc(&cc_input)?;
Lukasz Anforowicz9c663ca2022-02-09 01:33:31 +00002982 let f = retrieve_func(&ir, "func");
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00002983 let t = RsTypeKind::new(&f.params[0].type_.rs_type, &ir)?;
Lukasz Anforowicz20651e32022-02-10 14:52:15 +00002984
Lukasz Anforowicz90bdb962022-02-14 21:07:45 +00002985 let lifetime_to_name: HashMap::<LifetimeId, String> = t.lifetimes().map(
2986 |lifetime_id| (lifetime_id, "a".to_string())).collect();
2987
Lukasz Anforowicz20651e32022-02-10 14:52:15 +00002988 let fmt = tokens_to_string(t.format(&ir, &lifetime_to_name)?)?;
2989 assert_eq!(test.rs, fmt, "Testing: {}", test_name);
2990
2991 assert_eq!(test.is_copy, t.implements_copy(), "Testing: {}", test_name);
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00002992 }
2993 Ok(())
2994 }
Lukasz Anforowicza94ab702022-01-14 22:40:25 +00002995
2996 #[test]
2997 fn test_rs_type_kind_is_shared_ref_to_with_lifetimes() -> Result<()> {
2998 let ir = ir_from_cc(
2999 "#pragma clang lifetime_elision
3000 struct SomeStruct {};
3001 void foo(const SomeStruct& foo_param);
3002 void bar(SomeStruct& bar_param);",
3003 )?;
3004 let record = ir.records().next().unwrap();
Lukasz Anforowicz9c663ca2022-02-09 01:33:31 +00003005 let foo_func = retrieve_func(&ir, "foo");
3006 let bar_func = retrieve_func(&ir, "bar");
Lukasz Anforowicza94ab702022-01-14 22:40:25 +00003007
3008 // const-ref + lifetimes in C++ ===> shared-ref in Rust
3009 assert_eq!(foo_func.params.len(), 1);
3010 let foo_param = &foo_func.params[0];
3011 assert_eq!(&foo_param.identifier.identifier, "foo_param");
3012 let foo_type = RsTypeKind::new(&foo_param.type_.rs_type, &ir)?;
3013 assert!(foo_type.is_shared_ref_to(record));
3014 assert!(matches!(foo_type, RsTypeKind::Reference { mutability: Mutability::Const, .. }));
3015
3016 // non-const-ref + lifetimes in C++ ===> mutable-ref in Rust
3017 assert_eq!(bar_func.params.len(), 1);
3018 let bar_param = &bar_func.params[0];
3019 assert_eq!(&bar_param.identifier.identifier, "bar_param");
3020 let bar_type = RsTypeKind::new(&bar_param.type_.rs_type, &ir)?;
3021 assert!(!bar_type.is_shared_ref_to(record));
3022 assert!(matches!(bar_type, RsTypeKind::Reference { mutability: Mutability::Mut, .. }));
3023
3024 Ok(())
3025 }
3026
3027 #[test]
3028 fn test_rs_type_kind_is_shared_ref_to_without_lifetimes() -> Result<()> {
3029 let ir = ir_from_cc(
3030 "struct SomeStruct {};
3031 void foo(const SomeStruct& foo_param);",
3032 )?;
3033 let record = ir.records().next().unwrap();
Lukasz Anforowicz9c663ca2022-02-09 01:33:31 +00003034 let foo_func = retrieve_func(&ir, "foo");
Lukasz Anforowicza94ab702022-01-14 22:40:25 +00003035
3036 // const-ref + *no* lifetimes in C++ ===> const-pointer in Rust
3037 assert_eq!(foo_func.params.len(), 1);
3038 let foo_param = &foo_func.params[0];
3039 assert_eq!(&foo_param.identifier.identifier, "foo_param");
3040 let foo_type = RsTypeKind::new(&foo_param.type_.rs_type, &ir)?;
3041 assert!(!foo_type.is_shared_ref_to(record));
3042 assert!(matches!(foo_type, RsTypeKind::Pointer { mutability: Mutability::Const, .. }));
3043
3044 Ok(())
3045 }
Marcel Hlopkoeaae9b72022-01-21 15:54:11 +00003046
3047 #[test]
Lukasz Anforowicz90bdb962022-02-14 21:07:45 +00003048 fn test_rs_type_kind_dfs_iter_ordering() {
3049 // Set up a test input representing: A<B<C>, D<E>>.
3050 let a = {
3051 let b = {
3052 let c = RsTypeKind::Other { name: "C", type_args: vec![] };
3053 RsTypeKind::Other { name: "B", type_args: vec![c] }
3054 };
3055 let d = {
3056 let e = RsTypeKind::Other { name: "E", type_args: vec![] };
3057 RsTypeKind::Other { name: "D", type_args: vec![e] }
3058 };
3059 RsTypeKind::Other { name: "A", type_args: vec![b, d] }
3060 };
3061 let dfs_names = a
3062 .dfs_iter()
3063 .map(|t| match t {
3064 RsTypeKind::Other { name, .. } => *name,
3065 _ => unreachable!("Only 'other' types are used in this test"),
3066 })
3067 .collect_vec();
3068 assert_eq!(vec!["A", "B", "C", "D", "E"], dfs_names);
3069 }
3070
3071 #[test]
3072 fn test_rs_type_kind_lifetimes() -> Result<()> {
3073 let ir = ir_from_cc(
3074 r#"
3075 #pragma clang lifetime_elision
3076 using TypeAlias = int&;
3077 struct SomeStruct {};
3078 void foo(int a, int& b, int* c, int** d, TypeAlias e, SomeStruct f); "#,
3079 )?;
3080 let f = retrieve_func(&ir, "foo");
3081 let ret = RsTypeKind::new(&f.return_type.rs_type, &ir)?;
3082 let a = RsTypeKind::new(&f.params[0].type_.rs_type, &ir)?;
3083 let b = RsTypeKind::new(&f.params[1].type_.rs_type, &ir)?;
3084 let c = RsTypeKind::new(&f.params[2].type_.rs_type, &ir)?;
3085 let d = RsTypeKind::new(&f.params[3].type_.rs_type, &ir)?;
3086 let e = RsTypeKind::new(&f.params[4].type_.rs_type, &ir)?;
3087 let f = RsTypeKind::new(&f.params[5].type_.rs_type, &ir)?;
3088
3089 assert_eq!(0, ret.lifetimes().count()); // No lifetimes on `void`.
3090 assert_eq!(0, a.lifetimes().count()); // No lifetimes on `int`.
3091 assert_eq!(1, b.lifetimes().count()); // `&'a i32` has a single lifetime.
3092 assert_eq!(1, c.lifetimes().count()); // `Option<&'b i32>` has a single lifetime.
3093 assert_eq!(2, d.lifetimes().count()); // `&'c Option<&'d i32>` has two lifetimes.
3094 assert_eq!(1, e.lifetimes().count()); // Lifetime of underlying type should show through.
3095 assert_eq!(0, f.lifetimes().count()); // No lifetimes on structs (yet).
3096 Ok(())
3097 }
3098
3099 #[test]
3100 fn test_rs_type_kind_lifetimes_raw_ptr() -> Result<()> {
3101 let ir = ir_from_cc("void foo(int* a);")?;
3102 let f = retrieve_func(&ir, "foo");
3103 let a = RsTypeKind::new(&f.params[0].type_.rs_type, &ir)?;
3104 assert_eq!(0, a.lifetimes().count()); // No lifetimes on `int*`.
3105 Ok(())
3106 }
3107
3108 #[test]
Marcel Hlopkoeaae9b72022-01-21 15:54:11 +00003109 fn test_rust_keywords_are_escaped_in_rs_api_file() -> Result<()> {
3110 let ir = ir_from_cc("struct type { int dyn; };")?;
3111 let rs_api = generate_rs_api(&ir)?;
3112 assert_rs_matches!(rs_api, quote! { struct r#type { ... r#dyn: i32 ... } });
3113 Ok(())
3114 }
3115
3116 #[test]
3117 fn test_rust_keywords_are_not_escaped_in_rs_api_impl_file() -> Result<()> {
3118 let ir = ir_from_cc("struct type { int dyn; };")?;
3119 let rs_api_impl = generate_rs_api_impl(&ir)?;
3120 assert_cc_matches!(rs_api_impl, quote! { static_assert(offsetof(class type, dyn) ... ) });
3121 Ok(())
3122 }
Marcel Hlopko14ee3c82022-02-09 09:46:23 +00003123
3124 #[test]
3125 fn test_no_aligned_attr() {
3126 let ir = ir_from_cc("struct SomeStruct {};").unwrap();
3127 let rs_api = generate_rs_api(&ir).unwrap();
3128
3129 assert_rs_matches! {rs_api, quote! {
3130 #[repr(C)]
3131 pub struct SomeStruct { ... }
3132 }};
3133 }
3134
3135 #[test]
3136 fn test_aligned_attr() {
3137 let ir = ir_from_cc("struct SomeStruct {} __attribute__((aligned(64)));").unwrap();
3138 let rs_api = generate_rs_api(&ir).unwrap();
3139
3140 assert_rs_matches! {rs_api, quote! {
3141 #[repr(C, align(64))]
3142 pub struct SomeStruct { ... }
3143 }
3144 };
3145 }
Marcel Hlopko42abfc82021-08-09 07:03:17 +00003146}