blob: d63f2391883726187ded2f658e6210115584dfec [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... :-/
Lukasz Anforowicz72c4d222022-02-18 19:07:28 +000079 //
80 // TODO(lukasza): Try to remove `#![rustfmt:skip]` - in theory it shouldn't
81 // be needed when `@generated` comment/keyword is present...
Lukasz Anforowicz5b3f5302022-02-07 01:04:47 +000082 let rs_api = format!(
Lukasz Anforowicz72c4d222022-02-18 19:07:28 +000083 "// Automatically @generated Rust bindings for C++ target\n\
84 // {target}\n\
85 #![rustfmt::skip]\n\
86 {code}",
Lukasz Anforowicz5b3f5302022-02-07 01:04:47 +000087 target = ir.current_target().0,
88 code = rs_tokens_to_formatted_string(generate_rs_api(&ir)?)?
89 );
Marcel Hlopko89547752021-12-10 09:39:41 +000090 let rs_api_impl = tokens_to_string(generate_rs_api_impl(&ir)?)?;
Marcel Hlopkoca84ff42021-12-09 14:15:14 +000091
Marcel Hlopko45fba972021-08-23 19:52:20 +000092 Ok(Bindings { rs_api, rs_api_impl })
93}
94
Devin Jeanpierre6d5e7cc2021-10-21 12:56:07 +000095/// Rust source code with attached information about how to modify the parent
96/// crate.
Devin Jeanpierre273eeae2021-10-06 13:29:35 +000097///
Michael Forsterbee84482021-10-13 08:35:38 +000098/// For example, the snippet `vec![].into_raw_parts()` is not valid unless the
99/// `vec_into_raw_parts` feature is enabled. So such a snippet should be
100/// represented as:
Devin Jeanpierre273eeae2021-10-06 13:29:35 +0000101///
102/// ```
103/// RsSnippet {
Marcel Hlopkoeaae9b72022-01-21 15:54:11 +0000104/// features: btree_set![make_rs_ident("vec_into_raw_parts")],
Devin Jeanpierre273eeae2021-10-06 13:29:35 +0000105/// tokens: quote!{vec![].into_raw_parts()},
106/// }
107/// ```
Lukasz Anforowiczeb19ac62022-02-05 00:10:16 +0000108#[derive(Clone, Debug)]
Devin Jeanpierre273eeae2021-10-06 13:29:35 +0000109struct RsSnippet {
110 /// Rust feature flags used by this snippet.
111 features: BTreeSet<Ident>,
112 /// The snippet itself, as a token stream.
113 tokens: TokenStream,
114}
115
116impl From<TokenStream> for RsSnippet {
117 fn from(tokens: TokenStream) -> Self {
118 RsSnippet { features: BTreeSet::new(), tokens }
119 }
120}
121
Michael Forsterbee84482021-10-13 08:35:38 +0000122/// If we know the original C++ function is codegenned and already compatible
123/// with `extern "C"` calling convention we skip creating/calling the C++ thunk
124/// since we can call the original C++ directly.
Marcel Hlopko3164eee2021-08-24 20:09:22 +0000125fn can_skip_cc_thunk(func: &Func) -> bool {
Devin Jeanpierre96839c12021-12-14 00:27:38 +0000126 // ## Inline functions
127 //
Michael Forsterbee84482021-10-13 08:35:38 +0000128 // Inline functions may not be codegenned in the C++ library since Clang doesn't
129 // know if Rust calls the function or not. Therefore in order to make inline
130 // functions callable from Rust we need to generate a C++ file that defines
131 // a thunk that delegates to the original inline function. When compiled,
132 // Clang will emit code for this thunk and Rust code will call the
Marcel Hlopko3164eee2021-08-24 20:09:22 +0000133 // thunk when the user wants to call the original inline function.
134 //
Michael Forsterbee84482021-10-13 08:35:38 +0000135 // This is not great runtime-performance-wise in regular builds (inline function
136 // will not be inlined, there will always be a function call), but it is
137 // correct. ThinLTO builds will be able to see through the thunk and inline
138 // code across the language boundary. For non-ThinLTO builds we plan to
139 // implement <internal link> which removes the runtime performance overhead.
Devin Jeanpierre96839c12021-12-14 00:27:38 +0000140 if func.is_inline {
141 return false;
142 }
143 // ## Virtual functions
144 //
145 // When calling virtual `A::Method()`, it's not necessarily the case that we'll
146 // specifically call the concrete `A::Method` impl. For example, if this is
147 // called on something whose dynamic type is some subclass `B` with an
148 // overridden `B::Method`, then we'll call that.
149 //
150 // We must reuse the C++ dynamic dispatching system. In this case, the easiest
151 // way to do it is by resorting to a C++ thunk, whose implementation will do
152 // the lookup.
153 //
154 // In terms of runtime performance, since this only occurs for virtual function
155 // calls, which are already slow, it may not be such a big deal. We can
156 // benchmark it later. :)
157 if let Some(meta) = &func.member_func_metadata {
158 if let Some(inst_meta) = &meta.instance_method_metadata {
159 if inst_meta.is_virtual {
160 return false;
161 }
162 }
163 }
164
165 true
Marcel Hlopko3164eee2021-08-24 20:09:22 +0000166}
167
Googlerd03d05b2022-01-07 10:10:57 +0000168/// Uniquely identifies a generated Rust function.
Lukasz Anforowiczeb19ac62022-02-05 00:10:16 +0000169#[derive(Clone, Debug, PartialEq, Eq, Hash)]
Googlerd03d05b2022-01-07 10:10:57 +0000170struct FunctionId {
171 // If the function is on a trait impl, contains the name of the Self type for
172 // which the trait is being implemented.
173 self_type: Option<syn::Path>,
174 // Fully qualified path of the function. For functions in impl blocks, this
175 // includes the name of the type or trait on which the function is being
176 // implemented, e.g. `Default::default`.
177 function_path: syn::Path,
178}
179
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +0000180/// Returns the name of `func` in C++ syntax.
Googlerd03d05b2022-01-07 10:10:57 +0000181fn cxx_function_name(func: &Func, ir: &IR) -> Result<String> {
182 let record: Option<&str> = func
183 .member_func_metadata
184 .as_ref()
185 .map(|meta| meta.find_record(ir))
186 .transpose()?
187 .map(|r| &*r.identifier.identifier);
188
189 let func_name = match &func.name {
190 UnqualifiedIdentifier::Identifier(id) => id.identifier.clone(),
Lukasz Anforowicz9c663ca2022-02-09 01:33:31 +0000191 UnqualifiedIdentifier::Operator(op) => op.cc_name(),
Googlerd03d05b2022-01-07 10:10:57 +0000192 UnqualifiedIdentifier::Destructor => {
193 format!("~{}", record.expect("destructor must be associated with a record"))
194 }
195 UnqualifiedIdentifier::Constructor => {
Lukasz Anforowiczeb19ac62022-02-05 00:10:16 +0000196 record.expect("constructor must be associated with a record").to_string()
Googlerd03d05b2022-01-07 10:10:57 +0000197 }
198 };
199
200 if let Some(record_name) = record {
201 Ok(format!("{}::{}", record_name, func_name))
202 } else {
203 Ok(func_name)
204 }
205}
206
Lukasz Anforowiczeb19ac62022-02-05 00:10:16 +0000207fn make_unsupported_fn(func: &Func, ir: &IR, message: impl ToString) -> Result<UnsupportedItem> {
208 Ok(UnsupportedItem {
209 name: cxx_function_name(func, ir)?,
210 message: message.to_string(),
211 source_loc: func.source_loc.clone(),
212 })
213}
214
215#[derive(Clone, Debug)]
216enum GeneratedFunc {
217 None, // No explicit function needed (e.g. when deriving Drop).
218 Unsupported(UnsupportedItem),
219 Some { api_func: RsSnippet, thunk: RsSnippet, function_id: FunctionId },
220}
221
Michael Forstered642022021-10-04 09:48:25 +0000222/// Generates Rust source code for a given `Func`.
Lukasz Anforowiczeb19ac62022-02-05 00:10:16 +0000223fn generate_func(func: &Func, ir: &IR) -> Result<GeneratedFunc> {
224 let make_unsupported_result = |msg: &str| -> Result<GeneratedFunc> {
225 Ok(GeneratedFunc::Unsupported(make_unsupported_fn(func, ir, msg)?))
226 };
227
Michael Forstered642022021-10-04 09:48:25 +0000228 let mangled_name = &func.mangled_name;
Googlera675ae02021-12-07 08:04:59 +0000229 let thunk_ident = thunk_ident(func);
Michael Forster409d9412021-10-07 08:35:29 +0000230 let doc_comment = generate_doc_comment(&func.doc_comment);
Googler7cced422021-12-06 11:58:39 +0000231 let lifetime_to_name = HashMap::<LifetimeId, String>::from_iter(
232 func.lifetime_params.iter().map(|l| (l.id, l.name.clone())),
233 );
Lukasz Anforowiczcf230fd2022-02-18 19:20:39 +0000234 let return_type_fragment = RsTypeKind::new(&func.return_type.rs_type, ir)
235 .and_then(|t| t.format_as_return_type_fragment(ir, &lifetime_to_name))
Googlerb7e361d2022-01-04 14:02:59 +0000236 .with_context(|| format!("Failed to format return type for {:?}", func))?;
Michael Forstered642022021-10-04 09:48:25 +0000237
238 let param_idents =
Marcel Hlopkoeaae9b72022-01-21 15:54:11 +0000239 func.params.iter().map(|p| make_rs_ident(&p.identifier.identifier)).collect_vec();
Michael Forstered642022021-10-04 09:48:25 +0000240
Lukasz Anforowiczf7bdd392022-01-21 00:33:39 +0000241 let param_type_kinds = func
Marcel Hlopkoc0956cf2021-11-29 08:31:28 +0000242 .params
243 .iter()
Googlerb7e361d2022-01-04 14:02:59 +0000244 .map(|p| {
Lukasz Anforowiczf7bdd392022-01-21 00:33:39 +0000245 RsTypeKind::new(&p.type_.rs_type, ir).with_context(|| {
246 format!("Failed to process type of parameter {:?} on {:?}", p, func)
Googlerb7e361d2022-01-04 14:02:59 +0000247 })
248 })
Marcel Hlopkoc0956cf2021-11-29 08:31:28 +0000249 .collect::<Result<Vec<_>>>()?;
Lukasz Anforowiczf7bdd392022-01-21 00:33:39 +0000250 let param_types = param_type_kinds
251 .iter()
252 .map(|t| {
253 t.format(ir, &lifetime_to_name)
254 .with_context(|| format!("Failed to format parameter type {:?} on {:?}", t, func))
255 })
256 .collect::<Result<Vec<_>>>()?;
Devin Jeanpierre55298ac2022-02-24 01:37:37 +0000257 let is_unsafe = param_type_kinds.iter().any(|p| matches!(p, RsTypeKind::Pointer { .. }))
258 && func.name != UnqualifiedIdentifier::Destructor;
Michael Forstered642022021-10-04 09:48:25 +0000259
Lukasz Anforowiczab65e292022-01-14 23:04:21 +0000260 let maybe_record: Option<&Record> =
Lukasz Anforowicz13cf7492021-12-22 15:29:52 +0000261 func.member_func_metadata.as_ref().map(|meta| meta.find_record(ir)).transpose()?;
Lukasz Anforowicz732ca642022-02-03 20:58:38 +0000262 let maybe_record_name = maybe_record.map(|r| make_rs_ident(&r.identifier.identifier));
Lukasz Anforowicz13cf7492021-12-22 15:29:52 +0000263
Lukasz Anforowicz732ca642022-02-03 20:58:38 +0000264 // Find 1) the `func_name` and `impl_kind` of the API function to generate
265 // and 2) whether to `format_first_param_as_self` (`&self` or `&mut self`).
Lukasz Anforowiczab65e292022-01-14 23:04:21 +0000266 enum ImplKind {
Lukasz Anforowicz732ca642022-02-03 20:58:38 +0000267 None, // No `impl` needed
268 Struct, // e.g. `impl SomeStruct { ... }` (SomeStruct based on func.member_func_metadata)
269 Trait {
270 trait_name: TokenStream, // e.g. quote!{ From<int> }
271 record_name: Ident, /* e.g. SomeStruct (might *not* be from
272 * func.member_func_metadata) */
273 },
Lukasz Anforowiczab65e292022-01-14 23:04:21 +0000274 }
275 let impl_kind: ImplKind;
276 let func_name: syn::Ident;
277 let format_first_param_as_self: bool;
Googlerd03d05b2022-01-07 10:10:57 +0000278 match &func.name {
Lukasz Anforowicz9c663ca2022-02-09 01:33:31 +0000279 UnqualifiedIdentifier::Operator(op) if op.name == "==" => {
Lukasz Anforowiczeb19ac62022-02-05 00:10:16 +0000280 if param_type_kinds.len() != 2 {
281 bail!("Unexpected number of parameters in operator==: {:?}", func);
282 }
283 match (&param_type_kinds[0], &param_type_kinds[1]) {
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +0000284 (
Lukasz Anforowiczeb19ac62022-02-05 00:10:16 +0000285 RsTypeKind::Reference { referent: lhs, mutability: Mutability::Const, .. },
286 RsTypeKind::Reference { referent: rhs, mutability: Mutability::Const, .. },
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +0000287 ) => match **lhs {
288 RsTypeKind::Record(lhs_record) => {
Lukasz Anforowicz732ca642022-02-03 20:58:38 +0000289 let lhs: Ident = make_rs_ident(&lhs_record.identifier.identifier);
290 let rhs: TokenStream = rhs.format(ir, &lifetime_to_name)?;
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +0000291 format_first_param_as_self = true;
292 func_name = make_rs_ident("eq");
Lukasz Anforowicz732ca642022-02-03 20:58:38 +0000293 impl_kind = ImplKind::Trait {
294 trait_name: quote! {PartialEq<#rhs>},
295 record_name: lhs,
296 };
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +0000297 }
Marcel Hlopko14ee3c82022-02-09 09:46:23 +0000298 _ => {
299 return make_unsupported_result(
300 "operator== where lhs doesn't refer to a record",
301 );
302 }
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +0000303 },
Marcel Hlopko14ee3c82022-02-09 09:46:23 +0000304 _ => {
305 return make_unsupported_result(
306 "operator== where operands are not const references",
307 );
308 }
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +0000309 };
310 }
Lukasz Anforowicz9c663ca2022-02-09 01:33:31 +0000311 UnqualifiedIdentifier::Operator(_) => {
Lukasz Anforowiczeb19ac62022-02-05 00:10:16 +0000312 return make_unsupported_result("Bindings for this kind of operator are not supported");
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +0000313 }
Devin Jeanpierref2ec8712021-10-13 20:47:16 +0000314 UnqualifiedIdentifier::Identifier(id) => {
Marcel Hlopkoeaae9b72022-01-21 15:54:11 +0000315 func_name = make_rs_ident(&id.identifier);
Lukasz Anforowiczf9564622022-01-28 14:31:04 +0000316 match maybe_record {
317 None => {
318 impl_kind = ImplKind::None;
319 format_first_param_as_self = false;
320 }
321 Some(record) => {
322 impl_kind = ImplKind::Struct;
323 if func.is_instance_method() {
324 let first_param = param_type_kinds.first().ok_or_else(|| {
325 anyhow!("Missing `__this` parameter in an instance method: {:?}", func)
326 })?;
327 format_first_param_as_self = first_param.is_ref_to(record)
328 } else {
329 format_first_param_as_self = false;
330 }
331 }
332 };
Michael Forstered642022021-10-04 09:48:25 +0000333 }
Devin Jeanpierre91de7012021-10-21 12:53:51 +0000334 UnqualifiedIdentifier::Destructor => {
Lukasz Anforowiczab65e292022-01-14 23:04:21 +0000335 // Note: to avoid double-destruction of the fields, they are all wrapped in
336 // ManuallyDrop in this case. See `generate_record`.
337 let record =
338 maybe_record.ok_or_else(|| anyhow!("Destructors must be member functions."))?;
Lukasz Anforowicze57215c2022-01-12 14:54:16 +0000339 if !should_implement_drop(record) {
Lukasz Anforowiczeb19ac62022-02-05 00:10:16 +0000340 return Ok(GeneratedFunc::None);
Devin Jeanpierre91de7012021-10-21 12:53:51 +0000341 }
Lukasz Anforowicz732ca642022-02-03 20:58:38 +0000342 let record_name = maybe_record_name
343 .clone()
344 .ok_or_else(|| anyhow!("Destructors must be member functions."))?;
345 impl_kind = ImplKind::Trait { trait_name: quote! {Drop}, record_name };
Marcel Hlopkoeaae9b72022-01-21 15:54:11 +0000346 func_name = make_rs_ident("drop");
Lukasz Anforowiczab65e292022-01-14 23:04:21 +0000347 format_first_param_as_self = true;
Devin Jeanpierre91de7012021-10-21 12:53:51 +0000348 }
Lukasz Anforowicz13cf7492021-12-22 15:29:52 +0000349 UnqualifiedIdentifier::Constructor => {
Lukasz Anforowicz71716b72022-01-26 17:05:05 +0000350 let member_func_metadata = func
351 .member_func_metadata
352 .as_ref()
353 .ok_or_else(|| anyhow!("Constructors must be member functions."))?;
354 let record = maybe_record
355 .ok_or_else(|| anyhow!("Constructors must be associated with a record."))?;
356 let instance_method_metadata =
357 member_func_metadata
358 .instance_method_metadata
359 .as_ref()
360 .ok_or_else(|| anyhow!("Constructors must be instance methods."))?;
361
Devin Jeanpierre88343c72022-01-15 01:10:23 +0000362 if !record.is_unpin() {
Lukasz Anforowiczab65e292022-01-14 23:04:21 +0000363 // TODO: Handle <internal link>
Lukasz Anforowiczeb19ac62022-02-05 00:10:16 +0000364 return make_unsupported_result(
365 "Bindings for constructors of non-trivial types are not supported yet",
366 );
Lukasz Anforowicz9bab8352021-12-22 17:35:31 +0000367 }
Lukasz Anforowicz55673c92022-01-27 19:37:26 +0000368 if is_unsafe {
369 // TODO(b/216648347): Allow this outside of traits (e.g. after supporting
370 // translating C++ constructors into static methods in Rust).
Lukasz Anforowiczeb19ac62022-02-05 00:10:16 +0000371 return make_unsupported_result(
372 "Unsafe constructors (e.g. with no elided or explicit lifetimes) \
373 are intentionally not supported",
374 );
Lukasz Anforowicz55673c92022-01-27 19:37:26 +0000375 }
376
Lukasz Anforowicz732ca642022-02-03 20:58:38 +0000377 let record_name = maybe_record_name
378 .clone()
379 .ok_or_else(|| anyhow!("Constructors must be member functions."))?;
Lukasz Anforowicz2e41bb62022-01-11 18:23:07 +0000380 match func.params.len() {
Lukasz Anforowiczf9564622022-01-28 14:31:04 +0000381 0 => bail!("Missing `__this` parameter in a constructor: {:?}", func),
Lukasz Anforowicz2e41bb62022-01-11 18:23:07 +0000382 1 => {
Lukasz Anforowicz732ca642022-02-03 20:58:38 +0000383 impl_kind = ImplKind::Trait { trait_name: quote! {Default}, record_name };
Marcel Hlopkoeaae9b72022-01-21 15:54:11 +0000384 func_name = make_rs_ident("default");
Lukasz Anforowiczab65e292022-01-14 23:04:21 +0000385 format_first_param_as_self = false;
Lukasz Anforowicz2e41bb62022-01-11 18:23:07 +0000386 }
Lukasz Anforowicz73326af2022-01-05 01:13:10 +0000387 2 => {
Lukasz Anforowicz2e41bb62022-01-11 18:23:07 +0000388 // TODO(lukasza): Do something smart with move constructor.
Lukasz Anforowiczf7bdd392022-01-21 00:33:39 +0000389 if param_type_kinds[1].is_shared_ref_to(record) {
Lukasz Anforowicz2e41bb62022-01-11 18:23:07 +0000390 // Copy constructor
391 if should_derive_clone(record) {
Lukasz Anforowiczeb19ac62022-02-05 00:10:16 +0000392 return Ok(GeneratedFunc::None);
Lukasz Anforowicz2e41bb62022-01-11 18:23:07 +0000393 } else {
Lukasz Anforowicz732ca642022-02-03 20:58:38 +0000394 impl_kind = ImplKind::Trait { trait_name: quote! {Clone}, record_name };
Marcel Hlopkoeaae9b72022-01-21 15:54:11 +0000395 func_name = make_rs_ident("clone");
Lukasz Anforowiczab65e292022-01-14 23:04:21 +0000396 format_first_param_as_self = true;
Lukasz Anforowicz2e41bb62022-01-11 18:23:07 +0000397 }
Lukasz Anforowicz71716b72022-01-26 17:05:05 +0000398 } else if !instance_method_metadata.is_explicit_ctor {
Lukasz Anforowicz2e41bb62022-01-11 18:23:07 +0000399 let param_type = &param_types[1];
Lukasz Anforowicz732ca642022-02-03 20:58:38 +0000400 impl_kind = ImplKind::Trait {
401 trait_name: quote! {From< #param_type >},
402 record_name,
403 };
Marcel Hlopkoeaae9b72022-01-21 15:54:11 +0000404 func_name = make_rs_ident("from");
Lukasz Anforowiczab65e292022-01-14 23:04:21 +0000405 format_first_param_as_self = false;
Lukasz Anforowicz71716b72022-01-26 17:05:05 +0000406 } else {
Marcel Hlopko14ee3c82022-02-09 09:46:23 +0000407 return make_unsupported_result(
408 "Not yet supported type of constructor parameter",
409 );
Lukasz Anforowicz73326af2022-01-05 01:13:10 +0000410 }
411 }
412 _ => {
Lukasz Anforowicz55673c92022-01-27 19:37:26 +0000413 // TODO(b/216648347): Support bindings for other constructors.
Marcel Hlopko14ee3c82022-02-09 09:46:23 +0000414 return make_unsupported_result(
415 "More than 1 constructor parameter is not supported yet",
416 );
Lukasz Anforowicz73326af2022-01-05 01:13:10 +0000417 }
Lukasz Anforowiczab65e292022-01-14 23:04:21 +0000418 }
419 }
420 }
421
422 let api_func_def = {
Lukasz Anforowicz95551272022-01-20 00:02:24 +0000423 // Clone params, return type, etc - we may need to mutate them in the
424 // API func, but we want to retain the originals for the thunk.
Lukasz Anforowiczab65e292022-01-14 23:04:21 +0000425 let mut return_type_fragment = return_type_fragment.clone();
426 let mut thunk_args = param_idents.iter().map(|id| quote! { #id}).collect_vec();
427 let mut api_params = param_idents
428 .iter()
429 .zip(param_types.iter())
430 .map(|(ident, type_)| quote! { #ident : #type_ })
431 .collect_vec();
Lukasz Anforowicz95551272022-01-20 00:02:24 +0000432 let mut lifetimes = func.lifetime_params.iter().collect_vec();
Lukasz Anforowiczf7bdd392022-01-21 00:33:39 +0000433 let mut maybe_first_api_param = param_type_kinds.get(0);
Lukasz Anforowiczab65e292022-01-14 23:04:21 +0000434
435 if func.name == UnqualifiedIdentifier::Constructor {
436 return_type_fragment = quote! { -> Self };
437
Lukasz Anforowiczf9564622022-01-28 14:31:04 +0000438 // Drop `__this` parameter from the public Rust API. Presence of
439 // element #0 is indirectly verified by a `Constructor`-related
440 // `match` branch a little bit above.
441 api_params.remove(0);
442 thunk_args.remove(0);
Lukasz Anforowicz95551272022-01-20 00:02:24 +0000443
Lukasz Anforowicz326c4e42022-01-27 14:43:00 +0000444 // Remove the lifetime associated with `__this`.
Lukasz Anforowicz90bdb962022-02-14 21:07:45 +0000445 ensure!(func.return_type.rs_type.is_unit_type(),
446 "Unexpectedly non-void return type of a constructor: {:?}", func);
Lukasz Anforowiczf7bdd392022-01-21 00:33:39 +0000447 let maybe_first_lifetime = func.params[0].type_.rs_type.lifetime_args.first();
Lukasz Anforowicz55673c92022-01-27 19:37:26 +0000448 let no_longer_needed_lifetime_id = maybe_first_lifetime
449 .ok_or_else(|| anyhow!("Missing lifetime on `__this` parameter: {:?}", func))?;
450 lifetimes.retain(|l| l.id != *no_longer_needed_lifetime_id);
Lukasz Anforowicz55673c92022-01-27 19:37:26 +0000451 if let Some(type_still_dependent_on_removed_lifetime) = param_type_kinds
452 .iter()
453 .skip(1) // Skipping `__this`
Lukasz Anforowicz90bdb962022-02-14 21:07:45 +0000454 .flat_map(|t| t.lifetimes())
455 .find(|lifetime_id| *lifetime_id == *no_longer_needed_lifetime_id)
Lukasz Anforowicz55673c92022-01-27 19:37:26 +0000456 {
457 bail!(
458 "The lifetime of `__this` is unexpectedly also used by another \
459 parameter {:?} in function {:?}",
460 type_still_dependent_on_removed_lifetime,
461 func.name
462 );
Lukasz Anforowicz95551272022-01-20 00:02:24 +0000463 }
464
465 // Rebind `maybe_first_api_param` to the next param after `__this`.
Lukasz Anforowiczf7bdd392022-01-21 00:33:39 +0000466 maybe_first_api_param = param_type_kinds.get(1);
Lukasz Anforowiczab65e292022-01-14 23:04:21 +0000467 }
468
469 // Change `__this: &'a SomeStruct` into `&'a self` if needed.
470 if format_first_param_as_self {
471 let first_api_param = maybe_first_api_param
472 .ok_or_else(|| anyhow!("No parameter to format as 'self': {:?}", func))?;
Lukasz Anforowiczcde4b1b2022-02-03 21:20:55 +0000473 let self_decl =
474 first_api_param.format_as_self_param(func, ir, &lifetime_to_name).with_context(
475 || format!("Failed to format as `self` param: {:?}", first_api_param),
476 )?;
Lukasz Anforowiczf9564622022-01-28 14:31:04 +0000477 // Presence of element #0 is verified by `ok_or_else` on
478 // `maybe_first_api_param` above.
479 api_params[0] = self_decl;
480 thunk_args[0] = quote! { self };
Lukasz Anforowiczab65e292022-01-14 23:04:21 +0000481 }
482
483 let func_body = match &func.name {
Devin Jeanpierre55298ac2022-02-24 01:37:37 +0000484 UnqualifiedIdentifier::Identifier(_) | UnqualifiedIdentifier::Operator(_) | UnqualifiedIdentifier::Destructor => {
Lukasz Anforowiczf7bdd392022-01-21 00:33:39 +0000485 let mut body = quote! { crate::detail::#thunk_ident( #( #thunk_args ),* ) };
486 // Only need to wrap everything in an `unsafe { ... }` block if
487 // the *whole* api function is safe.
488 if !is_unsafe {
489 body = quote! { unsafe { #body } };
490 }
491 body
492 }
Lukasz Anforowiczab65e292022-01-14 23:04:21 +0000493 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.
Devin Jeanpierre55298ac2022-02-24 01:37:37 +0000523 assert!(!is_unsafe);
Lukasz Anforowicz55673c92022-01-27 19:37:26 +0000524 (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(|| {
Devin Jeanpierre149950d2022-02-22 21:02:02 +0000580 format!("Failed to format `__this` param for a constructor thunk: {:?}", func.params[0])
581 })?;
582 } else if func.name == UnqualifiedIdentifier::Destructor {
583 if param_types.is_empty() || func.params.is_empty() {
584 bail!("Destructors should have at least one parameter (__this)");
585 }
586 param_types[0] = param_type_kinds[0]
587 .format_ref_as_raw_ptr(ir, &lifetime_to_name)
588 .with_context(|| {
589 format!("Failed to format `__this` param for a destructor thunk: {:?}", func.params[0])
Lukasz Anforowicz231a3bb2022-01-12 14:05:59 +0000590 })?;
Lukasz Anforowicz40c2eb82022-01-11 18:22:31 +0000591 }
592
Lukasz Anforowicz95551272022-01-20 00:02:24 +0000593 let lifetimes = func.lifetime_params.iter().map(|l| format_lifetime_name(&l.name));
594 let generic_params = format_generic_params(lifetimes);
595
Devin Jeanpierre7e9a1de2021-12-03 08:04:22 +0000596 quote! {
597 #thunk_attr
Lukasz Anforowicz4ad012b2021-12-15 18:13:40 +0000598 pub(crate) fn #thunk_ident #generic_params( #( #param_idents: #param_types ),*
599 ) #return_type_fragment ;
Devin Jeanpierre7e9a1de2021-12-03 08:04:22 +0000600 }
Michael Forstered642022021-10-04 09:48:25 +0000601 };
602
Lukasz Anforowiczeb19ac62022-02-05 00:10:16 +0000603 Ok(GeneratedFunc::Some { api_func: api_func.into(), thunk: thunk.into(), function_id })
Michael Forstered642022021-10-04 09:48:25 +0000604}
605
Michael Forstercc5941a2021-10-07 07:12:24 +0000606fn generate_doc_comment(comment: &Option<String>) -> TokenStream {
607 match comment {
Michael Forster028800b2021-10-05 12:39:59 +0000608 Some(text) => {
Marcel Hlopko89547752021-12-10 09:39:41 +0000609 // token_stream_printer (and rustfmt) don't put a space between /// and the doc
610 // comment, let's add it here so our comments are pretty.
Lukasz Anforowiczdd9ae0f2022-02-17 15:52:53 +0000611 let doc = format!(" {}", text.replace('\n', "\n "));
Michael Forster028800b2021-10-05 12:39:59 +0000612 quote! {#[doc=#doc]}
613 }
614 None => quote! {},
Michael Forstercc5941a2021-10-07 07:12:24 +0000615 }
616}
Lukasz Anforowiczdaff0402021-12-23 00:37:50 +0000617
618fn format_generic_params<T: quote::ToTokens>(params: impl IntoIterator<Item = T>) -> TokenStream {
619 let mut params = params.into_iter().peekable();
620 if params.peek().is_none() {
621 quote! {}
622 } else {
623 quote! { < #( #params ),* > }
624 }
625}
626
Lukasz Anforowicze57215c2022-01-12 14:54:16 +0000627fn should_implement_drop(record: &Record) -> bool {
628 match record.destructor.definition {
629 // TODO(b/202258760): Only omit destructor if `Copy` is specified.
630 SpecialMemberDefinition::Trivial => false,
631
632 // TODO(b/212690698): Avoid calling into the C++ destructor (e.g. let
633 // Rust drive `drop`-ing) to avoid (somewhat unergonomic) ManuallyDrop
634 // if we can ask Rust to preserve C++ field destruction order in
635 // NontrivialMembers case.
636 SpecialMemberDefinition::NontrivialMembers => true,
637
638 // The `impl Drop` for NontrivialUserDefined needs to call into the
639 // user-defined destructor on C++ side.
640 SpecialMemberDefinition::NontrivialUserDefined => true,
641
642 // TODO(b/213516512): Today the IR doesn't contain Func entries for
643 // deleted functions/destructors/etc. But, maybe we should generate
644 // `impl Drop` in this case? With `unreachable!`? With
645 // `std::mem::forget`?
646 SpecialMemberDefinition::Deleted => false,
647 }
648}
649
650/// Returns whether fields of type `ty` need to be wrapped in `ManuallyDrop<T>`
651/// to prevent the fields from being destructed twice (once by the C++
652/// destructor calkled from the `impl Drop` of the struct and once by `drop` on
653/// the Rust side).
654///
655/// A type is safe to destroy twice if it implements `Copy`. Fields of such
656/// don't need to be wrapped in `ManuallyDrop<T>` even if the struct
657/// containing the fields provides an `impl Drop` that calles into a C++
658/// destructor (in addition to dropping the fields on the Rust side).
659///
660/// Note that it is not enough to just be `!needs_drop<T>()`: Rust only
661/// guarantees that it is safe to use-after-destroy for `Copy` types. See
662/// e.g. the documentation for
663/// [`drop_in_place`](https://doc.rust-lang.org/std/ptr/fn.drop_in_place.html):
664///
665/// > if `T` is not `Copy`, using the pointed-to value after calling
666/// > `drop_in_place` can cause undefined behavior
667fn needs_manually_drop(ty: &ir::RsType, ir: &IR) -> Result<bool> {
668 let ty_implements_copy = RsTypeKind::new(ty, ir)?.implements_copy();
669 Ok(!ty_implements_copy)
670}
671
Michael Forsterbee84482021-10-13 08:35:38 +0000672/// Generates Rust source code for a given `Record` and associated assertions as
673/// a tuple.
Marcel Hlopkoc0956cf2021-11-29 08:31:28 +0000674fn generate_record(record: &Record, ir: &IR) -> Result<(RsSnippet, RsSnippet)> {
Marcel Hlopkoeaae9b72022-01-21 15:54:11 +0000675 let ident = make_rs_ident(&record.identifier.identifier);
Michael Forstercc5941a2021-10-07 07:12:24 +0000676 let doc_comment = generate_doc_comment(&record.doc_comment);
Marcel Hlopkob4b28742021-09-15 12:45:20 +0000677 let field_idents =
Marcel Hlopkoeaae9b72022-01-21 15:54:11 +0000678 record.fields.iter().map(|f| make_rs_ident(&f.identifier.identifier)).collect_vec();
Michael Forstercc5941a2021-10-07 07:12:24 +0000679 let field_doc_coments =
680 record.fields.iter().map(|f| generate_doc_comment(&f.doc_comment)).collect_vec();
Devin Jeanpierre09c6f452021-09-29 07:34:24 +0000681 let field_types = record
682 .fields
683 .iter()
Devin Jeanpierreb69bcae2022-02-03 09:45:50 +0000684 .enumerate()
685 .map(|(i, f)| {
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +0000686 // [[no_unique_address]] fields are replaced by an unaligned block of memory
687 // which fills space up to the next field.
Devin Jeanpierreb69bcae2022-02-03 09:45:50 +0000688 // See: docs/struct_layout
689 if f.is_no_unique_address {
690 let next_offset = if let Some(next) = record.fields.get(i + 1) {
691 next.offset
692 } else {
693 record.size * 8
694 };
695 let width = Literal::usize_unsuffixed((next_offset - f.offset) / 8);
696 return Ok(quote! {[std::mem::MaybeUninit<u8>; #width]});
697 }
Lukasz Anforowicze57215c2022-01-12 14:54:16 +0000698 let mut formatted = format_rs_type(&f.type_.rs_type, ir, &HashMap::new())
699 .with_context(|| {
Googlerb7e361d2022-01-04 14:02:59 +0000700 format!("Failed to format type for field {:?} on record {:?}", f, record)
701 })?;
Lukasz Anforowicze57215c2022-01-12 14:54:16 +0000702 // TODO(b/212696226): Verify cases where ManuallyDrop<T> is skipped
703 // via static asserts in the generated code.
704 if should_implement_drop(record) && needs_manually_drop(&f.type_.rs_type, ir)? {
705 // TODO(b/212690698): Avoid (somewhat unergonomic) ManuallyDrop
706 // if we can ask Rust to preserve field destruction order if the
707 // destructor is the SpecialMemberDefinition::NontrivialMembers
708 // case.
709 formatted = quote! { std::mem::ManuallyDrop<#formatted> }
Lukasz Anforowicz6d553632022-01-06 21:36:14 +0000710 };
Devin Jeanpierre7e9a1de2021-12-03 08:04:22 +0000711 Ok(formatted)
712 })
Devin Jeanpierre09c6f452021-09-29 07:34:24 +0000713 .collect::<Result<Vec<_>>>()?;
Googlerec589eb2021-09-17 07:45:39 +0000714 let field_accesses = record
715 .fields
716 .iter()
717 .map(|f| {
Devin Jeanpierreb69bcae2022-02-03 09:45:50 +0000718 if f.access == AccessSpecifier::Public && !f.is_no_unique_address {
Googlerec589eb2021-09-17 07:45:39 +0000719 quote! { pub }
720 } else {
721 quote! {}
722 }
723 })
724 .collect_vec();
Googlerec648ff2021-09-23 07:19:53 +0000725 let size = record.size;
726 let alignment = record.alignment;
Googleraaa0a532021-10-01 09:11:27 +0000727 let field_assertions =
728 record.fields.iter().zip(field_idents.iter()).map(|(field, field_ident)| {
729 let offset = field.offset;
730 quote! {
731 // The IR contains the offset in bits, while offset_of!()
732 // returns the offset in bytes, so we need to convert.
Googler209b10a2021-12-06 09:11:57 +0000733 const _: () = assert!(offset_of!(#ident, #field_ident) * 8 == #offset);
Googleraaa0a532021-10-01 09:11:27 +0000734 }
735 });
Michael Forsterdb8101a2021-10-08 06:56:03 +0000736 let mut record_features = BTreeSet::new();
737 let mut assertion_features = BTreeSet::new();
Devin Jeanpierre273eeae2021-10-06 13:29:35 +0000738
739 // TODO(mboehme): For the time being, we're using unstable features to
740 // be able to use offset_of!() in static assertions. This is fine for a
741 // prototype, but longer-term we want to either get those features
742 // stabilized or find an alternative. For more details, see
743 // b/200120034#comment15
Marcel Hlopkoeaae9b72022-01-21 15:54:11 +0000744 assertion_features.insert(make_rs_ident("const_ptr_offset_from"));
Devin Jeanpierre2ed14ec2021-10-06 11:32:19 +0000745
Lukasz Anforowicze57215c2022-01-12 14:54:16 +0000746 let derives = generate_derives(record);
Devin Jeanpierre9227d2c2021-10-06 12:26:05 +0000747 let derives = if derives.is_empty() {
748 quote! {}
749 } else {
750 quote! {#[derive( #(#derives),* )]}
751 };
Lukasz Anforowiczdd9ae0f2022-02-17 15:52:53 +0000752 let unpin_impl = if record.is_unpin() {
753 quote! {}
Devin Jeanpierreea700d32021-10-06 11:33:56 +0000754 } else {
Michael Forsterbee84482021-10-13 08:35:38 +0000755 // negative_impls are necessary for universal initialization due to Rust's
756 // coherence rules: PhantomPinned isn't enough to prove to Rust that a
757 // blanket impl that requires Unpin doesn't apply. See http://<internal link>=h.f6jp8ifzgt3n
Marcel Hlopkoeaae9b72022-01-21 15:54:11 +0000758 record_features.insert(make_rs_ident("negative_impls"));
Lukasz Anforowiczdd9ae0f2022-02-17 15:52:53 +0000759 quote! {
Michael Forsterdb8101a2021-10-08 06:56:03 +0000760 __NEWLINE__ __NEWLINE__
761 impl !Unpin for #ident {}
Lukasz Anforowiczdd9ae0f2022-02-17 15:52:53 +0000762 }
763 };
Devin Jeanpierre273eeae2021-10-06 13:29:35 +0000764
Devin Jeanpierrec80e6242022-02-03 01:56:40 +0000765 let mut repr_attributes = vec![quote! {C}];
766 if record.override_alignment && record.alignment > 1 {
767 let alignment = Literal::usize_unsuffixed(record.alignment);
768 repr_attributes.push(quote! {align(#alignment)});
769 }
770
771 // Adjust the struct to also include base class subobjects. We use an opaque
772 // field because subobjects can live in the alignment of base class
773 // subobjects.
774 let base_subobjects_field = if let Some(base_size) = record.base_size {
775 let n = proc_macro2::Literal::usize_unsuffixed(base_size);
776 quote! {
777 __base_class_subobjects: [std::mem::MaybeUninit<u8>; #n],
778 }
779 } else {
780 quote! {}
781 };
782
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +0000783 let empty_struct_placeholder_field =
784 if record.fields.is_empty() && record.base_size.unwrap_or(0) == 0 {
785 quote! {
786 /// Prevent empty C++ struct being zero-size in Rust.
787 placeholder: std::mem::MaybeUninit<u8>,
788 }
789 } else {
790 quote! {}
791 };
Googlerf4792062021-10-20 07:21:21 +0000792
Devin Jeanpierre58181ac2022-02-14 21:30:05 +0000793 let no_unique_address_accessors = cc_struct_no_unique_address_impl(record, ir)?;
Devin Jeanpierre56777022022-02-03 01:57:15 +0000794 let base_class_into = cc_struct_upcast_impl(record, ir)?;
795
Michael Forsterdb8101a2021-10-08 06:56:03 +0000796 let record_tokens = quote! {
Michael Forster028800b2021-10-05 12:39:59 +0000797 #doc_comment
Devin Jeanpierre9227d2c2021-10-06 12:26:05 +0000798 #derives
Devin Jeanpierrec80e6242022-02-03 01:56:40 +0000799 #[repr(#( #repr_attributes ),*)]
Marcel Hlopkob4b28742021-09-15 12:45:20 +0000800 pub struct #ident {
Devin Jeanpierrec80e6242022-02-03 01:56:40 +0000801 #base_subobjects_field
Michael Forstercc5941a2021-10-07 07:12:24 +0000802 #( #field_doc_coments #field_accesses #field_idents: #field_types, )*
Googlerf4792062021-10-20 07:21:21 +0000803 #empty_struct_placeholder_field
Marcel Hlopkob4b28742021-09-15 12:45:20 +0000804 }
Googlerec648ff2021-09-23 07:19:53 +0000805
Devin Jeanpierre58181ac2022-02-14 21:30:05 +0000806 #no_unique_address_accessors
807
Devin Jeanpierre56777022022-02-03 01:57:15 +0000808 #base_class_into
809
Devin Jeanpierreea700d32021-10-06 11:33:56 +0000810 #unpin_impl
Devin Jeanpierre273eeae2021-10-06 13:29:35 +0000811 };
812
Michael Forsterdb8101a2021-10-08 06:56:03 +0000813 let assertion_tokens = quote! {
Googler209b10a2021-12-06 09:11:57 +0000814 const _: () = assert!(std::mem::size_of::<#ident>() == #size);
815 const _: () = assert!(std::mem::align_of::<#ident>() == #alignment);
Michael Forsterdb8101a2021-10-08 06:56:03 +0000816 #( #field_assertions )*
817 };
818
819 Ok((
820 RsSnippet { features: record_features, tokens: record_tokens },
821 RsSnippet { features: assertion_features, tokens: assertion_tokens },
822 ))
Marcel Hlopkob4b28742021-09-15 12:45:20 +0000823}
824
Lukasz Anforowicz2e41bb62022-01-11 18:23:07 +0000825fn should_derive_clone(record: &Record) -> bool {
Devin Jeanpierre88343c72022-01-15 01:10:23 +0000826 record.is_unpin()
Devin Jeanpierre2ed14ec2021-10-06 11:32:19 +0000827 && record.copy_constructor.access == ir::AccessSpecifier::Public
828 && record.copy_constructor.definition == SpecialMemberDefinition::Trivial
Lukasz Anforowicz2e41bb62022-01-11 18:23:07 +0000829}
830
Lukasz Anforowicze57215c2022-01-12 14:54:16 +0000831fn should_derive_copy(record: &Record) -> bool {
832 // TODO(b/202258760): Make `Copy` inclusion configurable.
833 should_derive_clone(record)
834}
835
836fn generate_derives(record: &Record) -> Vec<Ident> {
837 let mut derives = vec![];
Lukasz Anforowicz2e41bb62022-01-11 18:23:07 +0000838 if should_derive_clone(record) {
Marcel Hlopkoeaae9b72022-01-21 15:54:11 +0000839 derives.push(make_rs_ident("Clone"));
Devin Jeanpierre2ed14ec2021-10-06 11:32:19 +0000840 }
Lukasz Anforowicze57215c2022-01-12 14:54:16 +0000841 if should_derive_copy(record) {
Marcel Hlopkoeaae9b72022-01-21 15:54:11 +0000842 derives.push(make_rs_ident("Copy"));
Lukasz Anforowicze57215c2022-01-12 14:54:16 +0000843 }
844 derives
Devin Jeanpierre2ed14ec2021-10-06 11:32:19 +0000845}
846
Teddy Katz76fa42b2022-02-23 01:22:56 +0000847fn generate_enum(enum_: &Enum, ir: &IR) -> Result<TokenStream> {
848 let name = make_rs_ident(&enum_.identifier.identifier);
849 let underlying_type = format_rs_type(&enum_.underlying_type.rs_type, ir, &HashMap::new())?;
850 let enumerator_names =
851 enum_.enumerators.iter().map(|enumerator| make_rs_ident(&enumerator.identifier.identifier));
852 let enumerator_values = enum_.enumerators.iter().map(|enumerator| enumerator.value);
853 Ok(quote! {
854 #[repr(transparent)]
855 #[derive(Debug, PartialEq, Eq, Copy, Clone, Hash, PartialOrd, Ord)]
856 pub struct #name(#underlying_type);
857 impl #name {
858 #(pub const #enumerator_names: #name = #name(#enumerator_values);)*
859 }
860 impl From<#underlying_type> for #name {
861 fn from(value: #underlying_type) -> #name {
862 #name(v)
863 }
864 }
865 impl From<#name> for #underlying_type {
866 fn from(value: #name) -> #underlying_type {
867 v.0
868 }
869 }
870 })
871}
872
Googler6a0a5252022-01-11 14:08:09 +0000873fn generate_type_alias(type_alias: &TypeAlias, ir: &IR) -> Result<TokenStream> {
Marcel Hlopkoeaae9b72022-01-21 15:54:11 +0000874 let ident = make_rs_ident(&type_alias.identifier.identifier);
Googler6a0a5252022-01-11 14:08:09 +0000875 let underlying_type = format_rs_type(&type_alias.underlying_type.rs_type, ir, &HashMap::new())
876 .with_context(|| format!("Failed to format underlying type for {:?}", type_alias))?;
877 Ok(quote! {pub type #ident = #underlying_type;})
878}
879
Michael Forster523dbd42021-10-12 11:05:44 +0000880/// Generates Rust source code for a given `UnsupportedItem`.
881fn generate_unsupported(item: &UnsupportedItem) -> Result<TokenStream> {
Googler48a74dd2021-10-25 07:31:53 +0000882 let location = if item.source_loc.filename.is_empty() {
883 "<unknown location>".to_string()
884 } else {
885 // TODO(forster): The "google3" prefix should probably come from a command line
886 // argument.
887 // TODO(forster): Consider linking to the symbol instead of to the line number
888 // to avoid wrong links while generated files have not caught up.
889 format!("google3/{};l={}", &item.source_loc.filename, &item.source_loc.line)
890 };
Michael Forster6a184ad2021-10-12 13:04:05 +0000891 let message = format!(
Googler48a74dd2021-10-25 07:31:53 +0000892 "{}\nError while generating bindings for item '{}':\n{}",
893 &location, &item.name, &item.message
Michael Forster6a184ad2021-10-12 13:04:05 +0000894 );
Michael Forster523dbd42021-10-12 11:05:44 +0000895 Ok(quote! { __COMMENT__ #message })
896}
897
Michael Forsterf1dce422021-10-13 09:50:16 +0000898/// Generates Rust source code for a given `Comment`.
899fn generate_comment(comment: &Comment) -> Result<TokenStream> {
900 let text = &comment.text;
901 Ok(quote! { __COMMENT__ #text })
902}
903
Marcel Hlopko89547752021-12-10 09:39:41 +0000904fn generate_rs_api(ir: &IR) -> Result<TokenStream> {
Michael Forstered642022021-10-04 09:48:25 +0000905 let mut items = vec![];
Marcel Hlopko42abfc82021-08-09 07:03:17 +0000906 let mut thunks = vec![];
Michael Forsterdb8101a2021-10-08 06:56:03 +0000907 let mut assertions = vec![];
Marcel Hlopko42abfc82021-08-09 07:03:17 +0000908
Googler454f2652021-12-06 12:53:12 +0000909 // We import nullable pointers as an Option<&T> and assume that at the ABI
910 // level, None is represented as a zero pointer value whereas Some is
911 // represented as as non-zero pointer value. This seems like a pretty safe
912 // assumption to make, but to provide some safeguard, assert that
913 // `Option<&i32>` and `&i32` have the same size.
914 assertions.push(quote! {
915 const _: () = assert!(std::mem::size_of::<Option<&i32>>() == std::mem::size_of::<&i32>());
916 });
917
Michael Forsterbee84482021-10-13 08:35:38 +0000918 // TODO(jeanpierreda): Delete has_record, either in favor of using RsSnippet, or not
919 // having uses. See https://chat.google.com/room/AAAAnQmj8Qs/6QbkSvWcfhA
Devin Jeanpierreea700d32021-10-06 11:33:56 +0000920 let mut has_record = false;
Devin Jeanpierre273eeae2021-10-06 13:29:35 +0000921 let mut features = BTreeSet::new();
Marcel Hlopko42abfc82021-08-09 07:03:17 +0000922
Lukasz Anforowicz72c4d222022-02-18 19:07:28 +0000923 // For #![rustfmt::skip].
924 features.insert(make_rs_ident("custom_inner_attributes"));
925
Googlerd03d05b2022-01-07 10:10:57 +0000926 // Identify all functions having overloads that we can't import (yet).
927 // TODO(b/213280424): Implement support for overloaded functions.
928 let mut seen_funcs = HashSet::new();
929 let mut overloaded_funcs = HashSet::new();
930 for func in ir.functions() {
Lukasz Anforowiczeb19ac62022-02-05 00:10:16 +0000931 if let GeneratedFunc::Some { function_id, .. } = generate_func(func, ir)? {
Googlerd03d05b2022-01-07 10:10:57 +0000932 if !seen_funcs.insert(function_id.clone()) {
933 overloaded_funcs.insert(function_id);
934 }
935 }
936 }
937
Marcel Hlopko3b9bf9e2021-11-29 08:25:14 +0000938 for item in ir.items() {
Michael Forstered642022021-10-04 09:48:25 +0000939 match item {
Lukasz Anforowiczeb19ac62022-02-05 00:10:16 +0000940 Item::Func(func) => match generate_func(func, ir)? {
941 GeneratedFunc::None => (),
942 GeneratedFunc::Unsupported(unsupported) => {
943 items.push(generate_unsupported(&unsupported)?)
944 }
945 GeneratedFunc::Some { api_func, thunk, function_id } => {
Googlerd03d05b2022-01-07 10:10:57 +0000946 if overloaded_funcs.contains(&function_id) {
Lukasz Anforowiczeb19ac62022-02-05 00:10:16 +0000947 items.push(generate_unsupported(&make_unsupported_fn(
948 func,
949 ir,
950 "Cannot generate bindings for overloaded function",
951 )?)?);
Googlerd03d05b2022-01-07 10:10:57 +0000952 continue;
953 }
Lukasz Anforowiczeb19ac62022-02-05 00:10:16 +0000954 features.extend(api_func.features);
Googlerd03d05b2022-01-07 10:10:57 +0000955 features.extend(thunk.features);
Lukasz Anforowiczeb19ac62022-02-05 00:10:16 +0000956 items.push(api_func.tokens);
Googlerd03d05b2022-01-07 10:10:57 +0000957 thunks.push(thunk.tokens);
958 }
Lukasz Anforowiczeb19ac62022-02-05 00:10:16 +0000959 },
Michael Forstered642022021-10-04 09:48:25 +0000960 Item::Record(record) => {
Googler6a0a5252022-01-11 14:08:09 +0000961 if !ir.is_current_target(&record.owning_target)
962 && !ir.is_stdlib_target(&record.owning_target)
963 {
Marcel Hlopkoa0f38662021-12-03 08:45:26 +0000964 continue;
965 }
Marcel Hlopkoc0956cf2021-11-29 08:31:28 +0000966 let (snippet, assertions_snippet) = generate_record(record, ir)?;
Devin Jeanpierre273eeae2021-10-06 13:29:35 +0000967 features.extend(snippet.features);
Michael Forsterdb8101a2021-10-08 06:56:03 +0000968 features.extend(assertions_snippet.features);
Devin Jeanpierre273eeae2021-10-06 13:29:35 +0000969 items.push(snippet.tokens);
Michael Forsterdb8101a2021-10-08 06:56:03 +0000970 assertions.push(assertions_snippet.tokens);
Devin Jeanpierreea700d32021-10-06 11:33:56 +0000971 has_record = true;
Michael Forstered642022021-10-04 09:48:25 +0000972 }
Teddy Katz76fa42b2022-02-23 01:22:56 +0000973 Item::Enum(enum_) => {
974 if !ir.is_current_target(&enum_.owning_target)
975 && !ir.is_stdlib_target(&enum_.owning_target)
976 {
977 continue;
978 }
979 items.push(generate_enum(enum_, ir)?);
980 continue;
981 }
Googler098c4582022-01-10 12:29:34 +0000982 Item::TypeAlias(type_alias) => {
Googler6a0a5252022-01-11 14:08:09 +0000983 if !ir.is_current_target(&type_alias.owning_target)
984 && !ir.is_stdlib_target(&type_alias.owning_target)
985 {
986 continue;
987 }
988 items.push(generate_type_alias(type_alias, ir)?);
Googler098c4582022-01-10 12:29:34 +0000989 }
Michael Forster523dbd42021-10-12 11:05:44 +0000990 Item::UnsupportedItem(unsupported) => items.push(generate_unsupported(unsupported)?),
Michael Forsterf1dce422021-10-13 09:50:16 +0000991 Item::Comment(comment) => items.push(generate_comment(comment)?),
Michael Forstered642022021-10-04 09:48:25 +0000992 }
Marcel Hlopko42abfc82021-08-09 07:03:17 +0000993 }
994
Marcel Hlopkob4b28742021-09-15 12:45:20 +0000995 let mod_detail = if thunks.is_empty() {
996 quote! {}
997 } else {
998 quote! {
999 mod detail {
Googler55647142022-01-11 12:37:39 +00001000 #[allow(unused_imports)]
Devin Jeanpierred4dde0e2021-10-13 20:48:25 +00001001 use super::*;
Marcel Hlopkob4b28742021-09-15 12:45:20 +00001002 extern "C" {
1003 #( #thunks )*
1004 }
Marcel Hlopko42abfc82021-08-09 07:03:17 +00001005 }
1006 }
1007 };
1008
Devin Jeanpierreea700d32021-10-06 11:33:56 +00001009 let imports = if has_record {
Googlerec648ff2021-09-23 07:19:53 +00001010 quote! {
Googleraaa0a532021-10-01 09:11:27 +00001011 use memoffset_unstable_const::offset_of;
Googlerec648ff2021-09-23 07:19:53 +00001012 }
Michael Forstered642022021-10-04 09:48:25 +00001013 } else {
1014 quote! {}
Googlerec648ff2021-09-23 07:19:53 +00001015 };
1016
Devin Jeanpierre273eeae2021-10-06 13:29:35 +00001017 let features = if features.is_empty() {
1018 quote! {}
1019 } else {
1020 quote! {
1021 #![feature( #(#features),* )]
1022 }
1023 };
1024
Marcel Hlopko89547752021-12-10 09:39:41 +00001025 Ok(quote! {
Googler55647142022-01-11 12:37:39 +00001026 #features __NEWLINE__
1027 #![allow(non_camel_case_types)] __NEWLINE__
1028 #![allow(non_snake_case)] __NEWLINE__ __NEWLINE__
1029
Michael Forsterdb8101a2021-10-08 06:56:03 +00001030 #imports __NEWLINE__ __NEWLINE__
Googlerec648ff2021-09-23 07:19:53 +00001031
Michael Forsterdb8101a2021-10-08 06:56:03 +00001032 #( #items __NEWLINE__ __NEWLINE__ )*
Marcel Hlopkob4b28742021-09-15 12:45:20 +00001033
Michael Forsterdb8101a2021-10-08 06:56:03 +00001034 #mod_detail __NEWLINE__ __NEWLINE__
1035
1036 #( #assertions __NEWLINE__ __NEWLINE__ )*
Marcel Hlopko89547752021-12-10 09:39:41 +00001037 })
Marcel Hlopko42abfc82021-08-09 07:03:17 +00001038}
1039
Marcel Hlopkoeaae9b72022-01-21 15:54:11 +00001040/// Makes an 'Ident' to be used in the Rust source code. Escapes Rust keywords.
1041fn make_rs_ident(ident: &str) -> Ident {
1042 // TODO(https://github.com/dtolnay/syn/pull/1098): Remove the hardcoded list once syn recognizes
1043 // 2018 and 2021 keywords.
1044 if ["async", "await", "try", "dyn"].contains(&ident) {
1045 return format_ident!("r#{}", ident);
1046 }
1047 match syn::parse_str::<syn::Ident>(ident) {
1048 Ok(_) => format_ident!("{}", ident),
1049 Err(_) => format_ident!("r#{}", ident),
1050 }
1051}
1052
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +00001053/// Formats a C++ identifier. Does not escape C++ keywords.
1054fn format_cc_ident(ident: &str) -> TokenStream {
1055 ident.parse().unwrap()
Marcel Hlopko42abfc82021-08-09 07:03:17 +00001056}
1057
Googler6a0a5252022-01-11 14:08:09 +00001058fn rs_type_name_for_target_and_identifier(
1059 owning_target: &BlazeLabel,
1060 identifier: &ir::Identifier,
1061 ir: &IR,
1062) -> Result<TokenStream> {
Marcel Hlopkoeaae9b72022-01-21 15:54:11 +00001063 let ident = make_rs_ident(identifier.identifier.as_str());
Googler6a0a5252022-01-11 14:08:09 +00001064
1065 if ir.is_current_target(owning_target) || ir.is_stdlib_target(owning_target) {
1066 Ok(quote! {#ident})
1067 } else {
Marcel Hlopkod906b892022-01-27 08:52:36 +00001068 let owning_crate_name = owning_target.target_name()?;
1069 // TODO(b/216587072): Remove this hacky escaping and use the import! macro once
1070 // available
Lukasz Anforowiczdd9ae0f2022-02-17 15:52:53 +00001071 let escaped_owning_crate_name = owning_crate_name.replace('-', "_");
Marcel Hlopkod906b892022-01-27 08:52:36 +00001072 let owning_crate = make_rs_ident(&escaped_owning_crate_name);
Googler6a0a5252022-01-11 14:08:09 +00001073 Ok(quote! {#owning_crate::#ident})
1074 }
1075}
1076
Lukasz Anforowicz40c2eb82022-01-11 18:22:31 +00001077#[derive(Debug, Eq, PartialEq)]
1078enum Mutability {
1079 Const,
1080 Mut,
1081}
1082
1083impl Mutability {
1084 fn format_for_pointer(&self) -> TokenStream {
1085 match self {
1086 Mutability::Mut => quote! {mut},
1087 Mutability::Const => quote! {const},
1088 }
1089 }
1090
1091 fn format_for_reference(&self) -> TokenStream {
1092 match self {
1093 Mutability::Mut => quote! {mut},
1094 Mutability::Const => quote! {},
1095 }
1096 }
1097}
1098
1099// TODO(b/213947473): Instead of having a separate RsTypeKind here, consider
1100// changing ir::RsType into a similar `enum`, with fields that contain
1101// references (e.g. &'ir Record`) instead of DeclIds.
1102#[derive(Debug)]
1103enum RsTypeKind<'ir> {
1104 Pointer { pointee: Box<RsTypeKind<'ir>>, mutability: Mutability },
1105 Reference { referent: Box<RsTypeKind<'ir>>, mutability: Mutability, lifetime_id: LifetimeId },
Lukasz Anforowiczcf230fd2022-02-18 19:20:39 +00001106 FuncPtr { abi: &'ir str, return_type: Box<RsTypeKind<'ir>>, param_types: Vec<RsTypeKind<'ir>> },
Lukasz Anforowicz40c2eb82022-01-11 18:22:31 +00001107 Record(&'ir Record),
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00001108 TypeAlias { type_alias: &'ir TypeAlias, underlying_type: Box<RsTypeKind<'ir>> },
Lukasz Anforowicz40c2eb82022-01-11 18:22:31 +00001109 Unit,
1110 Other { name: &'ir str, type_args: Vec<RsTypeKind<'ir>> },
1111}
1112
1113impl<'ir> RsTypeKind<'ir> {
1114 pub fn new(ty: &'ir ir::RsType, ir: &'ir IR) -> Result<Self> {
1115 // The lambdas deduplicate code needed by multiple `match` branches.
1116 let get_type_args = || -> Result<Vec<RsTypeKind<'ir>>> {
1117 ty.type_args.iter().map(|type_arg| RsTypeKind::<'ir>::new(type_arg, ir)).collect()
1118 };
1119 let get_pointee = || -> Result<Box<RsTypeKind<'ir>>> {
1120 if ty.type_args.len() != 1 {
1121 bail!("Missing pointee/referent type (need exactly 1 type argument): {:?}", ty);
1122 }
1123 Ok(Box::new(get_type_args()?.remove(0)))
1124 };
1125 let get_lifetime = || -> Result<LifetimeId> {
1126 if ty.lifetime_args.len() != 1 {
1127 bail!("Missing reference lifetime (need exactly 1 lifetime argument): {:?}", ty);
1128 }
1129 Ok(ty.lifetime_args[0])
1130 };
1131
1132 let result = match ty.name.as_deref() {
1133 None => {
1134 ensure!(
1135 ty.type_args.is_empty(),
1136 "Type arguments on records nor type aliases are not yet supported: {:?}",
1137 ty
1138 );
1139 match ir.item_for_type(ty)? {
1140 Item::Record(record) => RsTypeKind::Record(record),
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00001141 Item::TypeAlias(type_alias) => RsTypeKind::TypeAlias {
1142 type_alias,
1143 underlying_type: Box::new(RsTypeKind::new(
1144 &type_alias.underlying_type.rs_type,
1145 ir,
1146 )?),
1147 },
Lukasz Anforowicz40c2eb82022-01-11 18:22:31 +00001148 other_item => bail!("Item does not define a type: {:?}", other_item),
1149 }
1150 }
1151 Some(name) => match name {
1152 "()" => {
1153 if !ty.type_args.is_empty() {
1154 bail!("Unit type must not have type arguments: {:?}", ty);
1155 }
1156 RsTypeKind::Unit
1157 }
1158 "*mut" => {
1159 RsTypeKind::Pointer { pointee: get_pointee()?, mutability: Mutability::Mut }
1160 }
1161 "*const" => {
1162 RsTypeKind::Pointer { pointee: get_pointee()?, mutability: Mutability::Const }
1163 }
1164 "&mut" => RsTypeKind::Reference {
1165 referent: get_pointee()?,
1166 mutability: Mutability::Mut,
1167 lifetime_id: get_lifetime()?,
1168 },
1169 "&" => RsTypeKind::Reference {
1170 referent: get_pointee()?,
1171 mutability: Mutability::Const,
1172 lifetime_id: get_lifetime()?,
1173 },
Lukasz Anforowiczcf230fd2022-02-18 19:20:39 +00001174 name => {
1175 let mut type_args = get_type_args()?;
1176 match name.strip_prefix("#funcPtr ") {
1177 None => RsTypeKind::Other { name, type_args },
1178 Some(abi) => {
1179 // TODO(b/217419782): Consider enforcing `'static` lifetime.
1180 ensure!(!type_args.is_empty(), "No return type in fn type: {:?}", ty);
1181 RsTypeKind::FuncPtr {
1182 abi,
1183 return_type: Box::new(type_args.remove(type_args.len() - 1)),
1184 param_types: type_args,
1185 }
1186 },
1187 }
1188 },
Lukasz Anforowicz40c2eb82022-01-11 18:22:31 +00001189 },
1190 };
1191 Ok(result)
1192 }
1193
Devin Jeanpierre149950d2022-02-22 21:02:02 +00001194 /// Returns true if the type is known to be `Unpin`, false otherwise.
1195 pub fn is_unpin(&self, ir: &IR) -> bool {
1196 match self {
1197 RsTypeKind::Record(record) => record.is_unpin(),
1198 RsTypeKind::TypeAlias { underlying_type, .. } => underlying_type.is_unpin(ir),
1199 _ => true,
1200 }
1201 }
1202
Lukasz Anforowicz40c2eb82022-01-11 18:22:31 +00001203 pub fn format(
1204 &self,
1205 ir: &IR,
1206 lifetime_to_name: &HashMap<LifetimeId, String>,
1207 ) -> Result<TokenStream> {
1208 let result = match self {
1209 RsTypeKind::Pointer { pointee, mutability } => {
1210 let mutability = mutability.format_for_pointer();
1211 let nested_type = pointee.format(ir, lifetime_to_name)?;
1212 quote! {* #mutability #nested_type}
1213 }
1214 RsTypeKind::Reference { referent, mutability, lifetime_id } => {
Devin Jeanpierre149950d2022-02-22 21:02:02 +00001215 let mut_ = mutability.format_for_reference();
Lukasz Anforowicz40c2eb82022-01-11 18:22:31 +00001216 let lifetime = Self::format_lifetime(lifetime_id, lifetime_to_name)?;
1217 let nested_type = referent.format(ir, lifetime_to_name)?;
Devin Jeanpierre149950d2022-02-22 21:02:02 +00001218 let reference = quote! {& #lifetime #mut_ #nested_type};
1219 if mutability == &Mutability::Mut && !referent.is_unpin(ir) {
1220 // TODO(b/200067242): Add a `use std::pin::Pin` to the crate, and use `Pin`.
1221 // Probably format needs to return an RsSnippet, and RsSnippet needs a `uses`
1222 // field.
1223 quote! {std::pin::Pin< #reference >}
1224 } else {
1225 reference
1226 }
Lukasz Anforowicz40c2eb82022-01-11 18:22:31 +00001227 }
Lukasz Anforowiczcf230fd2022-02-18 19:20:39 +00001228 RsTypeKind::FuncPtr { abi, return_type, param_types } => {
1229 let return_frag = return_type.format_as_return_type_fragment(ir, lifetime_to_name)?;
1230 let param_types = param_types
1231 .iter()
1232 .map(|t| t.format(ir, lifetime_to_name))
1233 .collect::<Result<Vec<_>>>()?;
1234 quote!{ extern #abi fn( #( #param_types ),* ) #return_frag }
1235 },
Lukasz Anforowicz40c2eb82022-01-11 18:22:31 +00001236 RsTypeKind::Record(record) => rs_type_name_for_target_and_identifier(
1237 &record.owning_target,
1238 &record.identifier,
1239 ir,
1240 )?,
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00001241 RsTypeKind::TypeAlias { type_alias, .. } => rs_type_name_for_target_and_identifier(
Lukasz Anforowicz40c2eb82022-01-11 18:22:31 +00001242 &type_alias.owning_target,
1243 &type_alias.identifier,
1244 ir,
1245 )?,
1246 RsTypeKind::Unit => quote! {()},
1247 RsTypeKind::Other { name, type_args } => {
Marcel Hlopkoeaae9b72022-01-21 15:54:11 +00001248 let ident = make_rs_ident(name);
Lukasz Anforowicz40c2eb82022-01-11 18:22:31 +00001249 let generic_params = format_generic_params(
1250 type_args
1251 .iter()
1252 .map(|type_arg| type_arg.format(ir, lifetime_to_name))
1253 .collect::<Result<Vec<_>>>()?,
1254 );
1255 quote! {#ident #generic_params}
1256 }
1257 };
1258 Ok(result)
1259 }
1260
Lukasz Anforowiczcf230fd2022-02-18 19:20:39 +00001261 pub fn format_as_return_type_fragment(
1262 &self,
1263 ir: &IR,
1264 lifetime_to_name: &HashMap<LifetimeId, String>,
1265 ) -> Result<TokenStream> {
1266 match self {
1267 RsTypeKind::Unit => Ok(quote! {}),
1268 other_type => {
1269 let return_type = other_type.format(ir, lifetime_to_name)?;
1270 Ok(quote! { -> #return_type })
1271 }
1272 }
1273 }
1274
Lukasz Anforowiczcde4b1b2022-02-03 21:20:55 +00001275 /// Formats this RsTypeKind as `&'a mut MaybeUninit<SomeStruct>`. This is
1276 /// used to format `__this` parameter in a constructor thunk.
1277 pub fn format_mut_ref_as_uninitialized(
Lukasz Anforowicz40c2eb82022-01-11 18:22:31 +00001278 &self,
1279 ir: &IR,
1280 lifetime_to_name: &HashMap<LifetimeId, String>,
1281 ) -> Result<TokenStream> {
Lukasz Anforowiczcde4b1b2022-02-03 21:20:55 +00001282 match self {
1283 RsTypeKind::Reference { referent, lifetime_id, mutability: Mutability::Mut } => {
1284 let nested_type = referent.format(ir, lifetime_to_name)?;
1285 let lifetime = Self::format_lifetime(lifetime_id, lifetime_to_name)?;
1286 Ok(quote! { & #lifetime mut std::mem::MaybeUninit< #nested_type > })
Lukasz Anforowicz40c2eb82022-01-11 18:22:31 +00001287 }
Devin Jeanpierre149950d2022-02-22 21:02:02 +00001288 _ => bail!("Expected reference to format as MaybeUninit, got: {:?}", self),
Lukasz Anforowiczcde4b1b2022-02-03 21:20:55 +00001289 }
Lukasz Anforowicz40c2eb82022-01-11 18:22:31 +00001290 }
1291
Devin Jeanpierre149950d2022-02-22 21:02:02 +00001292 /// Formats a reference or pointer as a raw pointer.
1293 pub fn format_ref_as_raw_ptr(
1294 &self,
1295 ir: &IR,
1296 lifetime_to_name: &HashMap<LifetimeId, String>,
1297 ) -> Result<TokenStream> {
1298 match self {
1299 RsTypeKind::Reference { referent: pointee, mutability, .. }
1300 | RsTypeKind::Pointer { pointee, mutability } => {
1301 let nested_type = pointee.format(ir, lifetime_to_name)?;
1302 let mut_ = mutability.format_for_pointer();
1303 Ok(quote! { * #mut_ #nested_type })
1304 }
1305 _ => bail!("Expected reference to format as raw ptr, got: {:?}", self),
1306 }
1307 }
1308
1309 /// Formats this RsTypeKind as the `self` parameter: usually, `&'a self` or
1310 /// `&'a mut self`.
1311 ///
1312 /// If this is !Unpin, however, it uses `self: Pin<&mut Self>` instead.
Lukasz Anforowiczcde4b1b2022-02-03 21:20:55 +00001313 pub fn format_as_self_param(
Lukasz Anforowicz231a3bb2022-01-12 14:05:59 +00001314 &self,
Lukasz Anforowiczce345392022-01-14 22:41:16 +00001315 func: &Func,
1316 ir: &IR,
Lukasz Anforowicz231a3bb2022-01-12 14:05:59 +00001317 lifetime_to_name: &HashMap<LifetimeId, String>,
Lukasz Anforowiczf9564622022-01-28 14:31:04 +00001318 ) -> Result<TokenStream> {
Lukasz Anforowicz732ca642022-02-03 20:58:38 +00001319 if func.name == UnqualifiedIdentifier::Destructor {
1320 let record = func
1321 .member_func_metadata
1322 .as_ref()
1323 .ok_or_else(|| anyhow!("Destructors must be member functions: {:?}", func))?
1324 .find_record(ir)?;
1325 if self.is_mut_ptr_to(record) {
1326 // Even in C++ it is UB to retain `this` pointer and dereference it
1327 // after a destructor runs. Therefore it is safe to use `&self` or
1328 // `&mut self` in Rust even if IR represents `__this` as a Rust
1329 // pointer (e.g. when lifetime annotations are missing - lifetime
1330 // annotations are required to represent it as a Rust reference).
1331 return Ok(quote! { &mut self });
1332 }
Lukasz Anforowicz231a3bb2022-01-12 14:05:59 +00001333 }
1334
1335 match self {
Devin Jeanpierre149950d2022-02-22 21:02:02 +00001336 RsTypeKind::Reference { referent, lifetime_id, mutability } => {
1337 let mut_ = mutability.format_for_reference();
Lukasz Anforowicz231a3bb2022-01-12 14:05:59 +00001338 let lifetime = Self::format_lifetime(lifetime_id, lifetime_to_name)?;
Devin Jeanpierre149950d2022-02-22 21:02:02 +00001339 if mutability == &Mutability::Mut && !referent.is_unpin(ir) && func.name != UnqualifiedIdentifier::Destructor {
1340 // TODO(b/200067242): Add a `use std::pin::Pin` to the crate, and use `Pin`.
1341 Ok(quote! {self: std::pin::Pin< & #lifetime #mut_ Self>})
1342 } else {
1343 Ok(quote! { & #lifetime #mut_ self })
1344 }
Lukasz Anforowicz231a3bb2022-01-12 14:05:59 +00001345 }
Lukasz Anforowicz732ca642022-02-03 20:58:38 +00001346 _ => bail!("Unexpected type of `self` parameter: {:?}", self),
Lukasz Anforowicz231a3bb2022-01-12 14:05:59 +00001347 }
1348 }
1349
Lukasz Anforowicz40c2eb82022-01-11 18:22:31 +00001350 fn format_lifetime(
1351 lifetime_id: &LifetimeId,
1352 lifetime_to_name: &HashMap<LifetimeId, String>,
1353 ) -> Result<TokenStream> {
1354 let lifetime_name = lifetime_to_name.get(lifetime_id).ok_or_else(|| {
1355 anyhow!("`lifetime_to_name` doesn't have an entry for {:?}", lifetime_id)
1356 })?;
Lukasz Anforowicz95551272022-01-20 00:02:24 +00001357 Ok(format_lifetime_name(lifetime_name))
Lukasz Anforowicz40c2eb82022-01-11 18:22:31 +00001358 }
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00001359
Lukasz Anforowicz90bdb962022-02-14 21:07:45 +00001360 /// Returns whether the type represented by `self` implements the `Copy`
1361 /// trait.
Lukasz Anforowicza94ab702022-01-14 22:40:25 +00001362 pub fn implements_copy(&self) -> bool {
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00001363 // TODO(b/212696226): Verify results of `implements_copy` via static
1364 // assertions in the generated Rust code (because incorrect results
1365 // can silently lead to unsafe behavior).
1366 match self {
1367 RsTypeKind::Unit => true,
1368 RsTypeKind::Pointer { .. } => true,
Lukasz Anforowiczcf230fd2022-02-18 19:20:39 +00001369 RsTypeKind::FuncPtr { .. } => true,
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00001370 RsTypeKind::Reference { mutability: Mutability::Const, .. } => true,
1371 RsTypeKind::Reference { mutability: Mutability::Mut, .. } => false,
1372 RsTypeKind::Record(record) => should_derive_copy(record),
1373 RsTypeKind::TypeAlias { underlying_type, .. } => underlying_type.implements_copy(),
Lukasz Anforowiczd81bea92022-02-11 08:57:58 +00001374 RsTypeKind::Other { type_args, .. } => {
Lukasz Anforowicz20651e32022-02-10 14:52:15 +00001375 // All types that may appear here without `type_args` (e.g.
1376 // primitive types like `i32`) implement `Copy`. Generic types
1377 // that may be present here (e.g. Option<...>) are `Copy` if all
1378 // of their `type_args` are `Copy`.
1379 type_args.iter().all(|t| t.implements_copy())
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00001380 }
1381 }
1382 }
Lukasz Anforowicza94ab702022-01-14 22:40:25 +00001383
Lukasz Anforowiczf9564622022-01-28 14:31:04 +00001384 pub fn is_mut_ptr_to(&self, expected_record: &Record) -> bool {
1385 match self {
1386 RsTypeKind::Pointer { pointee, mutability: Mutability::Mut, .. } => {
1387 pointee.is_record(expected_record)
1388 }
1389 _ => false,
1390 }
1391 }
1392
1393 pub fn is_ref_to(&self, expected_record: &Record) -> bool {
1394 match self {
1395 RsTypeKind::Reference { referent, .. } => referent.is_record(expected_record),
1396 _ => false,
1397 }
1398 }
1399
Lukasz Anforowicza94ab702022-01-14 22:40:25 +00001400 pub fn is_shared_ref_to(&self, expected_record: &Record) -> bool {
1401 match self {
1402 RsTypeKind::Reference { referent, mutability: Mutability::Const, .. } => {
Lukasz Anforowiczf9564622022-01-28 14:31:04 +00001403 referent.is_record(expected_record)
Lukasz Anforowicza94ab702022-01-14 22:40:25 +00001404 }
1405 _ => false,
1406 }
1407 }
Lukasz Anforowiczf9564622022-01-28 14:31:04 +00001408
1409 pub fn is_record(&self, expected_record: &Record) -> bool {
1410 match self {
1411 RsTypeKind::Record(actual_record) => actual_record.id == expected_record.id,
1412 _ => false,
1413 }
1414 }
Lukasz Anforowicz90bdb962022-02-14 21:07:45 +00001415
1416 /// Iterates over `self` and all the nested types (e.g. pointees, generic
1417 /// type args, etc.) in DFS order.
1418 pub fn dfs_iter<'ty>(&'ty self) -> impl Iterator<Item = &'ty RsTypeKind<'ir>> + '_ {
1419 RsTypeKindIter::new(self)
1420 }
1421
1422 /// Iterates over all `LifetimeId`s in `self` and in all the nested types.
1423 /// Note that the results might contain duplicate LifetimeId values (e.g.
1424 /// if the same LifetimeId is used in two `type_args`).
1425 pub fn lifetimes(&self) -> impl Iterator<Item = LifetimeId> + '_ {
1426 self.dfs_iter().filter_map(|t| match t {
1427 RsTypeKind::Reference { lifetime_id, .. } => Some(*lifetime_id),
1428 _ => None,
1429 })
1430 }
1431}
1432
1433struct RsTypeKindIter<'ty, 'ir> {
1434 todo: Vec<&'ty RsTypeKind<'ir>>,
1435}
1436
1437impl<'ty, 'ir> RsTypeKindIter<'ty, 'ir> {
1438 pub fn new(ty: &'ty RsTypeKind<'ir>) -> Self {
1439 Self { todo: vec![ty] }
1440 }
1441}
1442
1443impl<'ty, 'ir> Iterator for RsTypeKindIter<'ty, 'ir> {
1444 type Item = &'ty RsTypeKind<'ir>;
1445
1446 fn next(&mut self) -> Option<Self::Item> {
1447 match self.todo.pop() {
1448 None => None,
1449 Some(curr) => {
1450 match curr {
1451 RsTypeKind::Unit | RsTypeKind::Record(_) => (),
1452 RsTypeKind::Pointer { pointee, .. } => self.todo.push(pointee),
1453 RsTypeKind::Reference { referent, .. } => self.todo.push(referent),
1454 RsTypeKind::TypeAlias { underlying_type: t, .. } => self.todo.push(t),
Lukasz Anforowiczcf230fd2022-02-18 19:20:39 +00001455 RsTypeKind::FuncPtr { return_type, param_types, .. } => {
1456 self.todo.push(return_type);
1457 self.todo.extend(param_types.iter().rev());
1458 },
Lukasz Anforowicz90bdb962022-02-14 21:07:45 +00001459 RsTypeKind::Other { type_args, .. } => self.todo.extend(type_args.iter().rev()),
1460 };
1461 Some(curr)
1462 }
1463 }
1464 }
Lukasz Anforowicz40c2eb82022-01-11 18:22:31 +00001465}
1466
Lukasz Anforowicz95551272022-01-20 00:02:24 +00001467fn format_lifetime_name(lifetime_name: &str) -> TokenStream {
1468 let lifetime =
1469 syn::Lifetime::new(&format!("'{}", lifetime_name), proc_macro2::Span::call_site());
1470 quote! { #lifetime }
1471}
1472
Googler7cced422021-12-06 11:58:39 +00001473fn format_rs_type(
1474 ty: &ir::RsType,
1475 ir: &IR,
1476 lifetime_to_name: &HashMap<LifetimeId, String>,
1477) -> Result<TokenStream> {
Lukasz Anforowicz40c2eb82022-01-11 18:22:31 +00001478 RsTypeKind::new(ty, ir)
1479 .and_then(|kind| kind.format(ir, lifetime_to_name))
1480 .with_context(|| format!("Failed to format Rust type {:?}", ty))
Devin Jeanpierre7a7328e2021-09-17 07:10:08 +00001481}
1482
Googler6a0a5252022-01-11 14:08:09 +00001483fn cc_type_name_for_item(item: &ir::Item) -> Result<TokenStream> {
1484 let (disambiguator_fragment, identifier) = match item {
1485 Item::Record(record) => (quote! { class }, &record.identifier),
1486 Item::TypeAlias(type_alias) => (quote! {}, &type_alias.identifier),
1487 _ => bail!("Item does not define a type: {:?}", item),
1488 };
1489
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +00001490 let ident = format_cc_ident(identifier.identifier.as_str());
Googler6a0a5252022-01-11 14:08:09 +00001491 Ok(quote! { #disambiguator_fragment #ident })
1492}
1493
Marcel Hlopkoc0956cf2021-11-29 08:31:28 +00001494fn format_cc_type(ty: &ir::CcType, ir: &IR) -> Result<TokenStream> {
Devin Jeanpierre09c6f452021-09-29 07:34:24 +00001495 let const_fragment = if ty.is_const {
Devin Jeanpierre184f9ac2021-09-17 13:47:03 +00001496 quote! {const}
1497 } else {
1498 quote! {}
1499 };
Marcel Hlopkoc0956cf2021-11-29 08:31:28 +00001500 if let Some(ref name) = ty.name {
1501 match name.as_str() {
1502 "*" => {
Googlerff7fc232021-12-02 09:43:00 +00001503 if ty.type_args.len() != 1 {
1504 bail!("Invalid pointer type (need exactly 1 type argument): {:?}", ty);
Marcel Hlopkoc0956cf2021-11-29 08:31:28 +00001505 }
Googlerff7fc232021-12-02 09:43:00 +00001506 assert_eq!(ty.type_args.len(), 1);
1507 let nested_type = format_cc_type(&ty.type_args[0], ir)?;
Marcel Hlopkoc0956cf2021-11-29 08:31:28 +00001508 Ok(quote! {#nested_type * #const_fragment})
Devin Jeanpierre7a7328e2021-09-17 07:10:08 +00001509 }
Lukasz Anforowicz275fa922022-01-05 16:13:20 +00001510 "&" => {
1511 if ty.type_args.len() != 1 {
1512 bail!("Invalid reference type (need exactly 1 type argument): {:?}", ty);
1513 }
1514 let nested_type = format_cc_type(&ty.type_args[0], ir)?;
1515 Ok(quote! {#nested_type &})
1516 }
Lukasz Anforowicz957cbf22022-01-05 16:14:05 +00001517 cc_type_name => {
Googlerff7fc232021-12-02 09:43:00 +00001518 if !ty.type_args.is_empty() {
Marcel Hlopkoc0956cf2021-11-29 08:31:28 +00001519 bail!("Type not yet supported: {:?}", ty);
1520 }
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +00001521 let idents = cc_type_name.split_whitespace().map(format_cc_ident);
Lukasz Anforowicz957cbf22022-01-05 16:14:05 +00001522 Ok(quote! {#( #idents )* #const_fragment})
Devin Jeanpierre7a7328e2021-09-17 07:10:08 +00001523 }
Devin Jeanpierre7a7328e2021-09-17 07:10:08 +00001524 }
Marcel Hlopkoc0956cf2021-11-29 08:31:28 +00001525 } else {
Googler6a0a5252022-01-11 14:08:09 +00001526 let item = ir.item_for_type(ty)?;
1527 let type_name = cc_type_name_for_item(item)?;
1528 Ok(quote! {#const_fragment #type_name})
Devin Jeanpierre7a7328e2021-09-17 07:10:08 +00001529 }
1530}
1531
Marcel Hlopkoa0f38662021-12-03 08:45:26 +00001532fn cc_struct_layout_assertion(record: &Record, ir: &IR) -> TokenStream {
Googler6a0a5252022-01-11 14:08:09 +00001533 if !ir.is_current_target(&record.owning_target) && !ir.is_stdlib_target(&record.owning_target) {
Marcel Hlopkoa0f38662021-12-03 08:45:26 +00001534 return quote! {};
1535 }
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +00001536 let record_ident = format_cc_ident(&record.identifier.identifier);
Googler5ea88642021-09-29 08:05:59 +00001537 let size = Literal::usize_unsuffixed(record.size);
1538 let alignment = Literal::usize_unsuffixed(record.alignment);
Lukasz Anforowicz74704712021-12-22 15:30:31 +00001539 let field_assertions =
1540 record.fields.iter().filter(|f| f.access == AccessSpecifier::Public).map(|field| {
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +00001541 let field_ident = format_cc_ident(&field.identifier.identifier);
Lukasz Anforowicz74704712021-12-22 15:30:31 +00001542 let offset = Literal::usize_unsuffixed(field.offset);
1543 // The IR contains the offset in bits, while C++'s offsetof()
1544 // returns the offset in bytes, so we need to convert.
1545 quote! {
Googler972d3582022-01-11 10:17:22 +00001546 static_assert(offsetof(class #record_ident, #field_ident) * 8 == #offset);
Lukasz Anforowicz74704712021-12-22 15:30:31 +00001547 }
1548 });
Googler5ea88642021-09-29 08:05:59 +00001549 quote! {
Googler972d3582022-01-11 10:17:22 +00001550 static_assert(sizeof(class #record_ident) == #size);
1551 static_assert(alignof(class #record_ident) == #alignment);
Googler5ea88642021-09-29 08:05:59 +00001552 #( #field_assertions )*
1553 }
1554}
1555
Devin Jeanpierre58181ac2022-02-14 21:30:05 +00001556// Returns the accessor functions for no_unique_address member variables.
1557fn cc_struct_no_unique_address_impl(record: &Record, ir: &IR) -> Result<TokenStream> {
1558 let mut fields = vec![];
1559 let mut types = vec![];
1560 for field in &record.fields {
1561 if field.access != AccessSpecifier::Public || !field.is_no_unique_address {
1562 continue;
1563 }
1564 fields.push(make_rs_ident(&field.identifier.identifier));
1565 types.push(format_rs_type(&field.type_.rs_type, ir, &HashMap::new()).with_context(
1566 || format!("Failed to format type for field {:?} on record {:?}", field, record),
1567 )?)
1568 }
1569
1570 if fields.is_empty() {
1571 return Ok(quote! {});
1572 }
1573
1574 let ident = make_rs_ident(&record.identifier.identifier);
1575 Ok(quote! {
1576 impl #ident {
1577 #(
1578 pub fn #fields(&self) -> &#types {
1579 unsafe {&* (&self.#fields as *const _ as *const #types)}
1580 }
1581 )*
1582 }
1583 })
1584}
1585
Devin Jeanpierre56777022022-02-03 01:57:15 +00001586/// Returns the implementation of base class conversions, for converting a type
1587/// to its unambiguous public base classes.
1588///
1589/// TODO(b/216195042): Implement this in terms of a supporting trait which casts
1590/// raw pointers. Then, we would have blanket impls for reference, pinned mut
1591/// reference, etc. conversion. The current version is just enough to test the
1592/// logic in importer.
1593//
1594// TODO(b/216195042): Should this use, like, AsRef/AsMut (and some equivalent
1595// for Pin)?
1596fn cc_struct_upcast_impl(record: &Record, ir: &IR) -> Result<TokenStream> {
1597 let mut impls = Vec::with_capacity(record.unambiguous_public_bases.len());
1598 for base in &record.unambiguous_public_bases {
1599 let base_record: &Record = ir.find_decl(base.base_record_id)?.try_into()?;
1600 if let Some(offset) = base.offset {
1601 let offset = Literal::i64_unsuffixed(offset);
1602 // TODO(b/216195042): Correctly handle imported records, lifetimes.
1603 let base_name = make_rs_ident(&base_record.identifier.identifier);
1604 let derived_name = make_rs_ident(&record.identifier.identifier);
1605 impls.push(quote! {
1606 impl<'a> From<&'a #derived_name> for &'a #base_name {
1607 fn from(x: &'a #derived_name) -> Self {
1608 unsafe {
1609 &*((x as *const _ as *const u8).offset(#offset) as *const #base_name)
1610 }
1611 }
1612 }
1613 });
1614 } else {
1615 // TODO(b/216195042): determine offset dynamically / use a dynamic
1616 // cast. This requires a new C++ function to be
1617 // generated, so that we have something to call.
1618 }
1619 }
1620
1621 Ok(quote! {
1622 #(#impls)*
1623 })
1624}
1625
Googlera675ae02021-12-07 08:04:59 +00001626fn thunk_ident(func: &Func) -> Ident {
1627 format_ident!("__rust_thunk__{}", func.mangled_name)
Devin Jeanpierref2ec8712021-10-13 20:47:16 +00001628}
1629
Marcel Hlopko89547752021-12-10 09:39:41 +00001630fn generate_rs_api_impl(ir: &IR) -> Result<TokenStream> {
Michael Forsterbee84482021-10-13 08:35:38 +00001631 // This function uses quote! to generate C++ source code out of convenience.
1632 // This is a bold idea so we have to continously evaluate if it still makes
1633 // sense or the cost of working around differences in Rust and C++ tokens is
1634 // greather than the value added.
Marcel Hlopko3164eee2021-08-24 20:09:22 +00001635 //
Michael Forsterbee84482021-10-13 08:35:38 +00001636 // See rs_bindings_from_cc/
1637 // token_stream_printer.rs for a list of supported placeholders.
Marcel Hlopko3164eee2021-08-24 20:09:22 +00001638 let mut thunks = vec![];
Michael Forster7ef80732021-10-01 18:12:19 +00001639 for func in ir.functions() {
Lukasz Anforowiczdd9ae0f2022-02-17 15:52:53 +00001640 if can_skip_cc_thunk(func) {
Marcel Hlopko3164eee2021-08-24 20:09:22 +00001641 continue;
1642 }
1643
Googlera675ae02021-12-07 08:04:59 +00001644 let thunk_ident = thunk_ident(func);
Devin Jeanpierref2ec8712021-10-13 20:47:16 +00001645 let implementation_function = match &func.name {
Lukasz Anforowicz9c663ca2022-02-09 01:33:31 +00001646 UnqualifiedIdentifier::Operator(op) => {
1647 let name = syn::parse_str::<TokenStream>(&op.name)?;
1648 quote! { operator #name }
1649 }
Devin Jeanpierref2ec8712021-10-13 20:47:16 +00001650 UnqualifiedIdentifier::Identifier(id) => {
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +00001651 let fn_ident = format_cc_ident(&id.identifier);
Lukasz Anforowiczaab8ad22021-12-19 20:29:26 +00001652 let static_method_metadata = func
1653 .member_func_metadata
1654 .as_ref()
1655 .filter(|meta| meta.instance_method_metadata.is_none());
1656 match static_method_metadata {
1657 None => quote! {#fn_ident},
1658 Some(meta) => {
Marcel Hlopkoeaae9b72022-01-21 15:54:11 +00001659 let record_ident =
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +00001660 format_cc_ident(&meta.find_record(ir)?.identifier.identifier);
Lukasz Anforowiczaab8ad22021-12-19 20:29:26 +00001661 quote! { #record_ident :: #fn_ident }
1662 }
1663 }
Devin Jeanpierref2ec8712021-10-13 20:47:16 +00001664 }
Lukasz Anforowicz7b0042d2022-01-06 23:00:19 +00001665 // Use `destroy_at` to avoid needing to spell out the class name. Destructor identiifers
Devin Jeanpierrecc6cf092021-12-16 04:31:14 +00001666 // use the name of the type itself, without namespace qualification, template
1667 // parameters, or aliases. We do not need to use that naming scheme anywhere else in
1668 // the bindings, and it can be difficult (impossible?) to spell in the general case. By
1669 // using destroy_at, we avoid needing to determine or remember what the correct spelling
Lukasz Anforowicz7b0042d2022-01-06 23:00:19 +00001670 // is. Similar arguments apply to `construct_at`.
Lukasz Anforowicze643ec92021-12-22 15:45:15 +00001671 UnqualifiedIdentifier::Constructor => {
Lukasz Anforowicz7b0042d2022-01-06 23:00:19 +00001672 quote! { rs_api_impl_support::construct_at }
Lukasz Anforowicze643ec92021-12-22 15:45:15 +00001673 }
Devin Jeanpierref2ec8712021-10-13 20:47:16 +00001674 UnqualifiedIdentifier::Destructor => quote! {std::destroy_at},
Devin Jeanpierref2ec8712021-10-13 20:47:16 +00001675 };
Marcel Hlopkoc0956cf2021-11-29 08:31:28 +00001676 let return_type_name = format_cc_type(&func.return_type.cc_type, ir)?;
Lukasz Anforowicze643ec92021-12-22 15:45:15 +00001677 let return_stmt = if func.return_type.cc_type.is_void() {
1678 quote! {}
1679 } else {
1680 quote! { return }
1681 };
Marcel Hlopko3164eee2021-08-24 20:09:22 +00001682
1683 let param_idents =
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +00001684 func.params.iter().map(|p| format_cc_ident(&p.identifier.identifier)).collect_vec();
Marcel Hlopko3164eee2021-08-24 20:09:22 +00001685
Devin Jeanpierre09c6f452021-09-29 07:34:24 +00001686 let param_types = func
1687 .params
1688 .iter()
Marcel Hlopkoc0956cf2021-11-29 08:31:28 +00001689 .map(|p| format_cc_type(&p.type_.cc_type, ir))
Devin Jeanpierre09c6f452021-09-29 07:34:24 +00001690 .collect::<Result<Vec<_>>>()?;
Marcel Hlopko3164eee2021-08-24 20:09:22 +00001691
Lukasz Anforowiczb3d89aa2022-01-12 14:35:52 +00001692 let needs_this_deref = match &func.member_func_metadata {
1693 None => false,
1694 Some(meta) => match &func.name {
1695 UnqualifiedIdentifier::Constructor | UnqualifiedIdentifier::Destructor => false,
Marcel Hlopko14ee3c82022-02-09 09:46:23 +00001696 UnqualifiedIdentifier::Identifier(_) | UnqualifiedIdentifier::Operator(_) => {
1697 meta.instance_method_metadata.is_some()
1698 }
Lukasz Anforowiczb3d89aa2022-01-12 14:35:52 +00001699 },
1700 };
1701 let (implementation_function, arg_expressions) = if !needs_this_deref {
1702 (implementation_function, param_idents.clone())
1703 } else {
1704 let this_param = func
1705 .params
1706 .first()
1707 .ok_or_else(|| anyhow!("Instance methods must have `__this` param."))?;
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +00001708 let this_arg = format_cc_ident(&this_param.identifier.identifier);
Lukasz Anforowiczb3d89aa2022-01-12 14:35:52 +00001709 (
1710 quote! { #this_arg -> #implementation_function},
1711 param_idents.iter().skip(1).cloned().collect_vec(),
1712 )
1713 };
1714
Marcel Hlopko3164eee2021-08-24 20:09:22 +00001715 thunks.push(quote! {
1716 extern "C" #return_type_name #thunk_ident( #( #param_types #param_idents ),* ) {
Lukasz Anforowiczb3d89aa2022-01-12 14:35:52 +00001717 #return_stmt #implementation_function( #( #arg_expressions ),* );
Marcel Hlopko3164eee2021-08-24 20:09:22 +00001718 }
1719 });
1720 }
1721
Marcel Hlopkoa0f38662021-12-03 08:45:26 +00001722 let layout_assertions = ir.records().map(|record| cc_struct_layout_assertion(record, ir));
Googler5ea88642021-09-29 08:05:59 +00001723
Devin Jeanpierre231ef8d2021-10-27 10:50:44 +00001724 let mut standard_headers = <BTreeSet<Ident>>::new();
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +00001725 standard_headers.insert(format_ident!("memory")); // ubiquitous.
Devin Jeanpierre231ef8d2021-10-27 10:50:44 +00001726 if ir.records().next().is_some() {
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +00001727 standard_headers.insert(format_ident!("cstddef"));
Devin Jeanpierre231ef8d2021-10-27 10:50:44 +00001728 };
Googler5ea88642021-09-29 08:05:59 +00001729
Lukasz Anforowicz4457baf2021-12-23 17:24:04 +00001730 let mut includes =
1731 vec!["rs_bindings_from_cc/support/cxx20_backports.h"];
1732
Michael Forsterbee84482021-10-13 08:35:38 +00001733 // In order to generate C++ thunk in all the cases Clang needs to be able to
1734 // access declarations from public headers of the C++ library.
Lukasz Anforowicz4457baf2021-12-23 17:24:04 +00001735 includes.extend(ir.used_headers().map(|i| &i.name as &str));
Marcel Hlopko3164eee2021-08-24 20:09:22 +00001736
Marcel Hlopko89547752021-12-10 09:39:41 +00001737 Ok(quote! {
Googler5ea88642021-09-29 08:05:59 +00001738 #( __HASH_TOKEN__ include <#standard_headers> __NEWLINE__)*
Devin Jeanpierre7c74f842022-02-03 07:08:06 +00001739 __NEWLINE__
Michael Forsterdb8101a2021-10-08 06:56:03 +00001740 #( __HASH_TOKEN__ include #includes __NEWLINE__)* __NEWLINE__
Marcel Hlopkob8069ae2022-02-19 09:31:00 +00001741 __HASH_TOKEN__ pragma clang diagnostic push __NEWLINE__
1742 // Disable Clang thread-safety-analysis warnings that would otherwise
1743 // complain about thunks that call mutex locking functions in an unpaired way.
1744 __HASH_TOKEN__ pragma clang diagnostic ignored "-Wthread-safety-analysis" __NEWLINE__
Marcel Hlopko3164eee2021-08-24 20:09:22 +00001745
Michael Forsterdb8101a2021-10-08 06:56:03 +00001746 #( #thunks )* __NEWLINE__ __NEWLINE__
Googler5ea88642021-09-29 08:05:59 +00001747
Michael Forsterdb8101a2021-10-08 06:56:03 +00001748 #( #layout_assertions __NEWLINE__ __NEWLINE__ )*
Marcel Hlopkoc6b726c2021-10-07 06:53:09 +00001749
Marcel Hlopkob8069ae2022-02-19 09:31:00 +00001750 __NEWLINE__
1751 __HASH_TOKEN__ pragma clang diagnostic pop __NEWLINE__
Marcel Hlopkoc6b726c2021-10-07 06:53:09 +00001752 // To satisfy http://cs/symbol:devtools.metadata.Presubmit.CheckTerminatingNewline check.
1753 __NEWLINE__
Marcel Hlopko89547752021-12-10 09:39:41 +00001754 })
Marcel Hlopko3164eee2021-08-24 20:09:22 +00001755}
1756
Marcel Hlopko42abfc82021-08-09 07:03:17 +00001757#[cfg(test)]
1758mod tests {
Devin Jeanpierre45cb1162021-10-27 10:54:28 +00001759 use super::*;
Michael Forstered642022021-10-04 09:48:25 +00001760 use anyhow::anyhow;
Lukasz Anforowicz9c663ca2022-02-09 01:33:31 +00001761 use ir_testing::{ir_from_cc, ir_from_cc_dependency, ir_func, ir_record, retrieve_func};
Marcel Hlopko89547752021-12-10 09:39:41 +00001762 use token_stream_matchers::{
1763 assert_cc_matches, assert_cc_not_matches, assert_rs_matches, assert_rs_not_matches,
Devin Jeanpierre7e9a1de2021-12-03 08:04:22 +00001764 };
Michael Forsterdb8101a2021-10-08 06:56:03 +00001765 use token_stream_printer::tokens_to_string;
Marcel Hlopko42abfc82021-08-09 07:03:17 +00001766
1767 #[test]
Marcel Hlopkob8069ae2022-02-19 09:31:00 +00001768 fn test_disable_thread_safety_warnings() -> Result<()> {
1769 let ir = ir_from_cc("inline void foo() {}")?;
1770 let rs_api_impl = generate_rs_api_impl(&ir)?;
1771 assert_cc_matches!(
1772 rs_api_impl,
1773 quote! {
1774 ...
1775 __HASH_TOKEN__ pragma clang diagnostic push
1776 __HASH_TOKEN__ pragma clang diagnostic ignored "-Wthread-safety-analysis"
1777 ...
1778
1779 __HASH_TOKEN__ pragma clang diagnostic pop
1780 ...
1781 }
1782 );
1783 Ok(())
1784 }
1785
1786 #[test]
Marcel Hlopko3b9bf9e2021-11-29 08:25:14 +00001787 // TODO(hlopko): Move this test to a more principled place where it can access
1788 // `ir_testing`.
1789 fn test_duplicate_decl_ids_err() {
1790 let mut r1 = ir_record("R1");
Marcel Hlopko264b9ad2021-12-02 21:06:44 +00001791 r1.id = DeclId(42);
Marcel Hlopko3b9bf9e2021-11-29 08:25:14 +00001792 let mut r2 = ir_record("R2");
Marcel Hlopko264b9ad2021-12-02 21:06:44 +00001793 r2.id = DeclId(42);
Marcel Hlopko3b9bf9e2021-11-29 08:25:14 +00001794 let result = make_ir_from_items([r1.into(), r2.into()]);
1795 assert!(result.is_err());
1796 assert!(result.unwrap_err().to_string().contains("Duplicate decl_id found in"));
1797 }
1798
1799 #[test]
Marcel Hlopko45fba972021-08-23 19:52:20 +00001800 fn test_simple_function() -> Result<()> {
Marcel Hlopko89547752021-12-10 09:39:41 +00001801 let ir = ir_from_cc("int Add(int a, int b);")?;
1802 let rs_api = generate_rs_api(&ir)?;
1803 assert_rs_matches!(
1804 rs_api,
1805 quote! {
Michael Forsterdb8101a2021-10-08 06:56:03 +00001806 #[inline(always)]
Marcel Hlopko89547752021-12-10 09:39:41 +00001807 pub fn Add(a: i32, b: i32) -> i32 {
Googlera675ae02021-12-07 08:04:59 +00001808 unsafe { crate::detail::__rust_thunk___Z3Addii(a, b) }
Marcel Hlopko89547752021-12-10 09:39:41 +00001809 }
1810 }
1811 );
1812 assert_rs_matches!(
1813 rs_api,
1814 quote! {
Michael Forsterdb8101a2021-10-08 06:56:03 +00001815 mod detail {
Googler55647142022-01-11 12:37:39 +00001816 #[allow(unused_imports)]
Devin Jeanpierred4dde0e2021-10-13 20:48:25 +00001817 use super::*;
Michael Forsterdb8101a2021-10-08 06:56:03 +00001818 extern "C" {
1819 #[link_name = "_Z3Addii"]
Googlera675ae02021-12-07 08:04:59 +00001820 pub(crate) fn __rust_thunk___Z3Addii(a: i32, b: i32) -> i32;
Marcel Hlopko89547752021-12-10 09:39:41 +00001821 }
1822 }
1823 }
Marcel Hlopko42abfc82021-08-09 07:03:17 +00001824 );
Michael Forsterdb8101a2021-10-08 06:56:03 +00001825
Marcel Hlopko89547752021-12-10 09:39:41 +00001826 assert_cc_not_matches!(generate_rs_api_impl(&ir)?, quote! {__rust_thunk___Z3Addii});
Michael Forsterdb8101a2021-10-08 06:56:03 +00001827
Marcel Hlopko3164eee2021-08-24 20:09:22 +00001828 Ok(())
1829 }
1830
1831 #[test]
1832 fn test_inline_function() -> Result<()> {
Marcel Hlopko89547752021-12-10 09:39:41 +00001833 let ir = ir_from_cc("inline int Add(int a, int b);")?;
1834 let rs_api = generate_rs_api(&ir)?;
1835 assert_rs_matches!(
1836 rs_api,
1837 quote! {
Michael Forsterdb8101a2021-10-08 06:56:03 +00001838 #[inline(always)]
Marcel Hlopko89547752021-12-10 09:39:41 +00001839 pub fn Add(a: i32, b: i32) -> i32 {
Googlera675ae02021-12-07 08:04:59 +00001840 unsafe { crate::detail::__rust_thunk___Z3Addii(a, b) }
Marcel Hlopko89547752021-12-10 09:39:41 +00001841 }
1842 }
1843 );
1844 assert_rs_matches!(
1845 rs_api,
1846 quote! {
Michael Forsterdb8101a2021-10-08 06:56:03 +00001847 mod detail {
Googler55647142022-01-11 12:37:39 +00001848 #[allow(unused_imports)]
Devin Jeanpierred4dde0e2021-10-13 20:48:25 +00001849 use super::*;
Michael Forsterdb8101a2021-10-08 06:56:03 +00001850 extern "C" {
Googlera675ae02021-12-07 08:04:59 +00001851 pub(crate) fn __rust_thunk___Z3Addii(a: i32, b: i32) -> i32;
Marcel Hlopko89547752021-12-10 09:39:41 +00001852 }
1853 }
1854 }
Marcel Hlopko3164eee2021-08-24 20:09:22 +00001855 );
1856
Marcel Hlopko89547752021-12-10 09:39:41 +00001857 assert_cc_matches!(
1858 generate_rs_api_impl(&ir)?,
1859 quote! {
Googlera675ae02021-12-07 08:04:59 +00001860 extern "C" int __rust_thunk___Z3Addii(int a, int b) {
Marcel Hlopko89547752021-12-10 09:39:41 +00001861 return Add(a, b);
Marcel Hlopko3164eee2021-08-24 20:09:22 +00001862 }
Marcel Hlopko89547752021-12-10 09:39:41 +00001863 }
Marcel Hlopko3164eee2021-08-24 20:09:22 +00001864 );
Marcel Hlopko42abfc82021-08-09 07:03:17 +00001865 Ok(())
1866 }
Marcel Hlopkob4b28742021-09-15 12:45:20 +00001867
1868 #[test]
Marcel Hlopkoa0f38662021-12-03 08:45:26 +00001869 fn test_simple_function_with_types_from_other_target() -> Result<()> {
1870 let ir = ir_from_cc_dependency(
1871 "inline ReturnStruct DoSomething(ParamStruct param);",
1872 "struct ReturnStruct {}; struct ParamStruct {};",
1873 )?;
1874
Marcel Hlopko89547752021-12-10 09:39:41 +00001875 let rs_api = generate_rs_api(&ir)?;
1876 assert_rs_matches!(
1877 rs_api,
1878 quote! {
Marcel Hlopkoa0f38662021-12-03 08:45:26 +00001879 #[inline(always)]
1880 pub fn DoSomething(param: dependency::ParamStruct)
Marcel Hlopko89547752021-12-10 09:39:41 +00001881 -> dependency::ReturnStruct {
Googlera675ae02021-12-07 08:04:59 +00001882 unsafe { crate::detail::__rust_thunk___Z11DoSomething11ParamStruct(param) }
Marcel Hlopko89547752021-12-10 09:39:41 +00001883 }
1884 }
1885 );
1886 assert_rs_matches!(
1887 rs_api,
1888 quote! {
1889 mod detail {
Googler55647142022-01-11 12:37:39 +00001890 #[allow(unused_imports)]
Marcel Hlopko89547752021-12-10 09:39:41 +00001891 use super::*;
1892 extern "C" {
1893 pub(crate) fn __rust_thunk___Z11DoSomething11ParamStruct(param: dependency::ParamStruct)
1894 -> dependency::ReturnStruct;
1895 }
1896 }}
Marcel Hlopkoa0f38662021-12-03 08:45:26 +00001897 );
1898
Marcel Hlopko89547752021-12-10 09:39:41 +00001899 assert_cc_matches!(
1900 generate_rs_api_impl(&ir)?,
1901 quote! {
Googler972d3582022-01-11 10:17:22 +00001902 extern "C" class ReturnStruct __rust_thunk___Z11DoSomething11ParamStruct(class ParamStruct param) {
Marcel Hlopkoa0f38662021-12-03 08:45:26 +00001903 return DoSomething(param);
1904 }
Marcel Hlopko89547752021-12-10 09:39:41 +00001905 }
Marcel Hlopkoa0f38662021-12-03 08:45:26 +00001906 );
1907 Ok(())
1908 }
1909
1910 #[test]
Marcel Hlopkob4b28742021-09-15 12:45:20 +00001911 fn test_simple_struct() -> Result<()> {
Marcel Hlopko89547752021-12-10 09:39:41 +00001912 let ir = ir_from_cc(&tokens_to_string(quote! {
Devin Jeanpierre88343c72022-01-15 01:10:23 +00001913 struct SomeStruct final {
Marcel Hlopko89547752021-12-10 09:39:41 +00001914 int public_int;
1915 protected:
1916 int protected_int;
1917 private:
1918 int private_int;
1919 };
1920 })?)?;
Michael Forster028800b2021-10-05 12:39:59 +00001921
Marcel Hlopko89547752021-12-10 09:39:41 +00001922 let rs_api = generate_rs_api(&ir)?;
1923 assert_rs_matches!(
1924 rs_api,
1925 quote! {
Michael Forsterdb8101a2021-10-08 06:56:03 +00001926 #[derive(Clone, Copy)]
1927 #[repr(C)]
1928 pub struct SomeStruct {
1929 pub public_int: i32,
1930 protected_int: i32,
1931 private_int: i32,
Marcel Hlopko89547752021-12-10 09:39:41 +00001932 }
1933 }
1934 );
1935 assert_rs_matches!(
1936 rs_api,
1937 quote! {
Googler454f2652021-12-06 12:53:12 +00001938 const _: () = assert!(std::mem::size_of::<Option<&i32>>() == std::mem::size_of::<&i32>());
Googler209b10a2021-12-06 09:11:57 +00001939 const _: () = assert!(std::mem::size_of::<SomeStruct>() == 12usize);
1940 const _: () = assert!(std::mem::align_of::<SomeStruct>() == 4usize);
1941 const _: () = assert!(offset_of!(SomeStruct, public_int) * 8 == 0usize);
1942 const _: () = assert!(offset_of!(SomeStruct, protected_int) * 8 == 32usize);
1943 const _: () = assert!(offset_of!(SomeStruct, private_int) * 8 == 64usize);
Marcel Hlopko89547752021-12-10 09:39:41 +00001944 }
Marcel Hlopkob4b28742021-09-15 12:45:20 +00001945 );
Marcel Hlopko89547752021-12-10 09:39:41 +00001946 let rs_api_impl = generate_rs_api_impl(&ir)?;
1947 assert_cc_matches!(
1948 rs_api_impl,
1949 quote! {
Googler972d3582022-01-11 10:17:22 +00001950 extern "C" void __rust_thunk___ZN10SomeStructD1Ev(class SomeStruct * __this) {
Lukasz Anforowicze643ec92021-12-22 15:45:15 +00001951 std :: destroy_at (__this) ;
Marcel Hlopko89547752021-12-10 09:39:41 +00001952 }
1953 }
1954 );
1955 assert_cc_matches!(
1956 rs_api_impl,
1957 quote! {
Googler972d3582022-01-11 10:17:22 +00001958 static_assert(sizeof(class SomeStruct) == 12);
1959 static_assert(alignof(class SomeStruct) == 4);
1960 static_assert(offsetof(class SomeStruct, public_int) * 8 == 0);
Marcel Hlopko89547752021-12-10 09:39:41 +00001961 }
Googler5ea88642021-09-29 08:05:59 +00001962 );
Marcel Hlopkob4b28742021-09-15 12:45:20 +00001963 Ok(())
1964 }
Devin Jeanpierre7a7328e2021-09-17 07:10:08 +00001965
Devin Jeanpierre2ed14ec2021-10-06 11:32:19 +00001966 #[test]
Lukasz Anforowicz275fa922022-01-05 16:13:20 +00001967 fn test_ref_to_struct_in_thunk_impls() -> Result<()> {
Googler972d3582022-01-11 10:17:22 +00001968 let ir = ir_from_cc("struct S{}; inline void foo(class S& s) {} ")?;
Lukasz Anforowicz275fa922022-01-05 16:13:20 +00001969 let rs_api_impl = generate_rs_api_impl(&ir)?;
1970 assert_cc_matches!(
1971 rs_api_impl,
1972 quote! {
Googler972d3582022-01-11 10:17:22 +00001973 extern "C" void __rust_thunk___Z3fooR1S(class S& s) {
Lukasz Anforowicz275fa922022-01-05 16:13:20 +00001974 foo(s);
1975 }
1976 }
1977 );
1978 Ok(())
1979 }
1980
1981 #[test]
1982 fn test_const_ref_to_struct_in_thunk_impls() -> Result<()> {
Googler972d3582022-01-11 10:17:22 +00001983 let ir = ir_from_cc("struct S{}; inline void foo(const class S& s) {} ")?;
Lukasz Anforowicz275fa922022-01-05 16:13:20 +00001984 let rs_api_impl = generate_rs_api_impl(&ir)?;
1985 assert_cc_matches!(
1986 rs_api_impl,
1987 quote! {
Googler972d3582022-01-11 10:17:22 +00001988 extern "C" void __rust_thunk___Z3fooRK1S(const class S& s) {
Lukasz Anforowicz275fa922022-01-05 16:13:20 +00001989 foo(s);
1990 }
1991 }
1992 );
1993 Ok(())
1994 }
1995
1996 #[test]
Lukasz Anforowicz957cbf22022-01-05 16:14:05 +00001997 fn test_unsigned_int_in_thunk_impls() -> Result<()> {
1998 let ir = ir_from_cc("inline void foo(unsigned int i) {} ")?;
1999 let rs_api_impl = generate_rs_api_impl(&ir)?;
2000 assert_cc_matches!(
2001 rs_api_impl,
2002 quote! {
2003 extern "C" void __rust_thunk___Z3fooj(unsigned int i) {
2004 foo(i);
2005 }
2006 }
2007 );
2008 Ok(())
2009 }
2010
2011 #[test]
Marcel Hlopkodd1fcb12021-12-22 14:13:59 +00002012 fn test_record_static_methods_qualify_call_in_thunk() -> Result<()> {
2013 let ir = ir_from_cc(&tokens_to_string(quote! {
2014 struct SomeStruct {
2015 static inline int some_func() { return 42; }
2016 };
2017 })?)?;
2018
2019 assert_cc_matches!(
2020 generate_rs_api_impl(&ir)?,
2021 quote! {
2022 extern "C" int __rust_thunk___ZN10SomeStruct9some_funcEv() {
2023 return SomeStruct::some_func();
2024 }
2025 }
2026 );
2027 Ok(())
2028 }
2029
2030 #[test]
Lukasz Anforowiczb3d89aa2022-01-12 14:35:52 +00002031 fn test_record_instance_methods_deref_this_in_thunk() -> Result<()> {
2032 let ir = ir_from_cc(&tokens_to_string(quote! {
2033 struct SomeStruct {
2034 inline int some_func(int arg) const { return 42 + arg; }
2035 };
2036 })?)?;
2037
2038 assert_cc_matches!(
2039 generate_rs_api_impl(&ir)?,
2040 quote! {
2041 extern "C" int __rust_thunk___ZNK10SomeStruct9some_funcEi(
2042 const class SomeStruct* __this, int arg) {
2043 return __this->some_func(arg);
2044 }
2045 }
2046 );
2047 Ok(())
2048 }
2049
2050 #[test]
Marcel Hlopkoa0f38662021-12-03 08:45:26 +00002051 fn test_struct_from_other_target() -> Result<()> {
2052 let ir = ir_from_cc_dependency("// intentionally empty", "struct SomeStruct {};")?;
Marcel Hlopko89547752021-12-10 09:39:41 +00002053 assert_rs_not_matches!(generate_rs_api(&ir)?, quote! { SomeStruct });
2054 assert_cc_not_matches!(generate_rs_api_impl(&ir)?, quote! { SomeStruct });
Marcel Hlopkoa0f38662021-12-03 08:45:26 +00002055 Ok(())
2056 }
2057
2058 #[test]
Devin Jeanpierre2ed14ec2021-10-06 11:32:19 +00002059 fn test_copy_derives() {
Devin Jeanpierreccfefc82021-10-27 10:54:00 +00002060 let record = ir_record("S");
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00002061 assert_eq!(generate_derives(&record), &["Clone", "Copy"]);
Devin Jeanpierre2ed14ec2021-10-06 11:32:19 +00002062 }
2063
2064 #[test]
2065 fn test_copy_derives_not_is_trivial_abi() {
Devin Jeanpierreccfefc82021-10-27 10:54:00 +00002066 let mut record = ir_record("S");
Devin Jeanpierre2ed14ec2021-10-06 11:32:19 +00002067 record.is_trivial_abi = false;
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00002068 assert_eq!(generate_derives(&record), &[""; 0]);
Devin Jeanpierre2ed14ec2021-10-06 11:32:19 +00002069 }
2070
Devin Jeanpierre88343c72022-01-15 01:10:23 +00002071 /// Even if it's trivially relocatable, !Unpin C++ type cannot be
2072 /// cloned/copied or otherwise used by value, because values would allow
2073 /// assignment into the Pin.
2074 ///
2075 /// All !Unpin C++ types, not just non trivially relocatable ones, are
2076 /// unsafe to assign in the Rust sense.
Devin Jeanpierree6e16652021-12-22 15:54:46 +00002077 #[test]
Devin Jeanpierre88343c72022-01-15 01:10:23 +00002078 fn test_copy_derives_not_final() {
Devin Jeanpierree6e16652021-12-22 15:54:46 +00002079 let mut record = ir_record("S");
2080 record.is_final = false;
Devin Jeanpierre88343c72022-01-15 01:10:23 +00002081 assert_eq!(generate_derives(&record), &[""; 0]);
Devin Jeanpierree6e16652021-12-22 15:54:46 +00002082 }
2083
Devin Jeanpierre2ed14ec2021-10-06 11:32:19 +00002084 #[test]
2085 fn test_copy_derives_ctor_nonpublic() {
Devin Jeanpierreccfefc82021-10-27 10:54:00 +00002086 let mut record = ir_record("S");
Devin Jeanpierre2ed14ec2021-10-06 11:32:19 +00002087 for access in [ir::AccessSpecifier::Protected, ir::AccessSpecifier::Private] {
2088 record.copy_constructor.access = access;
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00002089 assert_eq!(generate_derives(&record), &[""; 0]);
Devin Jeanpierre2ed14ec2021-10-06 11:32:19 +00002090 }
2091 }
2092
2093 #[test]
2094 fn test_copy_derives_ctor_deleted() {
Devin Jeanpierreccfefc82021-10-27 10:54:00 +00002095 let mut record = ir_record("S");
Devin Jeanpierre2ed14ec2021-10-06 11:32:19 +00002096 record.copy_constructor.definition = ir::SpecialMemberDefinition::Deleted;
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00002097 assert_eq!(generate_derives(&record), &[""; 0]);
Devin Jeanpierre2ed14ec2021-10-06 11:32:19 +00002098 }
2099
2100 #[test]
Devin Jeanpierrebe2f33b2021-10-21 12:54:19 +00002101 fn test_copy_derives_ctor_nontrivial_members() {
Devin Jeanpierreccfefc82021-10-27 10:54:00 +00002102 let mut record = ir_record("S");
Devin Jeanpierrebe2f33b2021-10-21 12:54:19 +00002103 record.copy_constructor.definition = ir::SpecialMemberDefinition::NontrivialMembers;
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00002104 assert_eq!(generate_derives(&record), &[""; 0]);
Devin Jeanpierrebe2f33b2021-10-21 12:54:19 +00002105 }
2106
2107 #[test]
2108 fn test_copy_derives_ctor_nontrivial_self() {
Devin Jeanpierreccfefc82021-10-27 10:54:00 +00002109 let mut record = ir_record("S");
Devin Jeanpierre7b62e952021-12-08 21:43:30 +00002110 record.copy_constructor.definition = ir::SpecialMemberDefinition::NontrivialUserDefined;
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00002111 assert_eq!(generate_derives(&record), &[""; 0]);
Devin Jeanpierre2ed14ec2021-10-06 11:32:19 +00002112 }
2113
Devin Jeanpierre7a7328e2021-09-17 07:10:08 +00002114 #[test]
2115 fn test_ptr_func() -> Result<()> {
Marcel Hlopko89547752021-12-10 09:39:41 +00002116 let ir = ir_from_cc(&tokens_to_string(quote! {
2117 inline int* Deref(int*const* p);
2118 })?)?;
Devin Jeanpierred6da7002021-10-21 12:55:20 +00002119
Marcel Hlopko89547752021-12-10 09:39:41 +00002120 let rs_api = generate_rs_api(&ir)?;
2121 assert_rs_matches!(
2122 rs_api,
2123 quote! {
Michael Forsterdb8101a2021-10-08 06:56:03 +00002124 #[inline(always)]
Lukasz Anforowiczf7bdd392022-01-21 00:33:39 +00002125 pub unsafe fn Deref(p: *const *mut i32) -> *mut i32 {
2126 crate::detail::__rust_thunk___Z5DerefPKPi(p)
Marcel Hlopko89547752021-12-10 09:39:41 +00002127 }
2128 }
2129 );
2130 assert_rs_matches!(
2131 rs_api,
2132 quote! {
Michael Forsterdb8101a2021-10-08 06:56:03 +00002133 mod detail {
Googler55647142022-01-11 12:37:39 +00002134 #[allow(unused_imports)]
Devin Jeanpierred4dde0e2021-10-13 20:48:25 +00002135 use super::*;
Michael Forsterdb8101a2021-10-08 06:56:03 +00002136 extern "C" {
Googlera675ae02021-12-07 08:04:59 +00002137 pub(crate) fn __rust_thunk___Z5DerefPKPi(p: *const *mut i32) -> *mut i32;
Marcel Hlopko89547752021-12-10 09:39:41 +00002138 }
2139 }
2140 }
Devin Jeanpierre7a7328e2021-09-17 07:10:08 +00002141 );
Devin Jeanpierre184f9ac2021-09-17 13:47:03 +00002142
Marcel Hlopko89547752021-12-10 09:39:41 +00002143 assert_cc_matches!(
2144 generate_rs_api_impl(&ir)?,
2145 quote! {
Googlera675ae02021-12-07 08:04:59 +00002146 extern "C" int* __rust_thunk___Z5DerefPKPi(int* const * p) {
Devin Jeanpierre184f9ac2021-09-17 13:47:03 +00002147 return Deref(p);
2148 }
Marcel Hlopko89547752021-12-10 09:39:41 +00002149 }
Devin Jeanpierre184f9ac2021-09-17 13:47:03 +00002150 );
Devin Jeanpierre7a7328e2021-09-17 07:10:08 +00002151 Ok(())
2152 }
Michael Forstered642022021-10-04 09:48:25 +00002153
2154 #[test]
Googlerdb111532022-01-05 06:12:13 +00002155 fn test_const_char_ptr_func() -> Result<()> {
2156 // This is a regression test: We used to include the "const" in the name
2157 // of the CcType, which caused a panic in the code generator
2158 // ('"const char" is not a valid Ident').
2159 // It's therefore important that f() is inline so that we need to
2160 // generate a thunk for it (where we then process the CcType).
2161 let ir = ir_from_cc(&tokens_to_string(quote! {
2162 inline void f(const char *str);
2163 })?)?;
2164
2165 let rs_api = generate_rs_api(&ir)?;
2166 assert_rs_matches!(
2167 rs_api,
2168 quote! {
2169 #[inline(always)]
Lukasz Anforowiczf7bdd392022-01-21 00:33:39 +00002170 pub unsafe fn f(str: *const i8) {
2171 crate::detail::__rust_thunk___Z1fPKc(str)
Googlerdb111532022-01-05 06:12:13 +00002172 }
2173 }
2174 );
2175 assert_rs_matches!(
2176 rs_api,
2177 quote! {
2178 extern "C" {
2179 pub(crate) fn __rust_thunk___Z1fPKc(str: *const i8);
2180 }
2181 }
2182 );
2183
2184 assert_cc_matches!(
2185 generate_rs_api_impl(&ir)?,
2186 quote! {
2187 extern "C" void __rust_thunk___Z1fPKc(char const * str){ f(str) ; }
2188 }
2189 );
2190 Ok(())
2191 }
2192
2193 #[test]
Lukasz Anforowiczcf230fd2022-02-18 19:20:39 +00002194 fn test_func_ptr_where_params_are_primitive_types() -> Result<()> {
2195 let ir = ir_from_cc(r#" int (*get_ptr_to_func())(float, double); "#)?;
2196 let rs_api = generate_rs_api(&ir)?;
2197 let rs_api_impl = generate_rs_api_impl(&ir)?;
2198 assert_rs_matches!(
2199 rs_api,
2200 quote! {
2201 #[inline(always)]
2202 pub fn get_ptr_to_func() -> Option<extern "C" fn (f32, f64) -> i32> {
2203 unsafe { crate::detail::__rust_thunk___Z15get_ptr_to_funcv() }
2204 }
2205 }
2206 );
2207 assert_rs_matches!(
2208 rs_api,
2209 quote! {
2210 mod detail {
2211 #[allow(unused_imports)]
2212 use super::*;
2213 extern "C" {
2214 #[link_name = "_Z15get_ptr_to_funcv"]
2215 pub(crate) fn __rust_thunk___Z15get_ptr_to_funcv()
2216 -> Option<extern "C" fn(f32, f64) -> i32>;
2217 }
2218 }
2219 }
2220 );
2221 // Verify that no C++ thunk got generated.
2222 assert_cc_not_matches!(rs_api_impl, quote! { __rust_thunk___Z15get_ptr_to_funcv });
2223
2224 // TODO(b/217419782): Add another test for more exotic calling conventions /
2225 // abis.
2226
2227 // TODO(b/217419782): Add another test for pointer to a function that
2228 // takes/returns non-trivially-movable types by value. See also
2229 // <internal link>
2230
2231 Ok(())
2232 }
2233
2234 #[test]
2235 fn test_func_ptr_with_non_static_lifetime() -> Result<()> {
2236 let ir = ir_from_cc(
2237 r#"
Googler53f65942022-02-23 11:23:30 +00002238 [[clang::annotate("lifetimes", "-> a")]]
Lukasz Anforowiczcf230fd2022-02-18 19:20:39 +00002239 int (*get_ptr_to_func())(float, double); "#,
2240 )?;
2241 let rs_api = generate_rs_api(&ir)?;
2242 assert_rs_matches!(
2243 rs_api,
2244 quote! {
2245 // Error while generating bindings for item 'get_ptr_to_func':
2246 // Return type is not supported: Function pointers with non-'static lifetimes are not supported: int (*)(float, double)
2247 }
2248 );
2249 Ok(())
2250 }
2251
2252 #[test]
2253 fn test_func_ptr_where_params_are_raw_ptrs() -> Result<()> {
2254 let ir = ir_from_cc(r#" const int* (*get_ptr_to_func())(const int*); "#)?;
2255 let rs_api = generate_rs_api(&ir)?;
2256 let rs_api_impl = generate_rs_api_impl(&ir)?;
2257 assert_rs_matches!(
2258 rs_api,
2259 quote! {
2260 #[inline(always)]
2261 pub fn get_ptr_to_func() -> Option<extern "C" fn (*const i32) -> *const i32> {
2262 unsafe { crate::detail::__rust_thunk___Z15get_ptr_to_funcv() }
2263 }
2264 }
2265 );
2266 assert_rs_matches!(
2267 rs_api,
2268 quote! {
2269 mod detail {
2270 #[allow(unused_imports)]
2271 use super::*;
2272 extern "C" {
2273 #[link_name = "_Z15get_ptr_to_funcv"]
2274 pub(crate) fn __rust_thunk___Z15get_ptr_to_funcv()
2275 -> Option<extern "C" fn(*const i32) -> *const i32>;
2276 }
2277 }
2278 }
2279 );
2280 // Verify that no C++ thunk got generated.
2281 assert_cc_not_matches!(rs_api_impl, quote! { __rust_thunk___Z15get_ptr_to_funcv });
2282
2283 // TODO(b/217419782): Add another test where params (and the return
2284 // type) are references with lifetimes. Something like this:
2285 // #pragma clang lifetime_elision
2286 // const int& (*get_ptr_to_func())(const int&, const int&); "#)?;
2287 // 1) Need to investigate why this fails - seeing raw pointers in Rust
2288 // seems to indicate that no lifetimes are present at the `importer.cc`
2289 // level. Maybe lifetime elision doesn't support this scenario? Unclear
Googler53f65942022-02-23 11:23:30 +00002290 // how to explicitly apply [[clang::annotate("lifetimes", "a, b -> a")]]
Lukasz Anforowiczcf230fd2022-02-18 19:20:39 +00002291 // to the _inner_ function.
2292 // 2) It is important to have 2 reference parameters, so see if the problem
2293 // of passing `lifetimes` by value would have been caught - see:
2294 // cl/428079010/depot/rs_bindings_from_cc/
2295 // importer.cc?version=s6#823
2296
2297 // TODO(b/217419782): Decide what to do if the C++ pointer is *not*
2298 // annotated with a lifetime - emit `unsafe fn(...) -> ...` in that
2299 // case?
2300
2301 Ok(())
2302 }
2303
2304 #[test]
Michael Forstered642022021-10-04 09:48:25 +00002305 fn test_item_order() -> Result<()> {
Marcel Hlopko89547752021-12-10 09:39:41 +00002306 let ir = ir_from_cc(
2307 "int first_func();
2308 struct FirstStruct {};
2309 int second_func();
2310 struct SecondStruct {};",
2311 )?;
Michael Forstered642022021-10-04 09:48:25 +00002312
Marcel Hlopko89547752021-12-10 09:39:41 +00002313 let rs_api = rs_tokens_to_formatted_string(generate_rs_api(&ir)?)?;
2314
Lukasz Anforowiczdd9ae0f2022-02-17 15:52:53 +00002315 let idx = |s: &str| rs_api.find(s).ok_or_else(|| anyhow!("'{}' missing", s));
Michael Forstered642022021-10-04 09:48:25 +00002316
2317 let f1 = idx("fn first_func")?;
2318 let f2 = idx("fn second_func")?;
2319 let s1 = idx("struct FirstStruct")?;
2320 let s2 = idx("struct SecondStruct")?;
Googlera675ae02021-12-07 08:04:59 +00002321 let t1 = idx("fn __rust_thunk___Z10first_funcv")?;
2322 let t2 = idx("fn __rust_thunk___Z11second_funcv")?;
Michael Forstered642022021-10-04 09:48:25 +00002323
2324 assert!(f1 < s1);
2325 assert!(s1 < f2);
2326 assert!(f2 < s2);
2327 assert!(s2 < t1);
2328 assert!(t1 < t2);
2329
2330 Ok(())
2331 }
Michael Forster028800b2021-10-05 12:39:59 +00002332
2333 #[test]
Devin Jeanpierrec80e6242022-02-03 01:56:40 +00002334 fn test_base_class_subobject_layout() -> Result<()> {
2335 let ir = ir_from_cc(
2336 r#"
2337 // We use a class here to force `Derived::z` to live inside the tail padding of `Base`.
2338 // On the Itanium ABI, this would not happen if `Base` were a POD type.
Devin Jeanpierre56777022022-02-03 01:57:15 +00002339 class Base {__INT64_TYPE__ x; char y;};
2340 struct Derived final : Base {__INT16_TYPE__ z;};
Devin Jeanpierrec80e6242022-02-03 01:56:40 +00002341 "#,
2342 )?;
2343 let rs_api = generate_rs_api(&ir)?;
2344 assert_rs_matches!(
2345 rs_api,
2346 quote! {
2347 #[repr(C, align(8))]
2348 pub struct Derived {
2349 __base_class_subobjects: [std::mem::MaybeUninit<u8>; 10],
2350 pub z: i16,
2351 }
2352 }
2353 );
2354 Ok(())
2355 }
2356
2357 /// The same as test_base_class_subobject_layout, but with multiple
2358 /// inheritance.
2359 #[test]
2360 fn test_base_class_multiple_inheritance_subobject_layout() -> Result<()> {
2361 let ir = ir_from_cc(
2362 r#"
Devin Jeanpierre56777022022-02-03 01:57:15 +00002363 class Base1 {__INT64_TYPE__ x;};
Devin Jeanpierrec80e6242022-02-03 01:56:40 +00002364 class Base2 {char y;};
Devin Jeanpierre56777022022-02-03 01:57:15 +00002365 struct Derived final : Base1, Base2 {__INT16_TYPE__ z;};
Devin Jeanpierrec80e6242022-02-03 01:56:40 +00002366 "#,
2367 )?;
2368 let rs_api = generate_rs_api(&ir)?;
2369 assert_rs_matches!(
2370 rs_api,
2371 quote! {
2372 #[repr(C, align(8))]
2373 pub struct Derived {
2374 __base_class_subobjects: [std::mem::MaybeUninit<u8>; 10],
2375 pub z: i16,
2376 }
2377 }
2378 );
2379 Ok(())
2380 }
2381
2382 /// The same as test_base_class_subobject_layout, but with a chain of
2383 /// inheritance.
2384 #[test]
2385 fn test_base_class_deep_inheritance_subobject_layout() -> Result<()> {
2386 let ir = ir_from_cc(
2387 r#"
Devin Jeanpierre56777022022-02-03 01:57:15 +00002388 class Base1 {__INT64_TYPE__ x;};
Devin Jeanpierrec80e6242022-02-03 01:56:40 +00002389 class Base2 : Base1 {char y;};
Devin Jeanpierre56777022022-02-03 01:57:15 +00002390 struct Derived final : Base2 {__INT16_TYPE__ z;};
Devin Jeanpierrec80e6242022-02-03 01:56:40 +00002391 "#,
2392 )?;
2393 let rs_api = generate_rs_api(&ir)?;
2394 assert_rs_matches!(
2395 rs_api,
2396 quote! {
2397 #[repr(C, align(8))]
2398 pub struct Derived {
2399 __base_class_subobjects: [std::mem::MaybeUninit<u8>; 10],
2400 pub z: i16,
2401 }
2402 }
2403 );
2404 Ok(())
2405 }
2406
2407 /// For derived classes with no data members, we can't use the offset of the
2408 /// first member to determine the size of the base class subobjects.
2409 #[test]
2410 fn test_base_class_subobject_fieldless_layout() -> Result<()> {
2411 let ir = ir_from_cc(
2412 r#"
Devin Jeanpierre56777022022-02-03 01:57:15 +00002413 class Base {__INT64_TYPE__ x; char y;};
Devin Jeanpierrec80e6242022-02-03 01:56:40 +00002414 struct Derived final : Base {};
2415 "#,
2416 )?;
2417 let rs_api = generate_rs_api(&ir)?;
2418 assert_rs_matches!(
2419 rs_api,
2420 quote! {
2421 #[repr(C, align(8))]
2422 pub struct Derived {
2423 __base_class_subobjects: [std::mem::MaybeUninit<u8>; 9],
2424 }
2425 }
2426 );
2427 Ok(())
2428 }
2429
2430 #[test]
2431 fn test_base_class_subobject_empty_fieldless() -> Result<()> {
2432 let ir = ir_from_cc(
2433 r#"
2434 class Base {};
2435 struct Derived final : Base {};
2436 "#,
2437 )?;
2438 let rs_api = generate_rs_api(&ir)?;
2439 assert_rs_matches!(
2440 rs_api,
2441 quote! {
2442 #[repr(C)]
2443 pub struct Derived {
2444 __base_class_subobjects: [std::mem::MaybeUninit<u8>; 0],
2445 /// Prevent empty C++ struct being zero-size in Rust.
2446 placeholder: std::mem::MaybeUninit<u8>,
2447 }
2448 }
2449 );
2450 Ok(())
2451 }
2452
2453 #[test]
2454 fn test_base_class_subobject_empty() -> Result<()> {
2455 let ir = ir_from_cc(
2456 r#"
2457 class Base {};
2458 struct Derived final : Base {};
2459 "#,
2460 )?;
2461 let rs_api = generate_rs_api(&ir)?;
2462 assert_rs_matches!(
2463 rs_api,
2464 quote! {
2465 #[repr(C)]
2466 pub struct Derived {
2467 __base_class_subobjects: [std::mem::MaybeUninit<u8>; 0],
2468 /// Prevent empty C++ struct being zero-size in Rust.
2469 placeholder: std::mem::MaybeUninit<u8>,
2470 }
2471 }
2472 );
2473 Ok(())
2474 }
2475
Devin Jeanpierreb69bcae2022-02-03 09:45:50 +00002476 /// When a field is [[no_unique_address]], it occupies the space up to the
2477 /// next field.
2478 #[test]
2479 fn test_no_unique_address() -> Result<()> {
2480 let ir = ir_from_cc(
2481 r#"
2482 class Field1 {__INT64_TYPE__ x;};
2483 class Field2 {char y;};
2484 struct Struct final {
2485 [[no_unique_address]] Field1 field1;
2486 [[no_unique_address]] Field2 field2;
2487 __INT16_TYPE__ z;
2488 };
2489 "#,
2490 )?;
2491 let rs_api = generate_rs_api(&ir)?;
2492 assert_rs_matches!(
2493 rs_api,
2494 quote! {
2495 #[derive(Clone, Copy)]
2496 #[repr(C, align(8))]
2497 pub struct Struct {
2498 field1: [std::mem::MaybeUninit<u8>; 8],
2499 field2: [std::mem::MaybeUninit<u8>; 2],
2500 pub z: i16,
2501 }
Devin Jeanpierre58181ac2022-02-14 21:30:05 +00002502
2503 impl Struct {
2504 pub fn field1(&self) -> &Field1 {
2505 unsafe {&* (&self.field1 as *const _ as *const Field1)}
2506 }
2507 pub fn field2(&self) -> &Field2 {
2508 unsafe {&* (&self.field2 as *const _ as *const Field2)}
2509 }
2510 }
Devin Jeanpierreb69bcae2022-02-03 09:45:50 +00002511 }
2512 );
2513 Ok(())
2514 }
2515
2516 /// When a [[no_unique_address]] field is the last one, it occupies the rest
2517 /// of the object.
2518 #[test]
2519 fn test_no_unique_address_last_field() -> Result<()> {
2520 let ir = ir_from_cc(
2521 r#"
2522 class Field1 {__INT64_TYPE__ x;};
2523 class Field2 {char y;};
2524 struct Struct final {
2525 [[no_unique_address]] Field1 field1;
2526 [[no_unique_address]] Field2 field2;
2527 };
2528 "#,
2529 )?;
2530 let rs_api = generate_rs_api(&ir)?;
2531 assert_rs_matches!(
2532 rs_api,
2533 quote! {
2534 #[derive(Clone, Copy)]
2535 #[repr(C, align(8))]
2536 pub struct Struct {
2537 field1: [std::mem::MaybeUninit<u8>; 8],
2538 field2: [std::mem::MaybeUninit<u8>; 8],
2539 }
2540 }
2541 );
2542 Ok(())
2543 }
2544
2545 #[test]
2546 fn test_no_unique_address_empty() -> Result<()> {
2547 let ir = ir_from_cc(
2548 r#"
2549 class Field {};
2550 struct Struct final {
2551 [[no_unique_address]] Field field;
2552 int x;
2553 };
2554 "#,
2555 )?;
2556 let rs_api = generate_rs_api(&ir)?;
2557 assert_rs_matches!(
2558 rs_api,
2559 quote! {
2560 #[repr(C, align(4))]
2561 pub struct Struct {
2562 field: [std::mem::MaybeUninit<u8>; 0],
2563 pub x: i32,
2564 }
2565 }
2566 );
2567 Ok(())
2568 }
2569
2570 #[test]
2571 fn test_base_class_subobject_empty_last_field() -> Result<()> {
2572 let ir = ir_from_cc(
2573 r#"
2574 class Field {};
2575 struct Struct final {
2576 [[no_unique_address]] Field field;
2577 };
2578 "#,
2579 )?;
2580 let rs_api = generate_rs_api(&ir)?;
2581 assert_rs_matches!(
2582 rs_api,
2583 quote! {
2584 #[repr(C)]
2585 pub struct Struct {
2586 field: [std::mem::MaybeUninit<u8>; 1],
2587 }
2588 }
2589 );
2590 Ok(())
2591 }
2592
Devin Jeanpierrec80e6242022-02-03 01:56:40 +00002593 #[test]
Teddy Katz76fa42b2022-02-23 01:22:56 +00002594 fn test_generate_enum_basic() -> Result<()> {
2595 let ir = ir_from_cc("enum Color { kRed = 5, kBlue };")?;
2596 let rs_api = generate_rs_api(&ir)?;
2597 assert_rs_matches!(
2598 rs_api,
2599 quote! {
2600 #[repr(transparent)]
2601 #[derive(Debug, PartialEq, Eq, Copy, Clone, Hash, PartialOrd, Ord)]
2602 pub struct Color(u32);
2603 impl Color {
2604 pub const kRed: Color = Color(5);
2605 pub const kBlue: Color = Color(6);
2606 }
2607 impl From<u32> for Color {
2608 fn from(value: u32) -> Color {
2609 Color(v)
2610 }
2611 }
2612 impl From<Color> for u32 {
2613 fn from(value: Color) -> u32 {
2614 v.0
2615 }
2616 }
2617 }
2618 );
2619 Ok(())
2620 }
2621
2622 #[test]
2623 fn test_generate_scoped_enum_basic() -> Result<()> {
2624 let ir = ir_from_cc("enum class Color { kRed = -5, kBlue };")?;
2625 let rs_api = generate_rs_api(&ir)?;
2626 assert_rs_matches!(
2627 rs_api,
2628 quote! {
2629 #[repr(transparent)]
2630 #[derive(Debug, PartialEq, Eq, Copy, Clone, Hash, PartialOrd, Ord)]
2631 pub struct Color(i32);
2632 impl Color {
2633 pub const kRed: Color = Color(-5);
2634 pub const kBlue: Color = Color(-4);
2635 }
2636 impl From<i32> for Color {
2637 fn from(value: i32) -> Color {
2638 Color(v)
2639 }
2640 }
2641 impl From<Color> for i32 {
2642 fn from(value: Color) -> i32 {
2643 v.0
2644 }
2645 }
2646 }
2647 );
2648 Ok(())
2649 }
2650
2651 #[test]
2652 fn test_generate_enum_with_64_bit_signed_vals() -> Result<()> {
2653 let ir = ir_from_cc(
2654 "enum Color : long { kViolet = -9223372036854775807 - 1LL, kRed = -5, kBlue, kGreen = 3, kMagenta = 9223372036854775807 };",
2655 )?;
2656 let rs_api = generate_rs_api(&ir)?;
2657 assert_rs_matches!(
2658 rs_api,
2659 quote! {
2660 #[repr(transparent)]
2661 #[derive(Debug, PartialEq, Eq, Copy, Clone, Hash, PartialOrd, Ord)]
2662 pub struct Color(i64);
2663 impl Color {
2664 pub const kViolet: Color = Color(-9223372036854775808);
2665 pub const kRed: Color = Color(-5);
2666 pub const kBlue: Color = Color(-4);
2667 pub const kGreen: Color = Color(3);
2668 pub const kMagenta: Color = Color(9223372036854775807);
2669 }
2670 impl From<i64> for Color {
2671 fn from(value: i64) -> Color {
2672 Color(v)
2673 }
2674 }
2675 impl From<Color> for i64 {
2676 fn from(value: Color) -> i64 {
2677 v.0
2678 }
2679 }
2680 }
2681 );
2682 Ok(())
2683 }
2684
2685 #[test]
2686 fn test_generate_enum_with_64_bit_unsigned_vals() -> Result<()> {
2687 let ir = ir_from_cc(
2688 "enum Color: unsigned long { kRed, kBlue, kLimeGreen = 18446744073709551615 };",
2689 )?;
2690 let rs_api = generate_rs_api(&ir)?;
2691 assert_rs_matches!(
2692 rs_api,
2693 quote! {
2694 #[repr(transparent)]
2695 #[derive(Debug, PartialEq, Eq, Copy, Clone, Hash, PartialOrd, Ord)]
2696 pub struct Color(u64);
2697 impl Color {
2698 pub const kRed: Color = Color(0);
2699 pub const kBlue: Color = Color(1);
2700 pub const kLimeGreen: Color = Color(18446744073709551615);
2701 }
2702 impl From<u64> for Color {
2703 fn from(value: u64) -> Color {
2704 Color(v)
2705 }
2706 }
2707 impl From<Color> for u64 {
2708 fn from(value: Color) -> u64 {
2709 v.0
2710 }
2711 }
2712 }
2713 );
2714 Ok(())
2715 }
2716
2717 #[test]
2718 fn test_generate_enum_with_32_bit_signed_vals() -> Result<()> {
2719 let ir = ir_from_cc(
2720 "enum Color { kViolet = -2147483647 - 1, kRed = -5, kBlue, kGreen = 3, kMagenta = 2147483647 };",
2721 )?;
2722 let rs_api = generate_rs_api(&ir)?;
2723 assert_rs_matches!(
2724 rs_api,
2725 quote! {
2726 #[repr(transparent)]
2727 #[derive(Debug, PartialEq, Eq, Copy, Clone, Hash, PartialOrd, Ord)]
2728 pub struct Color(i32);
2729 impl Color {
2730 pub const kViolet: Color = Color(-2147483648);
2731 pub const kRed: Color = Color(-5);
2732 pub const kBlue: Color = Color(-4);
2733 pub const kGreen: Color = Color(3);
2734 pub const kMagenta: Color = Color(2147483647);
2735 }
2736 impl From<i32> for Color {
2737 fn from(value: i32) -> Color {
2738 Color(v)
2739 }
2740 }
2741 impl From<Color> for i32 {
2742 fn from(value: Color) -> i32 {
2743 v.0
2744 }
2745 }
2746 }
2747 );
2748 Ok(())
2749 }
2750
2751 #[test]
2752 fn test_generate_enum_with_32_bit_unsigned_vals() -> Result<()> {
2753 let ir = ir_from_cc("enum Color: unsigned int { kRed, kBlue, kLimeGreen = 4294967295 };")?;
2754 let rs_api = generate_rs_api(&ir)?;
2755 assert_rs_matches!(
2756 rs_api,
2757 quote! {
2758 #[repr(transparent)]
2759 #[derive(Debug, PartialEq, Eq, Copy, Clone, Hash, PartialOrd, Ord)]
2760 pub struct Color(u32);
2761 impl Color {
2762 pub const kRed: Color = Color(0);
2763 pub const kBlue: Color = Color(1);
2764 pub const kLimeGreen: Color = Color(4294967295);
2765 }
2766 impl From<u32> for Color {
2767 fn from(value: u32) -> Color {
2768 Color(v)
2769 }
2770 }
2771 impl From<Color> for u32 {
2772 fn from(value: Color) -> u32 {
2773 v.0
2774 }
2775 }
2776 }
2777 );
2778 Ok(())
2779 }
2780
2781 #[test]
Michael Forster409d9412021-10-07 08:35:29 +00002782 fn test_doc_comment_func() -> Result<()> {
Marcel Hlopko89547752021-12-10 09:39:41 +00002783 let ir = ir_from_cc(
2784 "
2785 // Doc Comment
2786 // with two lines
2787 int func();",
2788 )?;
Michael Forster409d9412021-10-07 08:35:29 +00002789
Marcel Hlopko89547752021-12-10 09:39:41 +00002790 assert_rs_matches!(
2791 generate_rs_api(&ir)?,
2792 // leading space is intentional so there is a space between /// and the text of the
2793 // comment
2794 quote! {
2795 #[doc = " Doc Comment\n with two lines"]
2796 #[inline(always)]
2797 pub fn func
2798 }
Michael Forster409d9412021-10-07 08:35:29 +00002799 );
2800
2801 Ok(())
2802 }
2803
2804 #[test]
2805 fn test_doc_comment_record() -> Result<()> {
Marcel Hlopko89547752021-12-10 09:39:41 +00002806 let ir = ir_from_cc(
2807 "// Doc Comment\n\
2808 //\n\
2809 // * with bullet\n\
Devin Jeanpierre88343c72022-01-15 01:10:23 +00002810 struct SomeStruct final {\n\
Marcel Hlopko89547752021-12-10 09:39:41 +00002811 // Field doc\n\
2812 int field;\
2813 };",
2814 )?;
Michael Forster028800b2021-10-05 12:39:59 +00002815
Marcel Hlopko89547752021-12-10 09:39:41 +00002816 assert_rs_matches!(
2817 generate_rs_api(&ir)?,
2818 quote! {
2819 #[doc = " Doc Comment\n \n * with bullet"]
2820 #[derive(Clone, Copy)]
2821 #[repr(C)]
2822 pub struct SomeStruct {
2823 # [doc = " Field doc"]
2824 pub field: i32,
2825 }
2826 }
Michael Forstercc5941a2021-10-07 07:12:24 +00002827 );
Michael Forster028800b2021-10-05 12:39:59 +00002828 Ok(())
2829 }
Devin Jeanpierre91de7012021-10-21 12:53:51 +00002830
Devin Jeanpierre96839c12021-12-14 00:27:38 +00002831 #[test]
Devin Jeanpierre56777022022-02-03 01:57:15 +00002832 fn test_unambiguous_public_bases() -> Result<()> {
2833 let ir = ir_from_cc_dependency(
2834 "
2835 struct VirtualBase {};
2836 struct PrivateBase {};
2837 struct ProtectedBase {};
2838 struct UnambiguousPublicBase {};
2839 struct AmbiguousPublicBase {};
2840 struct MultipleInheritance : UnambiguousPublicBase, AmbiguousPublicBase {};
2841 struct Derived : private PrivateBase, protected ProtectedBase, MultipleInheritance, AmbiguousPublicBase, virtual VirtualBase {};
2842 ",
2843 "",
2844 )?;
2845 let rs_api = generate_rs_api(&ir)?;
2846 // TODO(b/216195042): virtual bases.
2847 assert_rs_not_matches!(rs_api, quote! { From<&'a Derived> for &'a VirtualBase });
2848 assert_rs_matches!(rs_api, quote! { From<&'a Derived> for &'a UnambiguousPublicBase });
2849 assert_rs_matches!(rs_api, quote! { From<&'a Derived> for &'a MultipleInheritance });
2850 assert_rs_not_matches!(rs_api, quote! {From<&'a Derived> for &'a PrivateBase});
2851 assert_rs_not_matches!(rs_api, quote! {From<&'a Derived> for &'a ProtectedBase});
2852 assert_rs_not_matches!(rs_api, quote! {From<&'a Derived> for &'a AmbiguousPublicBase});
2853 Ok(())
2854 }
2855
2856 /// Contrary to intuitions: a base class conversion is ambiguous even if the
2857 /// ambiguity is from a private base class cast that you can't even
2858 /// perform.
2859 ///
2860 /// Explanation (courtesy James Dennett):
2861 ///
2862 /// > Once upon a time, there was a rule in C++ that changing all access
2863 /// > specifiers to "public" would not change the meaning of code.
2864 /// > That's no longer true, but some of its effects can still be seen.
2865 ///
2866 /// So, we need to be sure to not allow casting to privately-ambiguous
2867 /// bases.
2868 #[test]
2869 fn test_unambiguous_public_bases_private_ambiguity() -> Result<()> {
2870 let ir = ir_from_cc_dependency(
2871 "
2872 struct Base {};
2873 struct Intermediate : public Base {};
2874 struct Derived : Base, private Intermediate {};
2875 ",
2876 "",
2877 )?;
2878 let rs_api = generate_rs_api(&ir)?;
2879 assert_rs_not_matches!(rs_api, quote! { From<&'a Derived> for &'a Base });
2880 Ok(())
2881 }
2882
2883 #[test]
Devin Jeanpierre96839c12021-12-14 00:27:38 +00002884 fn test_virtual_thunk() -> Result<()> {
2885 let ir = ir_from_cc("struct Polymorphic { virtual void Foo(); };")?;
2886
2887 assert_cc_matches!(
2888 generate_rs_api_impl(&ir)?,
2889 quote! {
Googler972d3582022-01-11 10:17:22 +00002890 extern "C" void __rust_thunk___ZN11Polymorphic3FooEv(class Polymorphic * __this)
Devin Jeanpierre96839c12021-12-14 00:27:38 +00002891 }
2892 );
2893 Ok(())
2894 }
2895
Devin Jeanpierree6e16652021-12-22 15:54:46 +00002896 /// A trivially relocatable final struct is safe to use in Rust as normal,
2897 /// and is Unpin.
2898 #[test]
2899 fn test_no_negative_impl_unpin() -> Result<()> {
2900 let ir = ir_from_cc("struct Trivial final {};")?;
2901 let rs_api = generate_rs_api(&ir)?;
2902 assert_rs_not_matches!(rs_api, quote! {impl !Unpin});
2903 Ok(())
2904 }
2905
2906 /// A non-final struct, even if it's trivial, is not usable by mut
2907 /// reference, and so is !Unpin.
2908 #[test]
2909 fn test_negative_impl_unpin_nonfinal() -> Result<()> {
2910 let ir = ir_from_cc("struct Nonfinal {};")?;
2911 let rs_api = generate_rs_api(&ir)?;
2912 assert_rs_matches!(rs_api, quote! {impl !Unpin for Nonfinal {}});
2913 Ok(())
2914 }
2915
Devin Jeanpierre91de7012021-10-21 12:53:51 +00002916 /// At the least, a trivial type should have no drop impl if or until we add
2917 /// empty drop impls.
2918 #[test]
2919 fn test_no_impl_drop() -> Result<()> {
Googler7cced422021-12-06 11:58:39 +00002920 let ir = ir_from_cc("struct Trivial {};")?;
Marcel Hlopko89547752021-12-10 09:39:41 +00002921 let rs_api = rs_tokens_to_formatted_string(generate_rs_api(&ir)?)?;
Devin Jeanpierre91de7012021-10-21 12:53:51 +00002922 assert!(!rs_api.contains("impl Drop"));
2923 Ok(())
2924 }
2925
Devin Jeanpierre7e9a1de2021-12-03 08:04:22 +00002926 /// User-defined destructors *must* become Drop impls with ManuallyDrop
2927 /// fields
Devin Jeanpierre91de7012021-10-21 12:53:51 +00002928 #[test]
Devin Jeanpierre7e9a1de2021-12-03 08:04:22 +00002929 fn test_impl_drop_user_defined_destructor() -> Result<()> {
Googler7cced422021-12-06 11:58:39 +00002930 let ir = ir_from_cc(
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00002931 r#" struct NontrivialStruct { ~NontrivialStruct(); };
2932 struct UserDefinedDestructor {
Devin Jeanpierre91de7012021-10-21 12:53:51 +00002933 ~UserDefinedDestructor();
Devin Jeanpierre7e9a1de2021-12-03 08:04:22 +00002934 int x;
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00002935 NontrivialStruct nts;
Devin Jeanpierre91de7012021-10-21 12:53:51 +00002936 };"#,
2937 )?;
2938 let rs_api = generate_rs_api(&ir)?;
Lukasz Anforowicz6d553632022-01-06 21:36:14 +00002939 assert_rs_matches!(
2940 rs_api,
2941 quote! {
2942 impl Drop for UserDefinedDestructor {
2943 #[inline(always)]
2944 fn drop(&mut self) {
2945 unsafe { crate::detail::__rust_thunk___ZN21UserDefinedDestructorD1Ev(self) }
2946 }
2947 }
2948 }
2949 );
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00002950 assert_rs_matches!(rs_api, quote! {pub x: i32,});
2951 assert_rs_matches!(rs_api, quote! {pub nts: std::mem::ManuallyDrop<NontrivialStruct>,});
Devin Jeanpierre7e9a1de2021-12-03 08:04:22 +00002952 Ok(())
2953 }
2954
Lukasz Anforowicz6d553632022-01-06 21:36:14 +00002955 /// nontrivial types without user-defined destructors should invoke
2956 /// the C++ destructor to preserve the order of field destructions.
Devin Jeanpierre7e9a1de2021-12-03 08:04:22 +00002957 #[test]
2958 fn test_impl_drop_nontrivial_member_destructor() -> Result<()> {
2959 // TODO(jeanpierreda): This would be cleaner if the UserDefinedDestructor code were
2960 // omitted. For example, we simulate it so that UserDefinedDestructor
2961 // comes from another library.
Googler7cced422021-12-06 11:58:39 +00002962 let ir = ir_from_cc(
Devin Jeanpierre88343c72022-01-15 01:10:23 +00002963 r#"struct UserDefinedDestructor final {
Devin Jeanpierre7e9a1de2021-12-03 08:04:22 +00002964 ~UserDefinedDestructor();
2965 };
Devin Jeanpierre88343c72022-01-15 01:10:23 +00002966 struct TrivialStruct final { int i; };
2967 struct NontrivialMembers final {
Devin Jeanpierre7e9a1de2021-12-03 08:04:22 +00002968 UserDefinedDestructor udd;
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00002969 TrivialStruct ts;
Devin Jeanpierre7e9a1de2021-12-03 08:04:22 +00002970 int x;
2971 };"#,
2972 )?;
2973 let rs_api = generate_rs_api(&ir)?;
Lukasz Anforowicz6d553632022-01-06 21:36:14 +00002974 assert_rs_matches!(
2975 rs_api,
2976 quote! {
2977 impl Drop for NontrivialMembers {
2978 #[inline(always)]
2979 fn drop(&mut self) {
2980 unsafe { crate::detail::__rust_thunk___ZN17NontrivialMembersD1Ev(self) }
2981 }
2982 }
2983 }
2984 );
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00002985 assert_rs_matches!(rs_api, quote! {pub x: i32,});
2986 assert_rs_matches!(rs_api, quote! {pub ts: TrivialStruct,});
Lukasz Anforowicz6d553632022-01-06 21:36:14 +00002987 assert_rs_matches!(
2988 rs_api,
2989 quote! {pub udd: std::mem::ManuallyDrop<UserDefinedDestructor>,}
2990 );
Devin Jeanpierre7e9a1de2021-12-03 08:04:22 +00002991 Ok(())
2992 }
2993
2994 /// Trivial types (at least those that are mapped to Copy rust types) do not
2995 /// get a Drop impl.
2996 #[test]
2997 fn test_impl_drop_trivial() -> Result<()> {
Googler7cced422021-12-06 11:58:39 +00002998 let ir = ir_from_cc(
Devin Jeanpierre88343c72022-01-15 01:10:23 +00002999 r#"struct Trivial final {
Devin Jeanpierre7e9a1de2021-12-03 08:04:22 +00003000 ~Trivial() = default;
3001 int x;
3002 };"#,
3003 )?;
3004 let rs_api = generate_rs_api(&ir)?;
Marcel Hlopko89547752021-12-10 09:39:41 +00003005 assert_rs_not_matches!(rs_api, quote! {impl Drop});
3006 assert_rs_matches!(rs_api, quote! {pub x: i32});
Lukasz Anforowicz2f074162022-01-06 22:50:51 +00003007 let rs_api_impl = generate_rs_api_impl(&ir)?;
3008 // TODO(b/213326125): Avoid generating thunk impls that are never called.
3009 // (The test assertion below should be reversed once this bug is fixed.)
3010 assert_cc_matches!(rs_api_impl, quote! { std::destroy_at });
Devin Jeanpierre91de7012021-10-21 12:53:51 +00003011 Ok(())
3012 }
Devin Jeanpierre45cb1162021-10-27 10:54:28 +00003013
3014 #[test]
Lukasz Anforowicze643ec92021-12-22 15:45:15 +00003015 fn test_impl_default_explicitly_defaulted_constructor() -> Result<()> {
3016 let ir = ir_from_cc(
Lukasz Anforowicz95551272022-01-20 00:02:24 +00003017 r#"#pragma clang lifetime_elision
3018 struct DefaultedConstructor final {
Lukasz Anforowicze643ec92021-12-22 15:45:15 +00003019 DefaultedConstructor() = default;
3020 };"#,
3021 )?;
3022 let rs_api = generate_rs_api(&ir)?;
3023 assert_rs_matches!(
3024 rs_api,
3025 quote! {
3026 impl Default for DefaultedConstructor {
3027 #[inline(always)]
3028 fn default() -> Self {
Lukasz Anforowiczbedbdee2022-01-05 01:14:52 +00003029 let mut tmp = std::mem::MaybeUninit::<Self>::zeroed();
Lukasz Anforowicze643ec92021-12-22 15:45:15 +00003030 unsafe {
Lukasz Anforowicz40c2eb82022-01-11 18:22:31 +00003031 crate::detail::__rust_thunk___ZN20DefaultedConstructorC1Ev(&mut tmp);
Lukasz Anforowicze643ec92021-12-22 15:45:15 +00003032 tmp.assume_init()
3033 }
3034 }
3035 }
3036 }
3037 );
3038 let rs_api_impl = generate_rs_api_impl(&ir)?;
3039 assert_cc_matches!(
3040 rs_api_impl,
3041 quote! {
3042 extern "C" void __rust_thunk___ZN20DefaultedConstructorC1Ev(
Googler972d3582022-01-11 10:17:22 +00003043 class DefaultedConstructor* __this) {
Lukasz Anforowicz4457baf2021-12-23 17:24:04 +00003044 rs_api_impl_support::construct_at (__this) ;
Lukasz Anforowicze643ec92021-12-22 15:45:15 +00003045 }
3046 }
3047 );
3048 Ok(())
3049 }
3050
3051 #[test]
Lukasz Anforowicz326c4e42022-01-27 14:43:00 +00003052 fn test_impl_clone_that_propagates_lifetime() -> Result<()> {
3053 // This test covers the case where a single lifetime applies to 1)
3054 // the `__this` parameter and 2) other constructor parameters. For
3055 // example, maybe the newly constructed object needs to have the
3056 // same lifetime as the constructor's parameter. (This might require
3057 // annotating the whole C++ struct with a lifetime, so maybe the
3058 // example below is not fully realistic/accurate...).
3059 let mut ir = ir_from_cc(
3060 r#"#pragma clang lifetime_elision
3061 struct Foo final {
Googler53f65942022-02-23 11:23:30 +00003062 [[clang::annotate("lifetimes", "a: a")]]
Lukasz Anforowicz326c4e42022-01-27 14:43:00 +00003063 Foo(const int& i);
3064 };"#,
3065 )?;
3066 let ctor: &mut Func = ir
3067 .items_mut()
3068 .filter_map(|item| match item {
3069 Item::Func(func) => Some(func),
3070 _ => None,
3071 })
3072 .find(|f| {
3073 matches!(&f.name, UnqualifiedIdentifier::Constructor)
3074 && f.params.get(1).map(|p| p.identifier.identifier == "i").unwrap_or_default()
3075 })
3076 .unwrap();
3077 {
3078 // Double-check that the test scenario set up above uses the same lifetime
3079 // for both of the constructor's parameters: `__this` and `i`.
3080 assert_eq!(ctor.params.len(), 2);
3081 let this_lifetime: LifetimeId =
3082 *ctor.params[0].type_.rs_type.lifetime_args.first().unwrap();
3083 let i_lifetime: LifetimeId =
3084 *ctor.params[1].type_.rs_type.lifetime_args.first_mut().unwrap();
3085 assert_eq!(i_lifetime, this_lifetime);
3086 }
3087
3088 // Before cl/423346348 the generated Rust code would incorrectly look
3089 // like this (note the mismatched 'a and 'b lifetimes):
3090 // fn from<'b>(i: &'a i32) -> Self
3091 // After this CL, this scenario will result in an explicit error.
3092 let err = generate_rs_api(&ir).unwrap_err();
3093 let msg = format!("{}", err);
3094 assert!(
3095 msg.contains("The lifetime of `__this` is unexpectedly also used by another parameter")
3096 );
3097 Ok(())
3098 }
3099
3100 #[test]
Lukasz Anforowicz9bab8352021-12-22 17:35:31 +00003101 fn test_impl_default_non_trivial_struct() -> Result<()> {
3102 let ir = ir_from_cc(
Lukasz Anforowicz71716b72022-01-26 17:05:05 +00003103 r#"#pragma clang lifetime_elision
3104 struct NonTrivialStructWithConstructors final {
Lukasz Anforowicz9bab8352021-12-22 17:35:31 +00003105 NonTrivialStructWithConstructors();
3106 ~NonTrivialStructWithConstructors(); // Non-trivial
3107 };"#,
3108 )?;
3109 let rs_api = generate_rs_api(&ir)?;
3110 assert_rs_not_matches!(rs_api, quote! {impl Default});
3111 Ok(())
3112 }
3113
3114 #[test]
Lukasz Anforowicz71716b72022-01-26 17:05:05 +00003115 fn test_impl_from_for_explicit_conversion_constructor() -> Result<()> {
3116 let ir = ir_from_cc(
3117 r#"#pragma clang lifetime_elision
3118 struct SomeStruct final {
3119 explicit SomeStruct(int i);
3120 };"#,
3121 )?;
3122 let rs_api = generate_rs_api(&ir)?;
3123 // As discussed in b/214020567 for now we only generate `From::from` bindings
3124 // for *implicit* C++ conversion constructors.
3125 assert_rs_not_matches!(rs_api, quote! {impl From});
3126 Ok(())
3127 }
3128
3129 #[test]
3130 fn test_impl_from_for_implicit_conversion_constructor() -> Result<()> {
3131 let ir = ir_from_cc(
3132 r#"#pragma clang lifetime_elision
3133 struct SomeStruct final {
3134 SomeStruct(int i); // implicit - no `explicit` keyword
3135 };"#,
3136 )?;
3137 let rs_api = generate_rs_api(&ir)?;
3138 // As discussed in b/214020567 we generate `From::from` bindings for
3139 // *implicit* C++ conversion constructors.
3140 assert_rs_matches!(
3141 rs_api,
3142 quote! {
3143 impl From<i32> for SomeStruct {
3144 #[inline(always)]
3145 fn from(i: i32) -> Self {
3146 let mut tmp = std::mem::MaybeUninit::<Self>::zeroed();
3147 unsafe {
3148 crate::detail::__rust_thunk___ZN10SomeStructC1Ei(&mut tmp, i);
3149 tmp.assume_init()
3150 }
3151 }
3152 }
3153 }
3154 );
3155 Ok(())
3156 }
3157
3158 #[test]
Lukasz Anforowicz732ca642022-02-03 20:58:38 +00003159 fn test_impl_eq_for_member_function() -> Result<()> {
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +00003160 let ir = ir_from_cc(
3161 r#"#pragma clang lifetime_elision
3162 struct SomeStruct final {
3163 inline bool operator==(const SomeStruct& other) const {
3164 return i == other.i;
3165 }
3166 int i;
3167 };"#,
3168 )?;
3169 let rs_api = generate_rs_api(&ir)?;
3170 assert_rs_matches!(
3171 rs_api,
3172 quote! {
3173 impl PartialEq<SomeStruct> for SomeStruct {
3174 #[inline(always)]
3175 fn eq<'a, 'b>(&'a self, other: &'b SomeStruct) -> bool {
3176 unsafe { crate::detail::__rust_thunk___ZNK10SomeStructeqERKS_(self, other) }
3177 }
3178 }
3179 }
3180 );
3181 let rs_api_impl = generate_rs_api_impl(&ir)?;
3182 assert_cc_matches!(
3183 rs_api_impl,
3184 quote! {
3185 extern "C" bool __rust_thunk___ZNK10SomeStructeqERKS_(
3186 const class SomeStruct* __this, const class SomeStruct& other) {
3187 return __this->operator==(other);
3188 }
3189 }
3190 );
3191 Ok(())
3192 }
3193
3194 #[test]
Lukasz Anforowicz732ca642022-02-03 20:58:38 +00003195 fn test_impl_eq_for_free_function() -> Result<()> {
3196 let ir = ir_from_cc(
3197 r#"#pragma clang lifetime_elision
3198 struct SomeStruct final { int i; };
3199 bool operator==(const SomeStruct& lhs, const SomeStruct& rhs) {
3200 return lhs.i == rhs.i;
3201 }"#,
3202 )?;
3203 let rs_api = generate_rs_api(&ir)?;
3204 assert_rs_matches!(
3205 rs_api,
3206 quote! {
3207 impl PartialEq<SomeStruct> for SomeStruct {
3208 #[inline(always)]
3209 fn eq<'a, 'b>(&'a self, rhs: &'b SomeStruct) -> bool {
3210 unsafe { crate::detail::__rust_thunk___ZeqRK10SomeStructS1_(self, rhs) }
3211 }
3212 }
3213 }
3214 );
3215 Ok(())
3216 }
3217
3218 #[test]
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +00003219 fn test_impl_eq_non_const_member_function() -> Result<()> {
3220 let ir = ir_from_cc(
3221 r#"#pragma clang lifetime_elision
3222 struct SomeStruct final {
3223 bool operator==(const SomeStruct& other) /* no `const` here */;
3224 };"#,
3225 )?;
3226 let rs_api = generate_rs_api(&ir)?;
3227 assert_rs_not_matches!(rs_api, quote! {impl PartialEq});
3228 Ok(())
3229 }
3230
3231 #[test]
3232 fn test_impl_eq_rhs_by_value() -> Result<()> {
3233 let ir = ir_from_cc(
3234 r#"#pragma clang lifetime_elision
3235 struct SomeStruct final {
3236 bool operator==(SomeStruct other) const;
3237 };"#,
3238 )?;
3239 let rs_api = generate_rs_api(&ir)?;
3240 assert_rs_not_matches!(rs_api, quote! {impl PartialEq});
3241 Ok(())
3242 }
3243
3244 #[test]
Devin Jeanpierre45cb1162021-10-27 10:54:28 +00003245 fn test_thunk_ident_function() {
3246 let func = ir_func("foo");
Marcel Hlopkoeaae9b72022-01-21 15:54:11 +00003247 assert_eq!(thunk_ident(&func), make_rs_ident("__rust_thunk___Z3foov"));
Devin Jeanpierre45cb1162021-10-27 10:54:28 +00003248 }
3249
3250 #[test]
3251 fn test_thunk_ident_special_names() {
Marcel Hlopko4b13b962021-12-06 12:40:56 +00003252 let ir = ir_from_cc("struct Class {};").unwrap();
Devin Jeanpierre45cb1162021-10-27 10:54:28 +00003253
Googler45ad2752021-12-06 12:12:35 +00003254 let destructor =
3255 ir.functions().find(|f| f.name == UnqualifiedIdentifier::Destructor).unwrap();
Lukasz Anforowiczdd9ae0f2022-02-17 15:52:53 +00003256 assert_eq!(thunk_ident(destructor), make_rs_ident("__rust_thunk___ZN5ClassD1Ev"));
Devin Jeanpierre45cb1162021-10-27 10:54:28 +00003257
Lukasz Anforowicz49b5bbc2022-02-04 23:40:10 +00003258 let default_constructor = ir
3259 .functions()
3260 .find(|f| f.name == UnqualifiedIdentifier::Constructor && f.params.len() == 1)
3261 .unwrap();
Lukasz Anforowiczdd9ae0f2022-02-17 15:52:53 +00003262 assert_eq!(thunk_ident(default_constructor), make_rs_ident("__rust_thunk___ZN5ClassC1Ev"));
Devin Jeanpierre45cb1162021-10-27 10:54:28 +00003263 }
Googler7cced422021-12-06 11:58:39 +00003264
3265 #[test]
Marcel Hlopko89547752021-12-10 09:39:41 +00003266 fn test_elided_lifetimes() -> Result<()> {
Googler7cced422021-12-06 11:58:39 +00003267 let ir = ir_from_cc(
3268 r#"#pragma clang lifetime_elision
Devin Jeanpierre88343c72022-01-15 01:10:23 +00003269 struct S final {
Googler7cced422021-12-06 11:58:39 +00003270 int& f(int& i);
3271 };"#,
Marcel Hlopko89547752021-12-10 09:39:41 +00003272 )?;
3273 let rs_api = generate_rs_api(&ir)?;
3274 assert_rs_matches!(
3275 rs_api,
3276 quote! {
Lukasz Anforowicz231a3bb2022-01-12 14:05:59 +00003277 pub fn f<'a, 'b>(&'a mut self, i: &'b mut i32) -> &'a mut i32 { ... }
Marcel Hlopko89547752021-12-10 09:39:41 +00003278 }
Googler7cced422021-12-06 11:58:39 +00003279 );
Marcel Hlopko89547752021-12-10 09:39:41 +00003280 assert_rs_matches!(
3281 rs_api,
3282 quote! {
Googler6804a012022-01-05 07:04:36 +00003283 pub(crate) fn __rust_thunk___ZN1S1fERi<'a, 'b>(__this: &'a mut S, i: &'b mut i32)
3284 -> &'a mut i32;
Marcel Hlopko89547752021-12-10 09:39:41 +00003285 }
Googler7cced422021-12-06 11:58:39 +00003286 );
Marcel Hlopko89547752021-12-10 09:39:41 +00003287 Ok(())
Googler7cced422021-12-06 11:58:39 +00003288 }
Lukasz Anforowiczdaff0402021-12-23 00:37:50 +00003289
3290 #[test]
Googler386e5942022-02-24 08:53:29 +00003291 fn test_annotated_lifetimes() -> Result<()> {
3292 let ir = ir_from_cc(
3293 r#"[[clang::annotate("lifetimes", "a, a -> a")]]
3294 int& f(int& i1, int& i2);
3295 "#,
3296 )?;
3297 let rs_api = generate_rs_api(&ir)?;
3298 assert_rs_matches!(
3299 rs_api,
3300 quote! {
3301 pub fn f<'a>(i1: &'a mut i32, i2: &'a mut i32) -> &'a mut i32 { ... }
3302 }
3303 );
3304 assert_rs_matches!(
3305 rs_api,
3306 quote! {
3307 pub(crate) fn __rust_thunk___Z1fRiS_<'a>(i1: &'a mut i32, i2: &'a mut i32)
3308 -> &'a mut i32;
3309 }
3310 );
3311 Ok(())
3312 }
3313
3314 #[test]
Lukasz Anforowiczdaff0402021-12-23 00:37:50 +00003315 fn test_format_generic_params() -> Result<()> {
3316 assert_rs_matches!(format_generic_params(std::iter::empty::<syn::Ident>()), quote! {});
3317
Marcel Hlopkoeaae9b72022-01-21 15:54:11 +00003318 let idents = ["T1", "T2"].iter().map(|s| make_rs_ident(s));
Lukasz Anforowiczdaff0402021-12-23 00:37:50 +00003319 assert_rs_matches!(format_generic_params(idents), quote! { < T1, T2 > });
3320
3321 let lifetimes = ["a", "b"]
3322 .iter()
3323 .map(|s| syn::Lifetime::new(&format!("'{}", s), proc_macro2::Span::call_site()));
3324 assert_rs_matches!(format_generic_params(lifetimes), quote! { < 'a, 'b > });
3325
3326 Ok(())
3327 }
Googlerd03d05b2022-01-07 10:10:57 +00003328
3329 #[test]
3330 fn test_overloaded_functions() -> Result<()> {
3331 // TODO(b/213280424): We don't support creating bindings for overloaded
3332 // functions yet, except in the case of overloaded constructors with a
3333 // single parameter.
3334 let ir = ir_from_cc(
Lukasz Anforowicz55673c92022-01-27 19:37:26 +00003335 r#" #pragma clang lifetime_elision
3336 void f();
Googlerd03d05b2022-01-07 10:10:57 +00003337 void f(int i);
Devin Jeanpierre88343c72022-01-15 01:10:23 +00003338 struct S1 final {
Googlerd03d05b2022-01-07 10:10:57 +00003339 void f();
3340 void f(int i);
3341 };
Devin Jeanpierre88343c72022-01-15 01:10:23 +00003342 struct S2 final {
Googlerd03d05b2022-01-07 10:10:57 +00003343 void f();
3344 };
Devin Jeanpierre88343c72022-01-15 01:10:23 +00003345 struct S3 final {
Googlerd03d05b2022-01-07 10:10:57 +00003346 S3(int i);
3347 S3(double d);
3348 };
3349 "#,
3350 )?;
3351 let rs_api = generate_rs_api(&ir)?;
3352 let rs_api_str = tokens_to_string(rs_api.clone())?;
3353
3354 // Cannot overload free functions.
3355 assert!(rs_api_str.contains("Error while generating bindings for item 'f'"));
3356 assert_rs_not_matches!(rs_api, quote! {pub fn f()});
3357 assert_rs_not_matches!(rs_api, quote! {pub fn f(i: i32)});
3358
3359 // Cannot overload member functions.
3360 assert!(rs_api_str.contains("Error while generating bindings for item 'S1::f'"));
3361 assert_rs_not_matches!(rs_api, quote! {pub fn f(... S1 ...)});
3362
3363 // But we can import member functions that have the same name as a free
3364 // function.
Lukasz Anforowicz55673c92022-01-27 19:37:26 +00003365 assert_rs_matches!(rs_api, quote! {pub fn f<'a>(&'a mut self)});
Googlerd03d05b2022-01-07 10:10:57 +00003366
3367 // We can also import overloaded single-parameter constructors.
3368 assert_rs_matches!(rs_api, quote! {impl From<i32> for S3});
3369 assert_rs_matches!(rs_api, quote! {impl From<f64> for S3});
3370 Ok(())
3371 }
Googlerdcca7f72022-01-10 12:30:43 +00003372
3373 #[test]
3374 fn test_type_alias() -> Result<()> {
3375 let ir = ir_from_cc(
3376 r#"
3377 typedef int MyTypedefDecl;
3378 using MyTypeAliasDecl = int;
Googler6a0a5252022-01-11 14:08:09 +00003379 using MyTypeAliasDecl_Alias = MyTypeAliasDecl;
3380
Devin Jeanpierre88343c72022-01-15 01:10:23 +00003381 struct S final {};
Googler6a0a5252022-01-11 14:08:09 +00003382 using S_Alias = S;
3383 using S_Alias_Alias = S_Alias;
3384
3385 inline void f(MyTypedefDecl t) {}
Googlerdcca7f72022-01-10 12:30:43 +00003386 "#,
3387 )?;
3388 let rs_api = generate_rs_api(&ir)?;
Googler6a0a5252022-01-11 14:08:09 +00003389 assert_rs_matches!(rs_api, quote! { pub type MyTypedefDecl = i32; });
3390 assert_rs_matches!(rs_api, quote! { pub type MyTypeAliasDecl = i32; });
3391 assert_rs_matches!(rs_api, quote! { pub type MyTypeAliasDecl_Alias = MyTypeAliasDecl; });
3392 assert_rs_matches!(rs_api, quote! { pub type S_Alias = S; });
3393 assert_rs_matches!(rs_api, quote! { pub type S_Alias_Alias = S_Alias; });
3394 assert_rs_matches!(rs_api, quote! { pub fn f(t: MyTypedefDecl) });
3395 assert_cc_matches!(
3396 generate_rs_api_impl(&ir)?,
3397 quote! {
3398 extern "C" void __rust_thunk___Z1fi(MyTypedefDecl t){ f (t) ; }
3399 }
3400 );
Googlerdcca7f72022-01-10 12:30:43 +00003401 Ok(())
3402 }
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00003403
3404 #[test]
3405 fn test_rs_type_kind_implements_copy() -> Result<()> {
Lukasz Anforowicz20651e32022-02-10 14:52:15 +00003406 let template = r#" LIFETIMES
Devin Jeanpierre88343c72022-01-15 01:10:23 +00003407 struct [[clang::trivial_abi]] TrivialStruct final { int i; };
3408 struct [[clang::trivial_abi]] UserDefinedCopyConstructor final {
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00003409 UserDefinedCopyConstructor(const UserDefinedCopyConstructor&);
3410 };
3411 using IntAlias = int;
3412 using TrivialAlias = TrivialStruct;
3413 using NonTrivialAlias = UserDefinedCopyConstructor;
3414 void func(PARAM_TYPE some_param);
3415 "#;
3416 assert_impl_all!(i32: Copy);
3417 assert_impl_all!(&i32: Copy);
3418 assert_not_impl_all!(&mut i32: Copy);
Lukasz Anforowicz20651e32022-02-10 14:52:15 +00003419 assert_impl_all!(Option<&i32>: Copy);
3420 assert_not_impl_all!(Option<&mut i32>: Copy);
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00003421 assert_impl_all!(*const i32: Copy);
3422 assert_impl_all!(*mut i32: Copy);
Lukasz Anforowicz20651e32022-02-10 14:52:15 +00003423 struct Test {
3424 // Test inputs:
3425 cc: &'static str,
3426 lifetimes: bool,
3427 // Expected test outputs:
3428 rs: &'static str,
3429 is_copy: bool,
3430 }
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00003431 let tests = vec![
3432 // Validity of the next few tests is verified via
3433 // `assert_[not_]impl_all!` static assertions above.
Lukasz Anforowicz20651e32022-02-10 14:52:15 +00003434 Test { cc: "int", lifetimes: true, rs: "i32", is_copy: true },
3435 Test { cc: "const int&", lifetimes: true, rs: "&'a i32", is_copy: true },
3436 Test { cc: "int&", lifetimes: true, rs: "&'a mut i32", is_copy: false },
3437 Test { cc: "const int*", lifetimes: true, rs: "Option<&'a i32>", is_copy: true },
3438 Test { cc: "int*", lifetimes: true, rs: "Option<&'a mut i32>", is_copy: false },
3439 Test { cc: "const int*", lifetimes: false, rs: "*const i32", is_copy: true },
3440 Test { cc: "int*", lifetimes: false, rs: "*mut i32", is_copy: true },
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00003441 // Tests below have been thought-through and verified "manually".
Lukasz Anforowicz20651e32022-02-10 14:52:15 +00003442 // TrivialStruct is expected to derive Copy.
3443 Test { cc: "TrivialStruct", lifetimes: true, rs: "TrivialStruct", is_copy: true },
3444 Test {
3445 cc: "UserDefinedCopyConstructor",
3446 lifetimes: true,
3447 rs: "UserDefinedCopyConstructor",
3448 is_copy: false,
3449 },
3450 Test { cc: "IntAlias", lifetimes: true, rs: "IntAlias", is_copy: true },
3451 Test { cc: "TrivialAlias", lifetimes: true, rs: "TrivialAlias", is_copy: true },
3452 Test { cc: "NonTrivialAlias", lifetimes: true, rs: "NonTrivialAlias", is_copy: false },
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00003453 ];
Lukasz Anforowicz20651e32022-02-10 14:52:15 +00003454 for test in tests.iter() {
3455 let test_name = format!("cc='{}', lifetimes={}", test.cc, test.lifetimes);
3456 let cc_input = template.replace("PARAM_TYPE", test.cc).replace(
3457 "LIFETIMES",
3458 if test.lifetimes { "#pragma clang lifetime_elision" } else { "" },
3459 );
3460 let ir = ir_from_cc(&cc_input)?;
Lukasz Anforowicz9c663ca2022-02-09 01:33:31 +00003461 let f = retrieve_func(&ir, "func");
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00003462 let t = RsTypeKind::new(&f.params[0].type_.rs_type, &ir)?;
Lukasz Anforowicz20651e32022-02-10 14:52:15 +00003463
Lukasz Anforowicz90bdb962022-02-14 21:07:45 +00003464 let lifetime_to_name: HashMap::<LifetimeId, String> = t.lifetimes().map(
3465 |lifetime_id| (lifetime_id, "a".to_string())).collect();
3466
Lukasz Anforowicz20651e32022-02-10 14:52:15 +00003467 let fmt = tokens_to_string(t.format(&ir, &lifetime_to_name)?)?;
3468 assert_eq!(test.rs, fmt, "Testing: {}", test_name);
3469
3470 assert_eq!(test.is_copy, t.implements_copy(), "Testing: {}", test_name);
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00003471 }
3472 Ok(())
3473 }
Lukasz Anforowicza94ab702022-01-14 22:40:25 +00003474
3475 #[test]
3476 fn test_rs_type_kind_is_shared_ref_to_with_lifetimes() -> Result<()> {
3477 let ir = ir_from_cc(
3478 "#pragma clang lifetime_elision
3479 struct SomeStruct {};
3480 void foo(const SomeStruct& foo_param);
3481 void bar(SomeStruct& bar_param);",
3482 )?;
3483 let record = ir.records().next().unwrap();
Lukasz Anforowicz9c663ca2022-02-09 01:33:31 +00003484 let foo_func = retrieve_func(&ir, "foo");
3485 let bar_func = retrieve_func(&ir, "bar");
Lukasz Anforowicza94ab702022-01-14 22:40:25 +00003486
3487 // const-ref + lifetimes in C++ ===> shared-ref in Rust
3488 assert_eq!(foo_func.params.len(), 1);
3489 let foo_param = &foo_func.params[0];
3490 assert_eq!(&foo_param.identifier.identifier, "foo_param");
3491 let foo_type = RsTypeKind::new(&foo_param.type_.rs_type, &ir)?;
3492 assert!(foo_type.is_shared_ref_to(record));
3493 assert!(matches!(foo_type, RsTypeKind::Reference { mutability: Mutability::Const, .. }));
3494
3495 // non-const-ref + lifetimes in C++ ===> mutable-ref in Rust
3496 assert_eq!(bar_func.params.len(), 1);
3497 let bar_param = &bar_func.params[0];
3498 assert_eq!(&bar_param.identifier.identifier, "bar_param");
3499 let bar_type = RsTypeKind::new(&bar_param.type_.rs_type, &ir)?;
3500 assert!(!bar_type.is_shared_ref_to(record));
3501 assert!(matches!(bar_type, RsTypeKind::Reference { mutability: Mutability::Mut, .. }));
3502
3503 Ok(())
3504 }
3505
3506 #[test]
3507 fn test_rs_type_kind_is_shared_ref_to_without_lifetimes() -> Result<()> {
3508 let ir = ir_from_cc(
3509 "struct SomeStruct {};
3510 void foo(const SomeStruct& foo_param);",
3511 )?;
3512 let record = ir.records().next().unwrap();
Lukasz Anforowicz9c663ca2022-02-09 01:33:31 +00003513 let foo_func = retrieve_func(&ir, "foo");
Lukasz Anforowicza94ab702022-01-14 22:40:25 +00003514
3515 // const-ref + *no* lifetimes in C++ ===> const-pointer in Rust
3516 assert_eq!(foo_func.params.len(), 1);
3517 let foo_param = &foo_func.params[0];
3518 assert_eq!(&foo_param.identifier.identifier, "foo_param");
3519 let foo_type = RsTypeKind::new(&foo_param.type_.rs_type, &ir)?;
3520 assert!(!foo_type.is_shared_ref_to(record));
3521 assert!(matches!(foo_type, RsTypeKind::Pointer { mutability: Mutability::Const, .. }));
3522
3523 Ok(())
3524 }
Marcel Hlopkoeaae9b72022-01-21 15:54:11 +00003525
3526 #[test]
Lukasz Anforowicz90bdb962022-02-14 21:07:45 +00003527 fn test_rs_type_kind_dfs_iter_ordering() {
3528 // Set up a test input representing: A<B<C>, D<E>>.
3529 let a = {
3530 let b = {
3531 let c = RsTypeKind::Other { name: "C", type_args: vec![] };
3532 RsTypeKind::Other { name: "B", type_args: vec![c] }
3533 };
3534 let d = {
3535 let e = RsTypeKind::Other { name: "E", type_args: vec![] };
3536 RsTypeKind::Other { name: "D", type_args: vec![e] }
3537 };
3538 RsTypeKind::Other { name: "A", type_args: vec![b, d] }
3539 };
3540 let dfs_names = a
3541 .dfs_iter()
3542 .map(|t| match t {
3543 RsTypeKind::Other { name, .. } => *name,
3544 _ => unreachable!("Only 'other' types are used in this test"),
3545 })
3546 .collect_vec();
3547 assert_eq!(vec!["A", "B", "C", "D", "E"], dfs_names);
3548 }
3549
3550 #[test]
Lukasz Anforowiczcf230fd2022-02-18 19:20:39 +00003551 fn test_rs_type_kind_dfs_iter_ordering_for_func_ptr() {
3552 // Set up a test input representing: fn(A, B) -> C
3553 let f = {
3554 let a = RsTypeKind::Other { name: "A", type_args: vec![] };
3555 let b = RsTypeKind::Other { name: "B", type_args: vec![] };
3556 let c = RsTypeKind::Other { name: "C", type_args: vec![] };
3557 RsTypeKind::FuncPtr { abi: "blah", param_types: vec![a, b], return_type: Box::new(c) }
3558 };
3559 let dfs_names = f
3560 .dfs_iter()
3561 .map(|t| match t {
3562 RsTypeKind::FuncPtr { .. } => "fn",
3563 RsTypeKind::Other { name, .. } => *name,
3564 _ => unreachable!("Only FuncPtr and Other kinds are used in this test"),
3565 })
3566 .collect_vec();
3567 assert_eq!(vec!["fn", "A", "B", "C"], dfs_names);
3568 }
3569
3570 #[test]
Lukasz Anforowicz90bdb962022-02-14 21:07:45 +00003571 fn test_rs_type_kind_lifetimes() -> Result<()> {
3572 let ir = ir_from_cc(
3573 r#"
3574 #pragma clang lifetime_elision
3575 using TypeAlias = int&;
3576 struct SomeStruct {};
3577 void foo(int a, int& b, int* c, int** d, TypeAlias e, SomeStruct f); "#,
3578 )?;
3579 let f = retrieve_func(&ir, "foo");
3580 let ret = RsTypeKind::new(&f.return_type.rs_type, &ir)?;
3581 let a = RsTypeKind::new(&f.params[0].type_.rs_type, &ir)?;
3582 let b = RsTypeKind::new(&f.params[1].type_.rs_type, &ir)?;
3583 let c = RsTypeKind::new(&f.params[2].type_.rs_type, &ir)?;
3584 let d = RsTypeKind::new(&f.params[3].type_.rs_type, &ir)?;
3585 let e = RsTypeKind::new(&f.params[4].type_.rs_type, &ir)?;
3586 let f = RsTypeKind::new(&f.params[5].type_.rs_type, &ir)?;
3587
3588 assert_eq!(0, ret.lifetimes().count()); // No lifetimes on `void`.
3589 assert_eq!(0, a.lifetimes().count()); // No lifetimes on `int`.
3590 assert_eq!(1, b.lifetimes().count()); // `&'a i32` has a single lifetime.
3591 assert_eq!(1, c.lifetimes().count()); // `Option<&'b i32>` has a single lifetime.
3592 assert_eq!(2, d.lifetimes().count()); // `&'c Option<&'d i32>` has two lifetimes.
3593 assert_eq!(1, e.lifetimes().count()); // Lifetime of underlying type should show through.
3594 assert_eq!(0, f.lifetimes().count()); // No lifetimes on structs (yet).
3595 Ok(())
3596 }
3597
3598 #[test]
3599 fn test_rs_type_kind_lifetimes_raw_ptr() -> Result<()> {
3600 let ir = ir_from_cc("void foo(int* a);")?;
3601 let f = retrieve_func(&ir, "foo");
3602 let a = RsTypeKind::new(&f.params[0].type_.rs_type, &ir)?;
3603 assert_eq!(0, a.lifetimes().count()); // No lifetimes on `int*`.
3604 Ok(())
3605 }
3606
3607 #[test]
Marcel Hlopkoeaae9b72022-01-21 15:54:11 +00003608 fn test_rust_keywords_are_escaped_in_rs_api_file() -> Result<()> {
3609 let ir = ir_from_cc("struct type { int dyn; };")?;
3610 let rs_api = generate_rs_api(&ir)?;
3611 assert_rs_matches!(rs_api, quote! { struct r#type { ... r#dyn: i32 ... } });
3612 Ok(())
3613 }
3614
3615 #[test]
3616 fn test_rust_keywords_are_not_escaped_in_rs_api_impl_file() -> Result<()> {
3617 let ir = ir_from_cc("struct type { int dyn; };")?;
3618 let rs_api_impl = generate_rs_api_impl(&ir)?;
3619 assert_cc_matches!(rs_api_impl, quote! { static_assert(offsetof(class type, dyn) ... ) });
3620 Ok(())
3621 }
Marcel Hlopko14ee3c82022-02-09 09:46:23 +00003622
3623 #[test]
3624 fn test_no_aligned_attr() {
3625 let ir = ir_from_cc("struct SomeStruct {};").unwrap();
3626 let rs_api = generate_rs_api(&ir).unwrap();
3627
3628 assert_rs_matches! {rs_api, quote! {
3629 #[repr(C)]
3630 pub struct SomeStruct { ... }
3631 }};
3632 }
3633
3634 #[test]
3635 fn test_aligned_attr() {
3636 let ir = ir_from_cc("struct SomeStruct {} __attribute__((aligned(64)));").unwrap();
3637 let rs_api = generate_rs_api(&ir).unwrap();
3638
3639 assert_rs_matches! {rs_api, quote! {
3640 #[repr(C, align(64))]
3641 pub struct SomeStruct { ... }
3642 }
3643 };
3644 }
Devin Jeanpierre149950d2022-02-22 21:02:02 +00003645
3646 /// !Unpin references should not be pinned.
3647 #[test]
3648 fn test_nonunpin_ref_param() -> Result<()> {
3649 let rs_api_impl = generate_rs_api(&ir_from_cc(
3650 r#"
3651 #pragma clang lifetime_elision
3652 struct S {~S();};
3653 void Function(const S& s);
3654 "#,
3655 )?)?;
3656 assert_rs_matches!(
3657 rs_api_impl,
3658 quote! {
3659 fn Function<'a>(s: &'a S) { ... }
3660 }
3661 );
3662 Ok(())
3663 }
3664
3665 /// !Unpin mut references must be pinned.
3666 #[test]
3667 fn test_nonunpin_mut_param() -> Result<()> {
3668 let rs_api_impl = generate_rs_api(&ir_from_cc(
3669 r#"
3670 #pragma clang lifetime_elision
3671 struct S {~S();};
3672 void Function(S& s);
3673 "#,
3674 )?)?;
3675 assert_rs_matches!(
3676 rs_api_impl,
3677 quote! {
3678 fn Function<'a>(s: std::pin::Pin<&'a mut S>) { ... }
3679 }
3680 );
3681 Ok(())
3682 }
3683
3684 /// !Unpin &self should not be pinned.
3685 #[test]
3686 fn test_nonunpin_ref_self() -> Result<()> {
3687 let rs_api_impl = generate_rs_api(&ir_from_cc(
3688 r#"
3689 #pragma clang lifetime_elision
3690 struct S {
3691 ~S();
3692 void Function() const;
3693 };
3694 "#,
3695 )?)?;
3696 assert_rs_matches!(
3697 rs_api_impl,
3698 quote! {
3699 fn Function<'a>(&'a self) { ... }
3700 }
3701 );
3702 Ok(())
3703 }
3704
3705 /// !Unpin &mut self must be pinned.
3706 #[test]
3707 fn test_nonunpin_mut_self() -> Result<()> {
3708 let rs_api_impl = generate_rs_api(&ir_from_cc(
3709 r#"
3710 #pragma clang lifetime_elision
3711 struct S {
3712 ~S();
3713 void Function();
3714 };
3715 "#,
3716 )?)?;
3717 assert_rs_matches!(
3718 rs_api_impl,
3719 quote! {
3720 fn Function<'a>(self: std::pin::Pin<&'a mut Self>) { ... }
3721 }
3722 );
3723 Ok(())
3724 }
3725
3726 /// Drop::drop must not use self : Pin<...>.
3727 #[test]
3728 fn test_nonunpin_drop() -> Result<()> {
3729 let rs_api_impl = generate_rs_api(&ir_from_cc(
3730 r#"
3731 struct S {~S();};
3732 "#,
3733 )?)?;
3734 assert_rs_matches!(
3735 rs_api_impl,
3736 quote! {
3737 fn drop(&mut self) { ... }
3738 }
3739 );
3740 Ok(())
3741 }
Marcel Hlopko42abfc82021-08-09 07:03:17 +00003742}