blob: e2364a24af41c221008725bc70cc2d073e35c6e5 [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<_>>>()?;
257 let is_unsafe = param_type_kinds.iter().any(|p| matches!(p, RsTypeKind::Pointer { .. }));
Michael Forstered642022021-10-04 09:48:25 +0000258
Lukasz Anforowiczab65e292022-01-14 23:04:21 +0000259 let maybe_record: Option<&Record> =
Lukasz Anforowicz13cf7492021-12-22 15:29:52 +0000260 func.member_func_metadata.as_ref().map(|meta| meta.find_record(ir)).transpose()?;
Lukasz Anforowicz732ca642022-02-03 20:58:38 +0000261 let maybe_record_name = maybe_record.map(|r| make_rs_ident(&r.identifier.identifier));
Lukasz Anforowicz13cf7492021-12-22 15:29:52 +0000262
Lukasz Anforowicz732ca642022-02-03 20:58:38 +0000263 // Find 1) the `func_name` and `impl_kind` of the API function to generate
264 // and 2) whether to `format_first_param_as_self` (`&self` or `&mut self`).
Lukasz Anforowiczab65e292022-01-14 23:04:21 +0000265 enum ImplKind {
Lukasz Anforowicz732ca642022-02-03 20:58:38 +0000266 None, // No `impl` needed
267 Struct, // e.g. `impl SomeStruct { ... }` (SomeStruct based on func.member_func_metadata)
268 Trait {
269 trait_name: TokenStream, // e.g. quote!{ From<int> }
270 record_name: Ident, /* e.g. SomeStruct (might *not* be from
271 * func.member_func_metadata) */
272 },
Lukasz Anforowiczab65e292022-01-14 23:04:21 +0000273 }
274 let impl_kind: ImplKind;
275 let func_name: syn::Ident;
276 let format_first_param_as_self: bool;
Googlerd03d05b2022-01-07 10:10:57 +0000277 match &func.name {
Lukasz Anforowicz9c663ca2022-02-09 01:33:31 +0000278 UnqualifiedIdentifier::Operator(op) if op.name == "==" => {
Lukasz Anforowiczeb19ac62022-02-05 00:10:16 +0000279 if param_type_kinds.len() != 2 {
280 bail!("Unexpected number of parameters in operator==: {:?}", func);
281 }
282 match (&param_type_kinds[0], &param_type_kinds[1]) {
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +0000283 (
Lukasz Anforowiczeb19ac62022-02-05 00:10:16 +0000284 RsTypeKind::Reference { referent: lhs, mutability: Mutability::Const, .. },
285 RsTypeKind::Reference { referent: rhs, mutability: Mutability::Const, .. },
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +0000286 ) => match **lhs {
287 RsTypeKind::Record(lhs_record) => {
Lukasz Anforowicz732ca642022-02-03 20:58:38 +0000288 let lhs: Ident = make_rs_ident(&lhs_record.identifier.identifier);
289 let rhs: TokenStream = rhs.format(ir, &lifetime_to_name)?;
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +0000290 format_first_param_as_self = true;
291 func_name = make_rs_ident("eq");
Lukasz Anforowicz732ca642022-02-03 20:58:38 +0000292 impl_kind = ImplKind::Trait {
293 trait_name: quote! {PartialEq<#rhs>},
294 record_name: lhs,
295 };
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +0000296 }
Marcel Hlopko14ee3c82022-02-09 09:46:23 +0000297 _ => {
298 return make_unsupported_result(
299 "operator== where lhs doesn't refer to a record",
300 );
301 }
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +0000302 },
Marcel Hlopko14ee3c82022-02-09 09:46:23 +0000303 _ => {
304 return make_unsupported_result(
305 "operator== where operands are not const references",
306 );
307 }
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +0000308 };
309 }
Lukasz Anforowicz9c663ca2022-02-09 01:33:31 +0000310 UnqualifiedIdentifier::Operator(_) => {
Lukasz Anforowiczeb19ac62022-02-05 00:10:16 +0000311 return make_unsupported_result("Bindings for this kind of operator are not supported");
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +0000312 }
Devin Jeanpierref2ec8712021-10-13 20:47:16 +0000313 UnqualifiedIdentifier::Identifier(id) => {
Marcel Hlopkoeaae9b72022-01-21 15:54:11 +0000314 func_name = make_rs_ident(&id.identifier);
Lukasz Anforowiczf9564622022-01-28 14:31:04 +0000315 match maybe_record {
316 None => {
317 impl_kind = ImplKind::None;
318 format_first_param_as_self = false;
319 }
320 Some(record) => {
321 impl_kind = ImplKind::Struct;
322 if func.is_instance_method() {
323 let first_param = param_type_kinds.first().ok_or_else(|| {
324 anyhow!("Missing `__this` parameter in an instance method: {:?}", func)
325 })?;
326 format_first_param_as_self = first_param.is_ref_to(record)
327 } else {
328 format_first_param_as_self = false;
329 }
330 }
331 };
Michael Forstered642022021-10-04 09:48:25 +0000332 }
Devin Jeanpierre91de7012021-10-21 12:53:51 +0000333 UnqualifiedIdentifier::Destructor => {
Lukasz Anforowiczab65e292022-01-14 23:04:21 +0000334 // Note: to avoid double-destruction of the fields, they are all wrapped in
335 // ManuallyDrop in this case. See `generate_record`.
336 let record =
337 maybe_record.ok_or_else(|| anyhow!("Destructors must be member functions."))?;
Lukasz Anforowicze57215c2022-01-12 14:54:16 +0000338 if !should_implement_drop(record) {
Lukasz Anforowiczeb19ac62022-02-05 00:10:16 +0000339 return Ok(GeneratedFunc::None);
Devin Jeanpierre91de7012021-10-21 12:53:51 +0000340 }
Lukasz Anforowicz732ca642022-02-03 20:58:38 +0000341 let record_name = maybe_record_name
342 .clone()
343 .ok_or_else(|| anyhow!("Destructors must be member functions."))?;
344 impl_kind = ImplKind::Trait { trait_name: quote! {Drop}, record_name };
Marcel Hlopkoeaae9b72022-01-21 15:54:11 +0000345 func_name = make_rs_ident("drop");
Lukasz Anforowiczab65e292022-01-14 23:04:21 +0000346 format_first_param_as_self = true;
Devin Jeanpierre91de7012021-10-21 12:53:51 +0000347 }
Lukasz Anforowicz13cf7492021-12-22 15:29:52 +0000348 UnqualifiedIdentifier::Constructor => {
Lukasz Anforowicz71716b72022-01-26 17:05:05 +0000349 let member_func_metadata = func
350 .member_func_metadata
351 .as_ref()
352 .ok_or_else(|| anyhow!("Constructors must be member functions."))?;
353 let record = maybe_record
354 .ok_or_else(|| anyhow!("Constructors must be associated with a record."))?;
355 let instance_method_metadata =
356 member_func_metadata
357 .instance_method_metadata
358 .as_ref()
359 .ok_or_else(|| anyhow!("Constructors must be instance methods."))?;
360
Devin Jeanpierre88343c72022-01-15 01:10:23 +0000361 if !record.is_unpin() {
Lukasz Anforowiczab65e292022-01-14 23:04:21 +0000362 // TODO: Handle <internal link>
Lukasz Anforowiczeb19ac62022-02-05 00:10:16 +0000363 return make_unsupported_result(
364 "Bindings for constructors of non-trivial types are not supported yet",
365 );
Lukasz Anforowicz9bab8352021-12-22 17:35:31 +0000366 }
Lukasz Anforowicz55673c92022-01-27 19:37:26 +0000367 if is_unsafe {
368 // TODO(b/216648347): Allow this outside of traits (e.g. after supporting
369 // translating C++ constructors into static methods in Rust).
Lukasz Anforowiczeb19ac62022-02-05 00:10:16 +0000370 return make_unsupported_result(
371 "Unsafe constructors (e.g. with no elided or explicit lifetimes) \
372 are intentionally not supported",
373 );
Lukasz Anforowicz55673c92022-01-27 19:37:26 +0000374 }
375
Lukasz Anforowicz732ca642022-02-03 20:58:38 +0000376 let record_name = maybe_record_name
377 .clone()
378 .ok_or_else(|| anyhow!("Constructors must be member functions."))?;
Lukasz Anforowicz2e41bb62022-01-11 18:23:07 +0000379 match func.params.len() {
Lukasz Anforowiczf9564622022-01-28 14:31:04 +0000380 0 => bail!("Missing `__this` parameter in a constructor: {:?}", func),
Lukasz Anforowicz2e41bb62022-01-11 18:23:07 +0000381 1 => {
Lukasz Anforowicz732ca642022-02-03 20:58:38 +0000382 impl_kind = ImplKind::Trait { trait_name: quote! {Default}, record_name };
Marcel Hlopkoeaae9b72022-01-21 15:54:11 +0000383 func_name = make_rs_ident("default");
Lukasz Anforowiczab65e292022-01-14 23:04:21 +0000384 format_first_param_as_self = false;
Lukasz Anforowicz2e41bb62022-01-11 18:23:07 +0000385 }
Lukasz Anforowicz73326af2022-01-05 01:13:10 +0000386 2 => {
Lukasz Anforowicz2e41bb62022-01-11 18:23:07 +0000387 // TODO(lukasza): Do something smart with move constructor.
Lukasz Anforowiczf7bdd392022-01-21 00:33:39 +0000388 if param_type_kinds[1].is_shared_ref_to(record) {
Lukasz Anforowicz2e41bb62022-01-11 18:23:07 +0000389 // Copy constructor
390 if should_derive_clone(record) {
Lukasz Anforowiczeb19ac62022-02-05 00:10:16 +0000391 return Ok(GeneratedFunc::None);
Lukasz Anforowicz2e41bb62022-01-11 18:23:07 +0000392 } else {
Lukasz Anforowicz732ca642022-02-03 20:58:38 +0000393 impl_kind = ImplKind::Trait { trait_name: quote! {Clone}, record_name };
Marcel Hlopkoeaae9b72022-01-21 15:54:11 +0000394 func_name = make_rs_ident("clone");
Lukasz Anforowiczab65e292022-01-14 23:04:21 +0000395 format_first_param_as_self = true;
Lukasz Anforowicz2e41bb62022-01-11 18:23:07 +0000396 }
Lukasz Anforowicz71716b72022-01-26 17:05:05 +0000397 } else if !instance_method_metadata.is_explicit_ctor {
Lukasz Anforowicz2e41bb62022-01-11 18:23:07 +0000398 let param_type = &param_types[1];
Lukasz Anforowicz732ca642022-02-03 20:58:38 +0000399 impl_kind = ImplKind::Trait {
400 trait_name: quote! {From< #param_type >},
401 record_name,
402 };
Marcel Hlopkoeaae9b72022-01-21 15:54:11 +0000403 func_name = make_rs_ident("from");
Lukasz Anforowiczab65e292022-01-14 23:04:21 +0000404 format_first_param_as_self = false;
Lukasz Anforowicz71716b72022-01-26 17:05:05 +0000405 } else {
Marcel Hlopko14ee3c82022-02-09 09:46:23 +0000406 return make_unsupported_result(
407 "Not yet supported type of constructor parameter",
408 );
Lukasz Anforowicz73326af2022-01-05 01:13:10 +0000409 }
410 }
411 _ => {
Lukasz Anforowicz55673c92022-01-27 19:37:26 +0000412 // TODO(b/216648347): Support bindings for other constructors.
Marcel Hlopko14ee3c82022-02-09 09:46:23 +0000413 return make_unsupported_result(
414 "More than 1 constructor parameter is not supported yet",
415 );
Lukasz Anforowicz73326af2022-01-05 01:13:10 +0000416 }
Lukasz Anforowiczab65e292022-01-14 23:04:21 +0000417 }
418 }
419 }
420
421 let api_func_def = {
Lukasz Anforowicz95551272022-01-20 00:02:24 +0000422 // Clone params, return type, etc - we may need to mutate them in the
423 // API func, but we want to retain the originals for the thunk.
Lukasz Anforowiczab65e292022-01-14 23:04:21 +0000424 let mut return_type_fragment = return_type_fragment.clone();
425 let mut thunk_args = param_idents.iter().map(|id| quote! { #id}).collect_vec();
426 let mut api_params = param_idents
427 .iter()
428 .zip(param_types.iter())
429 .map(|(ident, type_)| quote! { #ident : #type_ })
430 .collect_vec();
Lukasz Anforowicz95551272022-01-20 00:02:24 +0000431 let mut lifetimes = func.lifetime_params.iter().collect_vec();
Lukasz Anforowiczf7bdd392022-01-21 00:33:39 +0000432 let mut maybe_first_api_param = param_type_kinds.get(0);
Lukasz Anforowiczab65e292022-01-14 23:04:21 +0000433
434 if func.name == UnqualifiedIdentifier::Constructor {
435 return_type_fragment = quote! { -> Self };
436
Lukasz Anforowiczf9564622022-01-28 14:31:04 +0000437 // Drop `__this` parameter from the public Rust API. Presence of
438 // element #0 is indirectly verified by a `Constructor`-related
439 // `match` branch a little bit above.
440 api_params.remove(0);
441 thunk_args.remove(0);
Lukasz Anforowicz95551272022-01-20 00:02:24 +0000442
Lukasz Anforowicz326c4e42022-01-27 14:43:00 +0000443 // Remove the lifetime associated with `__this`.
Lukasz Anforowicz90bdb962022-02-14 21:07:45 +0000444 ensure!(func.return_type.rs_type.is_unit_type(),
445 "Unexpectedly non-void return type of a constructor: {:?}", func);
Lukasz Anforowiczf7bdd392022-01-21 00:33:39 +0000446 let maybe_first_lifetime = func.params[0].type_.rs_type.lifetime_args.first();
Lukasz Anforowicz55673c92022-01-27 19:37:26 +0000447 let no_longer_needed_lifetime_id = maybe_first_lifetime
448 .ok_or_else(|| anyhow!("Missing lifetime on `__this` parameter: {:?}", func))?;
449 lifetimes.retain(|l| l.id != *no_longer_needed_lifetime_id);
Lukasz Anforowicz55673c92022-01-27 19:37:26 +0000450 if let Some(type_still_dependent_on_removed_lifetime) = param_type_kinds
451 .iter()
452 .skip(1) // Skipping `__this`
Lukasz Anforowicz90bdb962022-02-14 21:07:45 +0000453 .flat_map(|t| t.lifetimes())
454 .find(|lifetime_id| *lifetime_id == *no_longer_needed_lifetime_id)
Lukasz Anforowicz55673c92022-01-27 19:37:26 +0000455 {
456 bail!(
457 "The lifetime of `__this` is unexpectedly also used by another \
458 parameter {:?} in function {:?}",
459 type_still_dependent_on_removed_lifetime,
460 func.name
461 );
Lukasz Anforowicz95551272022-01-20 00:02:24 +0000462 }
463
464 // Rebind `maybe_first_api_param` to the next param after `__this`.
Lukasz Anforowiczf7bdd392022-01-21 00:33:39 +0000465 maybe_first_api_param = param_type_kinds.get(1);
Lukasz Anforowiczab65e292022-01-14 23:04:21 +0000466 }
467
468 // Change `__this: &'a SomeStruct` into `&'a self` if needed.
469 if format_first_param_as_self {
470 let first_api_param = maybe_first_api_param
471 .ok_or_else(|| anyhow!("No parameter to format as 'self': {:?}", func))?;
Lukasz Anforowiczcde4b1b2022-02-03 21:20:55 +0000472 let self_decl =
473 first_api_param.format_as_self_param(func, ir, &lifetime_to_name).with_context(
474 || format!("Failed to format as `self` param: {:?}", first_api_param),
475 )?;
Lukasz Anforowiczf9564622022-01-28 14:31:04 +0000476 // Presence of element #0 is verified by `ok_or_else` on
477 // `maybe_first_api_param` above.
478 api_params[0] = self_decl;
479 thunk_args[0] = quote! { self };
Lukasz Anforowiczab65e292022-01-14 23:04:21 +0000480 }
481
482 let func_body = match &func.name {
Lukasz Anforowicz9c663ca2022-02-09 01:33:31 +0000483 UnqualifiedIdentifier::Identifier(_) | UnqualifiedIdentifier::Operator(_) => {
Lukasz Anforowiczf7bdd392022-01-21 00:33:39 +0000484 let mut body = quote! { crate::detail::#thunk_ident( #( #thunk_args ),* ) };
485 // Only need to wrap everything in an `unsafe { ... }` block if
486 // the *whole* api function is safe.
487 if !is_unsafe {
488 body = quote! { unsafe { #body } };
489 }
490 body
491 }
492 UnqualifiedIdentifier::Destructor => {
Lukasz Anforowiczab65e292022-01-14 23:04:21 +0000493 quote! { unsafe { crate::detail::#thunk_ident( #( #thunk_args ),* ) } }
494 }
495 UnqualifiedIdentifier::Constructor => {
496 // SAFETY: A user-defined constructor is not guaranteed to
497 // initialize all the fields. To make the `assume_init()` call
498 // below safe, the memory is zero-initialized first. This is a
499 // bit safer, because zero-initialized memory represents a valid
500 // value for the currently supported field types (this may
501 // change once the bindings generator starts supporting
502 // reference fields). TODO(b/213243309): Double-check if
503 // zero-initialization is desirable here.
504 quote! {
505 let mut tmp = std::mem::MaybeUninit::<Self>::zeroed();
506 unsafe {
507 crate::detail::#thunk_ident( &mut tmp #( , #thunk_args )* );
508 tmp.assume_init()
Lukasz Anforowicz13cf7492021-12-22 15:29:52 +0000509 }
Lukasz Anforowicz13cf7492021-12-22 15:29:52 +0000510 }
Lukasz Anforowiczab65e292022-01-14 23:04:21 +0000511 }
512 };
513
Lukasz Anforowiczf7bdd392022-01-21 00:33:39 +0000514 let (pub_, unsafe_) = match impl_kind {
515 ImplKind::None | ImplKind::Struct => (
516 quote! { pub },
517 if is_unsafe {
518 quote! {unsafe}
519 } else {
520 quote! {}
521 },
522 ),
Lukasz Anforowicz732ca642022-02-03 20:58:38 +0000523 ImplKind::Trait { .. } => {
Lukasz Anforowicz55673c92022-01-27 19:37:26 +0000524 // Currently supported bindings have no unsafe trait functions.
525 assert!(!is_unsafe || func.name == UnqualifiedIdentifier::Destructor);
526 (quote! {}, quote! {})
527 }
Lukasz Anforowiczab65e292022-01-14 23:04:21 +0000528 };
529
Lukasz Anforowicz95551272022-01-20 00:02:24 +0000530 let lifetimes = lifetimes.into_iter().map(|l| format_lifetime_name(&l.name));
531 let generic_params = format_generic_params(lifetimes);
532
Lukasz Anforowiczab65e292022-01-14 23:04:21 +0000533 quote! {
534 #[inline(always)]
Lukasz Anforowiczf7bdd392022-01-21 00:33:39 +0000535 #pub_ #unsafe_ fn #func_name #generic_params( #( #api_params ),* ) #return_type_fragment {
Lukasz Anforowiczab65e292022-01-14 23:04:21 +0000536 #func_body
537 }
Lukasz Anforowicz13cf7492021-12-22 15:29:52 +0000538 }
Michael Forstered642022021-10-04 09:48:25 +0000539 };
540
Lukasz Anforowiczab65e292022-01-14 23:04:21 +0000541 let api_func: TokenStream;
542 let function_id: FunctionId;
Lukasz Anforowiczab65e292022-01-14 23:04:21 +0000543 match impl_kind {
544 ImplKind::None => {
545 api_func = quote! { #doc_comment #api_func_def };
546 function_id = FunctionId { self_type: None, function_path: func_name.into() };
547 }
548 ImplKind::Struct => {
549 let record_name =
550 maybe_record_name.ok_or_else(|| anyhow!("Struct methods must have records"))?;
551 api_func = quote! { impl #record_name { #doc_comment #api_func_def } };
552 function_id = FunctionId {
553 self_type: None,
554 function_path: syn::parse2(quote! { #record_name :: #func_name })?,
555 };
556 }
Lukasz Anforowicz732ca642022-02-03 20:58:38 +0000557 ImplKind::Trait { trait_name, record_name } => {
Lukasz Anforowiczab65e292022-01-14 23:04:21 +0000558 api_func = quote! { #doc_comment impl #trait_name for #record_name { #api_func_def } };
559 function_id = FunctionId {
560 self_type: Some(record_name.into()),
561 function_path: syn::parse2(quote! { #trait_name :: #func_name })?,
562 };
563 }
564 }
565
Lukasz Anforowicz6d553632022-01-06 21:36:14 +0000566 let thunk = {
Devin Jeanpierre7e9a1de2021-12-03 08:04:22 +0000567 let thunk_attr = if can_skip_cc_thunk(func) {
568 quote! {#[link_name = #mangled_name]}
569 } else {
570 quote! {}
571 };
572
Lukasz Anforowicz40c2eb82022-01-11 18:22:31 +0000573 // For constructors inject MaybeUninit into the type of `__this_` parameter.
574 let mut param_types = param_types;
575 if func.name == UnqualifiedIdentifier::Constructor {
576 if param_types.is_empty() || func.params.is_empty() {
577 bail!("Constructors should have at least one parameter (__this)");
578 }
Lukasz Anforowiczf7bdd392022-01-21 00:33:39 +0000579 param_types[0] = param_type_kinds[0]
Lukasz Anforowiczcde4b1b2022-02-03 21:20:55 +0000580 .format_mut_ref_as_uninitialized(ir, &lifetime_to_name)
Lukasz Anforowicz231a3bb2022-01-12 14:05:59 +0000581 .with_context(|| {
Devin Jeanpierre149950d2022-02-22 21:02:02 +0000582 format!("Failed to format `__this` param for a constructor thunk: {:?}", func.params[0])
583 })?;
584 } else if func.name == UnqualifiedIdentifier::Destructor {
585 if param_types.is_empty() || func.params.is_empty() {
586 bail!("Destructors should have at least one parameter (__this)");
587 }
588 param_types[0] = param_type_kinds[0]
589 .format_ref_as_raw_ptr(ir, &lifetime_to_name)
590 .with_context(|| {
591 format!("Failed to format `__this` param for a destructor thunk: {:?}", func.params[0])
Lukasz Anforowicz231a3bb2022-01-12 14:05:59 +0000592 })?;
Lukasz Anforowicz40c2eb82022-01-11 18:22:31 +0000593 }
594
Lukasz Anforowicz95551272022-01-20 00:02:24 +0000595 let lifetimes = func.lifetime_params.iter().map(|l| format_lifetime_name(&l.name));
596 let generic_params = format_generic_params(lifetimes);
597
Devin Jeanpierre7e9a1de2021-12-03 08:04:22 +0000598 quote! {
599 #thunk_attr
Lukasz Anforowicz4ad012b2021-12-15 18:13:40 +0000600 pub(crate) fn #thunk_ident #generic_params( #( #param_idents: #param_types ),*
601 ) #return_type_fragment ;
Devin Jeanpierre7e9a1de2021-12-03 08:04:22 +0000602 }
Michael Forstered642022021-10-04 09:48:25 +0000603 };
604
Lukasz Anforowiczeb19ac62022-02-05 00:10:16 +0000605 Ok(GeneratedFunc::Some { api_func: api_func.into(), thunk: thunk.into(), function_id })
Michael Forstered642022021-10-04 09:48:25 +0000606}
607
Michael Forstercc5941a2021-10-07 07:12:24 +0000608fn generate_doc_comment(comment: &Option<String>) -> TokenStream {
609 match comment {
Michael Forster028800b2021-10-05 12:39:59 +0000610 Some(text) => {
Marcel Hlopko89547752021-12-10 09:39:41 +0000611 // token_stream_printer (and rustfmt) don't put a space between /// and the doc
612 // comment, let's add it here so our comments are pretty.
Lukasz Anforowiczdd9ae0f2022-02-17 15:52:53 +0000613 let doc = format!(" {}", text.replace('\n', "\n "));
Michael Forster028800b2021-10-05 12:39:59 +0000614 quote! {#[doc=#doc]}
615 }
616 None => quote! {},
Michael Forstercc5941a2021-10-07 07:12:24 +0000617 }
618}
Lukasz Anforowiczdaff0402021-12-23 00:37:50 +0000619
620fn format_generic_params<T: quote::ToTokens>(params: impl IntoIterator<Item = T>) -> TokenStream {
621 let mut params = params.into_iter().peekable();
622 if params.peek().is_none() {
623 quote! {}
624 } else {
625 quote! { < #( #params ),* > }
626 }
627}
628
Lukasz Anforowicze57215c2022-01-12 14:54:16 +0000629fn should_implement_drop(record: &Record) -> bool {
630 match record.destructor.definition {
631 // TODO(b/202258760): Only omit destructor if `Copy` is specified.
632 SpecialMemberDefinition::Trivial => false,
633
634 // TODO(b/212690698): Avoid calling into the C++ destructor (e.g. let
635 // Rust drive `drop`-ing) to avoid (somewhat unergonomic) ManuallyDrop
636 // if we can ask Rust to preserve C++ field destruction order in
637 // NontrivialMembers case.
638 SpecialMemberDefinition::NontrivialMembers => true,
639
640 // The `impl Drop` for NontrivialUserDefined needs to call into the
641 // user-defined destructor on C++ side.
642 SpecialMemberDefinition::NontrivialUserDefined => true,
643
644 // TODO(b/213516512): Today the IR doesn't contain Func entries for
645 // deleted functions/destructors/etc. But, maybe we should generate
646 // `impl Drop` in this case? With `unreachable!`? With
647 // `std::mem::forget`?
648 SpecialMemberDefinition::Deleted => false,
649 }
650}
651
652/// Returns whether fields of type `ty` need to be wrapped in `ManuallyDrop<T>`
653/// to prevent the fields from being destructed twice (once by the C++
654/// destructor calkled from the `impl Drop` of the struct and once by `drop` on
655/// the Rust side).
656///
657/// A type is safe to destroy twice if it implements `Copy`. Fields of such
658/// don't need to be wrapped in `ManuallyDrop<T>` even if the struct
659/// containing the fields provides an `impl Drop` that calles into a C++
660/// destructor (in addition to dropping the fields on the Rust side).
661///
662/// Note that it is not enough to just be `!needs_drop<T>()`: Rust only
663/// guarantees that it is safe to use-after-destroy for `Copy` types. See
664/// e.g. the documentation for
665/// [`drop_in_place`](https://doc.rust-lang.org/std/ptr/fn.drop_in_place.html):
666///
667/// > if `T` is not `Copy`, using the pointed-to value after calling
668/// > `drop_in_place` can cause undefined behavior
669fn needs_manually_drop(ty: &ir::RsType, ir: &IR) -> Result<bool> {
670 let ty_implements_copy = RsTypeKind::new(ty, ir)?.implements_copy();
671 Ok(!ty_implements_copy)
672}
673
Michael Forsterbee84482021-10-13 08:35:38 +0000674/// Generates Rust source code for a given `Record` and associated assertions as
675/// a tuple.
Marcel Hlopkoc0956cf2021-11-29 08:31:28 +0000676fn generate_record(record: &Record, ir: &IR) -> Result<(RsSnippet, RsSnippet)> {
Marcel Hlopkoeaae9b72022-01-21 15:54:11 +0000677 let ident = make_rs_ident(&record.identifier.identifier);
Michael Forstercc5941a2021-10-07 07:12:24 +0000678 let doc_comment = generate_doc_comment(&record.doc_comment);
Marcel Hlopkob4b28742021-09-15 12:45:20 +0000679 let field_idents =
Marcel Hlopkoeaae9b72022-01-21 15:54:11 +0000680 record.fields.iter().map(|f| make_rs_ident(&f.identifier.identifier)).collect_vec();
Michael Forstercc5941a2021-10-07 07:12:24 +0000681 let field_doc_coments =
682 record.fields.iter().map(|f| generate_doc_comment(&f.doc_comment)).collect_vec();
Devin Jeanpierre09c6f452021-09-29 07:34:24 +0000683 let field_types = record
684 .fields
685 .iter()
Devin Jeanpierreb69bcae2022-02-03 09:45:50 +0000686 .enumerate()
687 .map(|(i, f)| {
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +0000688 // [[no_unique_address]] fields are replaced by an unaligned block of memory
689 // which fills space up to the next field.
Devin Jeanpierreb69bcae2022-02-03 09:45:50 +0000690 // See: docs/struct_layout
691 if f.is_no_unique_address {
692 let next_offset = if let Some(next) = record.fields.get(i + 1) {
693 next.offset
694 } else {
695 record.size * 8
696 };
697 let width = Literal::usize_unsuffixed((next_offset - f.offset) / 8);
698 return Ok(quote! {[std::mem::MaybeUninit<u8>; #width]});
699 }
Lukasz Anforowicze57215c2022-01-12 14:54:16 +0000700 let mut formatted = format_rs_type(&f.type_.rs_type, ir, &HashMap::new())
701 .with_context(|| {
Googlerb7e361d2022-01-04 14:02:59 +0000702 format!("Failed to format type for field {:?} on record {:?}", f, record)
703 })?;
Lukasz Anforowicze57215c2022-01-12 14:54:16 +0000704 // TODO(b/212696226): Verify cases where ManuallyDrop<T> is skipped
705 // via static asserts in the generated code.
706 if should_implement_drop(record) && needs_manually_drop(&f.type_.rs_type, ir)? {
707 // TODO(b/212690698): Avoid (somewhat unergonomic) ManuallyDrop
708 // if we can ask Rust to preserve field destruction order if the
709 // destructor is the SpecialMemberDefinition::NontrivialMembers
710 // case.
711 formatted = quote! { std::mem::ManuallyDrop<#formatted> }
Lukasz Anforowicz6d553632022-01-06 21:36:14 +0000712 };
Devin Jeanpierre7e9a1de2021-12-03 08:04:22 +0000713 Ok(formatted)
714 })
Devin Jeanpierre09c6f452021-09-29 07:34:24 +0000715 .collect::<Result<Vec<_>>>()?;
Googlerec589eb2021-09-17 07:45:39 +0000716 let field_accesses = record
717 .fields
718 .iter()
719 .map(|f| {
Devin Jeanpierreb69bcae2022-02-03 09:45:50 +0000720 if f.access == AccessSpecifier::Public && !f.is_no_unique_address {
Googlerec589eb2021-09-17 07:45:39 +0000721 quote! { pub }
722 } else {
723 quote! {}
724 }
725 })
726 .collect_vec();
Googlerec648ff2021-09-23 07:19:53 +0000727 let size = record.size;
728 let alignment = record.alignment;
Googleraaa0a532021-10-01 09:11:27 +0000729 let field_assertions =
730 record.fields.iter().zip(field_idents.iter()).map(|(field, field_ident)| {
731 let offset = field.offset;
732 quote! {
733 // The IR contains the offset in bits, while offset_of!()
734 // returns the offset in bytes, so we need to convert.
Googler209b10a2021-12-06 09:11:57 +0000735 const _: () = assert!(offset_of!(#ident, #field_ident) * 8 == #offset);
Googleraaa0a532021-10-01 09:11:27 +0000736 }
737 });
Michael Forsterdb8101a2021-10-08 06:56:03 +0000738 let mut record_features = BTreeSet::new();
739 let mut assertion_features = BTreeSet::new();
Devin Jeanpierre273eeae2021-10-06 13:29:35 +0000740
741 // TODO(mboehme): For the time being, we're using unstable features to
742 // be able to use offset_of!() in static assertions. This is fine for a
743 // prototype, but longer-term we want to either get those features
744 // stabilized or find an alternative. For more details, see
745 // b/200120034#comment15
Marcel Hlopkoeaae9b72022-01-21 15:54:11 +0000746 assertion_features.insert(make_rs_ident("const_ptr_offset_from"));
Devin Jeanpierre2ed14ec2021-10-06 11:32:19 +0000747
Lukasz Anforowicze57215c2022-01-12 14:54:16 +0000748 let derives = generate_derives(record);
Devin Jeanpierre9227d2c2021-10-06 12:26:05 +0000749 let derives = if derives.is_empty() {
750 quote! {}
751 } else {
752 quote! {#[derive( #(#derives),* )]}
753 };
Lukasz Anforowiczdd9ae0f2022-02-17 15:52:53 +0000754 let unpin_impl = if record.is_unpin() {
755 quote! {}
Devin Jeanpierreea700d32021-10-06 11:33:56 +0000756 } else {
Michael Forsterbee84482021-10-13 08:35:38 +0000757 // negative_impls are necessary for universal initialization due to Rust's
758 // coherence rules: PhantomPinned isn't enough to prove to Rust that a
759 // blanket impl that requires Unpin doesn't apply. See http://<internal link>=h.f6jp8ifzgt3n
Marcel Hlopkoeaae9b72022-01-21 15:54:11 +0000760 record_features.insert(make_rs_ident("negative_impls"));
Lukasz Anforowiczdd9ae0f2022-02-17 15:52:53 +0000761 quote! {
Michael Forsterdb8101a2021-10-08 06:56:03 +0000762 __NEWLINE__ __NEWLINE__
763 impl !Unpin for #ident {}
Lukasz Anforowiczdd9ae0f2022-02-17 15:52:53 +0000764 }
765 };
Devin Jeanpierre273eeae2021-10-06 13:29:35 +0000766
Devin Jeanpierrec80e6242022-02-03 01:56:40 +0000767 let mut repr_attributes = vec![quote! {C}];
768 if record.override_alignment && record.alignment > 1 {
769 let alignment = Literal::usize_unsuffixed(record.alignment);
770 repr_attributes.push(quote! {align(#alignment)});
771 }
772
773 // Adjust the struct to also include base class subobjects. We use an opaque
774 // field because subobjects can live in the alignment of base class
775 // subobjects.
776 let base_subobjects_field = if let Some(base_size) = record.base_size {
777 let n = proc_macro2::Literal::usize_unsuffixed(base_size);
778 quote! {
779 __base_class_subobjects: [std::mem::MaybeUninit<u8>; #n],
780 }
781 } else {
782 quote! {}
783 };
784
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +0000785 let empty_struct_placeholder_field =
786 if record.fields.is_empty() && record.base_size.unwrap_or(0) == 0 {
787 quote! {
788 /// Prevent empty C++ struct being zero-size in Rust.
789 placeholder: std::mem::MaybeUninit<u8>,
790 }
791 } else {
792 quote! {}
793 };
Googlerf4792062021-10-20 07:21:21 +0000794
Devin Jeanpierre58181ac2022-02-14 21:30:05 +0000795 let no_unique_address_accessors = cc_struct_no_unique_address_impl(record, ir)?;
Devin Jeanpierre56777022022-02-03 01:57:15 +0000796 let base_class_into = cc_struct_upcast_impl(record, ir)?;
797
Michael Forsterdb8101a2021-10-08 06:56:03 +0000798 let record_tokens = quote! {
Michael Forster028800b2021-10-05 12:39:59 +0000799 #doc_comment
Devin Jeanpierre9227d2c2021-10-06 12:26:05 +0000800 #derives
Devin Jeanpierrec80e6242022-02-03 01:56:40 +0000801 #[repr(#( #repr_attributes ),*)]
Marcel Hlopkob4b28742021-09-15 12:45:20 +0000802 pub struct #ident {
Devin Jeanpierrec80e6242022-02-03 01:56:40 +0000803 #base_subobjects_field
Michael Forstercc5941a2021-10-07 07:12:24 +0000804 #( #field_doc_coments #field_accesses #field_idents: #field_types, )*
Googlerf4792062021-10-20 07:21:21 +0000805 #empty_struct_placeholder_field
Marcel Hlopkob4b28742021-09-15 12:45:20 +0000806 }
Googlerec648ff2021-09-23 07:19:53 +0000807
Devin Jeanpierre58181ac2022-02-14 21:30:05 +0000808 #no_unique_address_accessors
809
Devin Jeanpierre56777022022-02-03 01:57:15 +0000810 #base_class_into
811
Devin Jeanpierreea700d32021-10-06 11:33:56 +0000812 #unpin_impl
Devin Jeanpierre273eeae2021-10-06 13:29:35 +0000813 };
814
Michael Forsterdb8101a2021-10-08 06:56:03 +0000815 let assertion_tokens = quote! {
Googler209b10a2021-12-06 09:11:57 +0000816 const _: () = assert!(std::mem::size_of::<#ident>() == #size);
817 const _: () = assert!(std::mem::align_of::<#ident>() == #alignment);
Michael Forsterdb8101a2021-10-08 06:56:03 +0000818 #( #field_assertions )*
819 };
820
821 Ok((
822 RsSnippet { features: record_features, tokens: record_tokens },
823 RsSnippet { features: assertion_features, tokens: assertion_tokens },
824 ))
Marcel Hlopkob4b28742021-09-15 12:45:20 +0000825}
826
Lukasz Anforowicz2e41bb62022-01-11 18:23:07 +0000827fn should_derive_clone(record: &Record) -> bool {
Devin Jeanpierre88343c72022-01-15 01:10:23 +0000828 record.is_unpin()
Devin Jeanpierre2ed14ec2021-10-06 11:32:19 +0000829 && record.copy_constructor.access == ir::AccessSpecifier::Public
830 && record.copy_constructor.definition == SpecialMemberDefinition::Trivial
Lukasz Anforowicz2e41bb62022-01-11 18:23:07 +0000831}
832
Lukasz Anforowicze57215c2022-01-12 14:54:16 +0000833fn should_derive_copy(record: &Record) -> bool {
834 // TODO(b/202258760): Make `Copy` inclusion configurable.
835 should_derive_clone(record)
836}
837
838fn generate_derives(record: &Record) -> Vec<Ident> {
839 let mut derives = vec![];
Lukasz Anforowicz2e41bb62022-01-11 18:23:07 +0000840 if should_derive_clone(record) {
Marcel Hlopkoeaae9b72022-01-21 15:54:11 +0000841 derives.push(make_rs_ident("Clone"));
Devin Jeanpierre2ed14ec2021-10-06 11:32:19 +0000842 }
Lukasz Anforowicze57215c2022-01-12 14:54:16 +0000843 if should_derive_copy(record) {
Marcel Hlopkoeaae9b72022-01-21 15:54:11 +0000844 derives.push(make_rs_ident("Copy"));
Lukasz Anforowicze57215c2022-01-12 14:54:16 +0000845 }
846 derives
Devin Jeanpierre2ed14ec2021-10-06 11:32:19 +0000847}
848
Teddy Katz76fa42b2022-02-23 01:22:56 +0000849fn generate_enum(enum_: &Enum, ir: &IR) -> Result<TokenStream> {
850 let name = make_rs_ident(&enum_.identifier.identifier);
851 let underlying_type = format_rs_type(&enum_.underlying_type.rs_type, ir, &HashMap::new())?;
852 let enumerator_names =
853 enum_.enumerators.iter().map(|enumerator| make_rs_ident(&enumerator.identifier.identifier));
854 let enumerator_values = enum_.enumerators.iter().map(|enumerator| enumerator.value);
855 Ok(quote! {
856 #[repr(transparent)]
857 #[derive(Debug, PartialEq, Eq, Copy, Clone, Hash, PartialOrd, Ord)]
858 pub struct #name(#underlying_type);
859 impl #name {
860 #(pub const #enumerator_names: #name = #name(#enumerator_values);)*
861 }
862 impl From<#underlying_type> for #name {
863 fn from(value: #underlying_type) -> #name {
864 #name(v)
865 }
866 }
867 impl From<#name> for #underlying_type {
868 fn from(value: #name) -> #underlying_type {
869 v.0
870 }
871 }
872 })
873}
874
Googler6a0a5252022-01-11 14:08:09 +0000875fn generate_type_alias(type_alias: &TypeAlias, ir: &IR) -> Result<TokenStream> {
Marcel Hlopkoeaae9b72022-01-21 15:54:11 +0000876 let ident = make_rs_ident(&type_alias.identifier.identifier);
Googler6a0a5252022-01-11 14:08:09 +0000877 let underlying_type = format_rs_type(&type_alias.underlying_type.rs_type, ir, &HashMap::new())
878 .with_context(|| format!("Failed to format underlying type for {:?}", type_alias))?;
879 Ok(quote! {pub type #ident = #underlying_type;})
880}
881
Michael Forster523dbd42021-10-12 11:05:44 +0000882/// Generates Rust source code for a given `UnsupportedItem`.
883fn generate_unsupported(item: &UnsupportedItem) -> Result<TokenStream> {
Googler48a74dd2021-10-25 07:31:53 +0000884 let location = if item.source_loc.filename.is_empty() {
885 "<unknown location>".to_string()
886 } else {
887 // TODO(forster): The "google3" prefix should probably come from a command line
888 // argument.
889 // TODO(forster): Consider linking to the symbol instead of to the line number
890 // to avoid wrong links while generated files have not caught up.
891 format!("google3/{};l={}", &item.source_loc.filename, &item.source_loc.line)
892 };
Michael Forster6a184ad2021-10-12 13:04:05 +0000893 let message = format!(
Googler48a74dd2021-10-25 07:31:53 +0000894 "{}\nError while generating bindings for item '{}':\n{}",
895 &location, &item.name, &item.message
Michael Forster6a184ad2021-10-12 13:04:05 +0000896 );
Michael Forster523dbd42021-10-12 11:05:44 +0000897 Ok(quote! { __COMMENT__ #message })
898}
899
Michael Forsterf1dce422021-10-13 09:50:16 +0000900/// Generates Rust source code for a given `Comment`.
901fn generate_comment(comment: &Comment) -> Result<TokenStream> {
902 let text = &comment.text;
903 Ok(quote! { __COMMENT__ #text })
904}
905
Marcel Hlopko89547752021-12-10 09:39:41 +0000906fn generate_rs_api(ir: &IR) -> Result<TokenStream> {
Michael Forstered642022021-10-04 09:48:25 +0000907 let mut items = vec![];
Marcel Hlopko42abfc82021-08-09 07:03:17 +0000908 let mut thunks = vec![];
Michael Forsterdb8101a2021-10-08 06:56:03 +0000909 let mut assertions = vec![];
Marcel Hlopko42abfc82021-08-09 07:03:17 +0000910
Googler454f2652021-12-06 12:53:12 +0000911 // We import nullable pointers as an Option<&T> and assume that at the ABI
912 // level, None is represented as a zero pointer value whereas Some is
913 // represented as as non-zero pointer value. This seems like a pretty safe
914 // assumption to make, but to provide some safeguard, assert that
915 // `Option<&i32>` and `&i32` have the same size.
916 assertions.push(quote! {
917 const _: () = assert!(std::mem::size_of::<Option<&i32>>() == std::mem::size_of::<&i32>());
918 });
919
Michael Forsterbee84482021-10-13 08:35:38 +0000920 // TODO(jeanpierreda): Delete has_record, either in favor of using RsSnippet, or not
921 // having uses. See https://chat.google.com/room/AAAAnQmj8Qs/6QbkSvWcfhA
Devin Jeanpierreea700d32021-10-06 11:33:56 +0000922 let mut has_record = false;
Devin Jeanpierre273eeae2021-10-06 13:29:35 +0000923 let mut features = BTreeSet::new();
Marcel Hlopko42abfc82021-08-09 07:03:17 +0000924
Lukasz Anforowicz72c4d222022-02-18 19:07:28 +0000925 // For #![rustfmt::skip].
926 features.insert(make_rs_ident("custom_inner_attributes"));
927
Googlerd03d05b2022-01-07 10:10:57 +0000928 // Identify all functions having overloads that we can't import (yet).
929 // TODO(b/213280424): Implement support for overloaded functions.
930 let mut seen_funcs = HashSet::new();
931 let mut overloaded_funcs = HashSet::new();
932 for func in ir.functions() {
Lukasz Anforowiczeb19ac62022-02-05 00:10:16 +0000933 if let GeneratedFunc::Some { function_id, .. } = generate_func(func, ir)? {
Googlerd03d05b2022-01-07 10:10:57 +0000934 if !seen_funcs.insert(function_id.clone()) {
935 overloaded_funcs.insert(function_id);
936 }
937 }
938 }
939
Marcel Hlopko3b9bf9e2021-11-29 08:25:14 +0000940 for item in ir.items() {
Michael Forstered642022021-10-04 09:48:25 +0000941 match item {
Lukasz Anforowiczeb19ac62022-02-05 00:10:16 +0000942 Item::Func(func) => match generate_func(func, ir)? {
943 GeneratedFunc::None => (),
944 GeneratedFunc::Unsupported(unsupported) => {
945 items.push(generate_unsupported(&unsupported)?)
946 }
947 GeneratedFunc::Some { api_func, thunk, function_id } => {
Googlerd03d05b2022-01-07 10:10:57 +0000948 if overloaded_funcs.contains(&function_id) {
Lukasz Anforowiczeb19ac62022-02-05 00:10:16 +0000949 items.push(generate_unsupported(&make_unsupported_fn(
950 func,
951 ir,
952 "Cannot generate bindings for overloaded function",
953 )?)?);
Googlerd03d05b2022-01-07 10:10:57 +0000954 continue;
955 }
Lukasz Anforowiczeb19ac62022-02-05 00:10:16 +0000956 features.extend(api_func.features);
Googlerd03d05b2022-01-07 10:10:57 +0000957 features.extend(thunk.features);
Lukasz Anforowiczeb19ac62022-02-05 00:10:16 +0000958 items.push(api_func.tokens);
Googlerd03d05b2022-01-07 10:10:57 +0000959 thunks.push(thunk.tokens);
960 }
Lukasz Anforowiczeb19ac62022-02-05 00:10:16 +0000961 },
Michael Forstered642022021-10-04 09:48:25 +0000962 Item::Record(record) => {
Googler6a0a5252022-01-11 14:08:09 +0000963 if !ir.is_current_target(&record.owning_target)
964 && !ir.is_stdlib_target(&record.owning_target)
965 {
Marcel Hlopkoa0f38662021-12-03 08:45:26 +0000966 continue;
967 }
Marcel Hlopkoc0956cf2021-11-29 08:31:28 +0000968 let (snippet, assertions_snippet) = generate_record(record, ir)?;
Devin Jeanpierre273eeae2021-10-06 13:29:35 +0000969 features.extend(snippet.features);
Michael Forsterdb8101a2021-10-08 06:56:03 +0000970 features.extend(assertions_snippet.features);
Devin Jeanpierre273eeae2021-10-06 13:29:35 +0000971 items.push(snippet.tokens);
Michael Forsterdb8101a2021-10-08 06:56:03 +0000972 assertions.push(assertions_snippet.tokens);
Devin Jeanpierreea700d32021-10-06 11:33:56 +0000973 has_record = true;
Michael Forstered642022021-10-04 09:48:25 +0000974 }
Teddy Katz76fa42b2022-02-23 01:22:56 +0000975 Item::Enum(enum_) => {
976 if !ir.is_current_target(&enum_.owning_target)
977 && !ir.is_stdlib_target(&enum_.owning_target)
978 {
979 continue;
980 }
981 items.push(generate_enum(enum_, ir)?);
982 continue;
983 }
Googler098c4582022-01-10 12:29:34 +0000984 Item::TypeAlias(type_alias) => {
Googler6a0a5252022-01-11 14:08:09 +0000985 if !ir.is_current_target(&type_alias.owning_target)
986 && !ir.is_stdlib_target(&type_alias.owning_target)
987 {
988 continue;
989 }
990 items.push(generate_type_alias(type_alias, ir)?);
Googler098c4582022-01-10 12:29:34 +0000991 }
Michael Forster523dbd42021-10-12 11:05:44 +0000992 Item::UnsupportedItem(unsupported) => items.push(generate_unsupported(unsupported)?),
Michael Forsterf1dce422021-10-13 09:50:16 +0000993 Item::Comment(comment) => items.push(generate_comment(comment)?),
Michael Forstered642022021-10-04 09:48:25 +0000994 }
Marcel Hlopko42abfc82021-08-09 07:03:17 +0000995 }
996
Marcel Hlopkob4b28742021-09-15 12:45:20 +0000997 let mod_detail = if thunks.is_empty() {
998 quote! {}
999 } else {
1000 quote! {
1001 mod detail {
Googler55647142022-01-11 12:37:39 +00001002 #[allow(unused_imports)]
Devin Jeanpierred4dde0e2021-10-13 20:48:25 +00001003 use super::*;
Marcel Hlopkob4b28742021-09-15 12:45:20 +00001004 extern "C" {
1005 #( #thunks )*
1006 }
Marcel Hlopko42abfc82021-08-09 07:03:17 +00001007 }
1008 }
1009 };
1010
Devin Jeanpierreea700d32021-10-06 11:33:56 +00001011 let imports = if has_record {
Googlerec648ff2021-09-23 07:19:53 +00001012 quote! {
Googleraaa0a532021-10-01 09:11:27 +00001013 use memoffset_unstable_const::offset_of;
Googlerec648ff2021-09-23 07:19:53 +00001014 }
Michael Forstered642022021-10-04 09:48:25 +00001015 } else {
1016 quote! {}
Googlerec648ff2021-09-23 07:19:53 +00001017 };
1018
Devin Jeanpierre273eeae2021-10-06 13:29:35 +00001019 let features = if features.is_empty() {
1020 quote! {}
1021 } else {
1022 quote! {
1023 #![feature( #(#features),* )]
1024 }
1025 };
1026
Marcel Hlopko89547752021-12-10 09:39:41 +00001027 Ok(quote! {
Googler55647142022-01-11 12:37:39 +00001028 #features __NEWLINE__
1029 #![allow(non_camel_case_types)] __NEWLINE__
1030 #![allow(non_snake_case)] __NEWLINE__ __NEWLINE__
1031
Michael Forsterdb8101a2021-10-08 06:56:03 +00001032 #imports __NEWLINE__ __NEWLINE__
Googlerec648ff2021-09-23 07:19:53 +00001033
Michael Forsterdb8101a2021-10-08 06:56:03 +00001034 #( #items __NEWLINE__ __NEWLINE__ )*
Marcel Hlopkob4b28742021-09-15 12:45:20 +00001035
Michael Forsterdb8101a2021-10-08 06:56:03 +00001036 #mod_detail __NEWLINE__ __NEWLINE__
1037
1038 #( #assertions __NEWLINE__ __NEWLINE__ )*
Marcel Hlopko89547752021-12-10 09:39:41 +00001039 })
Marcel Hlopko42abfc82021-08-09 07:03:17 +00001040}
1041
Marcel Hlopkoeaae9b72022-01-21 15:54:11 +00001042/// Makes an 'Ident' to be used in the Rust source code. Escapes Rust keywords.
1043fn make_rs_ident(ident: &str) -> Ident {
1044 // TODO(https://github.com/dtolnay/syn/pull/1098): Remove the hardcoded list once syn recognizes
1045 // 2018 and 2021 keywords.
1046 if ["async", "await", "try", "dyn"].contains(&ident) {
1047 return format_ident!("r#{}", ident);
1048 }
1049 match syn::parse_str::<syn::Ident>(ident) {
1050 Ok(_) => format_ident!("{}", ident),
1051 Err(_) => format_ident!("r#{}", ident),
1052 }
1053}
1054
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +00001055/// Formats a C++ identifier. Does not escape C++ keywords.
1056fn format_cc_ident(ident: &str) -> TokenStream {
1057 ident.parse().unwrap()
Marcel Hlopko42abfc82021-08-09 07:03:17 +00001058}
1059
Googler6a0a5252022-01-11 14:08:09 +00001060fn rs_type_name_for_target_and_identifier(
1061 owning_target: &BlazeLabel,
1062 identifier: &ir::Identifier,
1063 ir: &IR,
1064) -> Result<TokenStream> {
Marcel Hlopkoeaae9b72022-01-21 15:54:11 +00001065 let ident = make_rs_ident(identifier.identifier.as_str());
Googler6a0a5252022-01-11 14:08:09 +00001066
1067 if ir.is_current_target(owning_target) || ir.is_stdlib_target(owning_target) {
1068 Ok(quote! {#ident})
1069 } else {
Marcel Hlopkod906b892022-01-27 08:52:36 +00001070 let owning_crate_name = owning_target.target_name()?;
1071 // TODO(b/216587072): Remove this hacky escaping and use the import! macro once
1072 // available
Lukasz Anforowiczdd9ae0f2022-02-17 15:52:53 +00001073 let escaped_owning_crate_name = owning_crate_name.replace('-', "_");
Marcel Hlopkod906b892022-01-27 08:52:36 +00001074 let owning_crate = make_rs_ident(&escaped_owning_crate_name);
Googler6a0a5252022-01-11 14:08:09 +00001075 Ok(quote! {#owning_crate::#ident})
1076 }
1077}
1078
Lukasz Anforowicz40c2eb82022-01-11 18:22:31 +00001079#[derive(Debug, Eq, PartialEq)]
1080enum Mutability {
1081 Const,
1082 Mut,
1083}
1084
1085impl Mutability {
1086 fn format_for_pointer(&self) -> TokenStream {
1087 match self {
1088 Mutability::Mut => quote! {mut},
1089 Mutability::Const => quote! {const},
1090 }
1091 }
1092
1093 fn format_for_reference(&self) -> TokenStream {
1094 match self {
1095 Mutability::Mut => quote! {mut},
1096 Mutability::Const => quote! {},
1097 }
1098 }
1099}
1100
1101// TODO(b/213947473): Instead of having a separate RsTypeKind here, consider
1102// changing ir::RsType into a similar `enum`, with fields that contain
1103// references (e.g. &'ir Record`) instead of DeclIds.
1104#[derive(Debug)]
1105enum RsTypeKind<'ir> {
1106 Pointer { pointee: Box<RsTypeKind<'ir>>, mutability: Mutability },
1107 Reference { referent: Box<RsTypeKind<'ir>>, mutability: Mutability, lifetime_id: LifetimeId },
Lukasz Anforowiczcf230fd2022-02-18 19:20:39 +00001108 FuncPtr { abi: &'ir str, return_type: Box<RsTypeKind<'ir>>, param_types: Vec<RsTypeKind<'ir>> },
Lukasz Anforowicz40c2eb82022-01-11 18:22:31 +00001109 Record(&'ir Record),
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00001110 TypeAlias { type_alias: &'ir TypeAlias, underlying_type: Box<RsTypeKind<'ir>> },
Lukasz Anforowicz40c2eb82022-01-11 18:22:31 +00001111 Unit,
1112 Other { name: &'ir str, type_args: Vec<RsTypeKind<'ir>> },
1113}
1114
1115impl<'ir> RsTypeKind<'ir> {
1116 pub fn new(ty: &'ir ir::RsType, ir: &'ir IR) -> Result<Self> {
1117 // The lambdas deduplicate code needed by multiple `match` branches.
1118 let get_type_args = || -> Result<Vec<RsTypeKind<'ir>>> {
1119 ty.type_args.iter().map(|type_arg| RsTypeKind::<'ir>::new(type_arg, ir)).collect()
1120 };
1121 let get_pointee = || -> Result<Box<RsTypeKind<'ir>>> {
1122 if ty.type_args.len() != 1 {
1123 bail!("Missing pointee/referent type (need exactly 1 type argument): {:?}", ty);
1124 }
1125 Ok(Box::new(get_type_args()?.remove(0)))
1126 };
1127 let get_lifetime = || -> Result<LifetimeId> {
1128 if ty.lifetime_args.len() != 1 {
1129 bail!("Missing reference lifetime (need exactly 1 lifetime argument): {:?}", ty);
1130 }
1131 Ok(ty.lifetime_args[0])
1132 };
1133
1134 let result = match ty.name.as_deref() {
1135 None => {
1136 ensure!(
1137 ty.type_args.is_empty(),
1138 "Type arguments on records nor type aliases are not yet supported: {:?}",
1139 ty
1140 );
1141 match ir.item_for_type(ty)? {
1142 Item::Record(record) => RsTypeKind::Record(record),
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00001143 Item::TypeAlias(type_alias) => RsTypeKind::TypeAlias {
1144 type_alias,
1145 underlying_type: Box::new(RsTypeKind::new(
1146 &type_alias.underlying_type.rs_type,
1147 ir,
1148 )?),
1149 },
Lukasz Anforowicz40c2eb82022-01-11 18:22:31 +00001150 other_item => bail!("Item does not define a type: {:?}", other_item),
1151 }
1152 }
1153 Some(name) => match name {
1154 "()" => {
1155 if !ty.type_args.is_empty() {
1156 bail!("Unit type must not have type arguments: {:?}", ty);
1157 }
1158 RsTypeKind::Unit
1159 }
1160 "*mut" => {
1161 RsTypeKind::Pointer { pointee: get_pointee()?, mutability: Mutability::Mut }
1162 }
1163 "*const" => {
1164 RsTypeKind::Pointer { pointee: get_pointee()?, mutability: Mutability::Const }
1165 }
1166 "&mut" => RsTypeKind::Reference {
1167 referent: get_pointee()?,
1168 mutability: Mutability::Mut,
1169 lifetime_id: get_lifetime()?,
1170 },
1171 "&" => RsTypeKind::Reference {
1172 referent: get_pointee()?,
1173 mutability: Mutability::Const,
1174 lifetime_id: get_lifetime()?,
1175 },
Lukasz Anforowiczcf230fd2022-02-18 19:20:39 +00001176 name => {
1177 let mut type_args = get_type_args()?;
1178 match name.strip_prefix("#funcPtr ") {
1179 None => RsTypeKind::Other { name, type_args },
1180 Some(abi) => {
1181 // TODO(b/217419782): Consider enforcing `'static` lifetime.
1182 ensure!(!type_args.is_empty(), "No return type in fn type: {:?}", ty);
1183 RsTypeKind::FuncPtr {
1184 abi,
1185 return_type: Box::new(type_args.remove(type_args.len() - 1)),
1186 param_types: type_args,
1187 }
1188 },
1189 }
1190 },
Lukasz Anforowicz40c2eb82022-01-11 18:22:31 +00001191 },
1192 };
1193 Ok(result)
1194 }
1195
Devin Jeanpierre149950d2022-02-22 21:02:02 +00001196 /// Returns true if the type is known to be `Unpin`, false otherwise.
1197 pub fn is_unpin(&self, ir: &IR) -> bool {
1198 match self {
1199 RsTypeKind::Record(record) => record.is_unpin(),
1200 RsTypeKind::TypeAlias { underlying_type, .. } => underlying_type.is_unpin(ir),
1201 _ => true,
1202 }
1203 }
1204
Lukasz Anforowicz40c2eb82022-01-11 18:22:31 +00001205 pub fn format(
1206 &self,
1207 ir: &IR,
1208 lifetime_to_name: &HashMap<LifetimeId, String>,
1209 ) -> Result<TokenStream> {
1210 let result = match self {
1211 RsTypeKind::Pointer { pointee, mutability } => {
1212 let mutability = mutability.format_for_pointer();
1213 let nested_type = pointee.format(ir, lifetime_to_name)?;
1214 quote! {* #mutability #nested_type}
1215 }
1216 RsTypeKind::Reference { referent, mutability, lifetime_id } => {
Devin Jeanpierre149950d2022-02-22 21:02:02 +00001217 let mut_ = mutability.format_for_reference();
Lukasz Anforowicz40c2eb82022-01-11 18:22:31 +00001218 let lifetime = Self::format_lifetime(lifetime_id, lifetime_to_name)?;
1219 let nested_type = referent.format(ir, lifetime_to_name)?;
Devin Jeanpierre149950d2022-02-22 21:02:02 +00001220 let reference = quote! {& #lifetime #mut_ #nested_type};
1221 if mutability == &Mutability::Mut && !referent.is_unpin(ir) {
1222 // TODO(b/200067242): Add a `use std::pin::Pin` to the crate, and use `Pin`.
1223 // Probably format needs to return an RsSnippet, and RsSnippet needs a `uses`
1224 // field.
1225 quote! {std::pin::Pin< #reference >}
1226 } else {
1227 reference
1228 }
Lukasz Anforowicz40c2eb82022-01-11 18:22:31 +00001229 }
Lukasz Anforowiczcf230fd2022-02-18 19:20:39 +00001230 RsTypeKind::FuncPtr { abi, return_type, param_types } => {
1231 let return_frag = return_type.format_as_return_type_fragment(ir, lifetime_to_name)?;
1232 let param_types = param_types
1233 .iter()
1234 .map(|t| t.format(ir, lifetime_to_name))
1235 .collect::<Result<Vec<_>>>()?;
1236 quote!{ extern #abi fn( #( #param_types ),* ) #return_frag }
1237 },
Lukasz Anforowicz40c2eb82022-01-11 18:22:31 +00001238 RsTypeKind::Record(record) => rs_type_name_for_target_and_identifier(
1239 &record.owning_target,
1240 &record.identifier,
1241 ir,
1242 )?,
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00001243 RsTypeKind::TypeAlias { type_alias, .. } => rs_type_name_for_target_and_identifier(
Lukasz Anforowicz40c2eb82022-01-11 18:22:31 +00001244 &type_alias.owning_target,
1245 &type_alias.identifier,
1246 ir,
1247 )?,
1248 RsTypeKind::Unit => quote! {()},
1249 RsTypeKind::Other { name, type_args } => {
Marcel Hlopkoeaae9b72022-01-21 15:54:11 +00001250 let ident = make_rs_ident(name);
Lukasz Anforowicz40c2eb82022-01-11 18:22:31 +00001251 let generic_params = format_generic_params(
1252 type_args
1253 .iter()
1254 .map(|type_arg| type_arg.format(ir, lifetime_to_name))
1255 .collect::<Result<Vec<_>>>()?,
1256 );
1257 quote! {#ident #generic_params}
1258 }
1259 };
1260 Ok(result)
1261 }
1262
Lukasz Anforowiczcf230fd2022-02-18 19:20:39 +00001263 pub fn format_as_return_type_fragment(
1264 &self,
1265 ir: &IR,
1266 lifetime_to_name: &HashMap<LifetimeId, String>,
1267 ) -> Result<TokenStream> {
1268 match self {
1269 RsTypeKind::Unit => Ok(quote! {}),
1270 other_type => {
1271 let return_type = other_type.format(ir, lifetime_to_name)?;
1272 Ok(quote! { -> #return_type })
1273 }
1274 }
1275 }
1276
Lukasz Anforowiczcde4b1b2022-02-03 21:20:55 +00001277 /// Formats this RsTypeKind as `&'a mut MaybeUninit<SomeStruct>`. This is
1278 /// used to format `__this` parameter in a constructor thunk.
1279 pub fn format_mut_ref_as_uninitialized(
Lukasz Anforowicz40c2eb82022-01-11 18:22:31 +00001280 &self,
1281 ir: &IR,
1282 lifetime_to_name: &HashMap<LifetimeId, String>,
1283 ) -> Result<TokenStream> {
Lukasz Anforowiczcde4b1b2022-02-03 21:20:55 +00001284 match self {
1285 RsTypeKind::Reference { referent, lifetime_id, mutability: Mutability::Mut } => {
1286 let nested_type = referent.format(ir, lifetime_to_name)?;
1287 let lifetime = Self::format_lifetime(lifetime_id, lifetime_to_name)?;
1288 Ok(quote! { & #lifetime mut std::mem::MaybeUninit< #nested_type > })
Lukasz Anforowicz40c2eb82022-01-11 18:22:31 +00001289 }
Devin Jeanpierre149950d2022-02-22 21:02:02 +00001290 _ => bail!("Expected reference to format as MaybeUninit, got: {:?}", self),
Lukasz Anforowiczcde4b1b2022-02-03 21:20:55 +00001291 }
Lukasz Anforowicz40c2eb82022-01-11 18:22:31 +00001292 }
1293
Devin Jeanpierre149950d2022-02-22 21:02:02 +00001294 /// Formats a reference or pointer as a raw pointer.
1295 pub fn format_ref_as_raw_ptr(
1296 &self,
1297 ir: &IR,
1298 lifetime_to_name: &HashMap<LifetimeId, String>,
1299 ) -> Result<TokenStream> {
1300 match self {
1301 RsTypeKind::Reference { referent: pointee, mutability, .. }
1302 | RsTypeKind::Pointer { pointee, mutability } => {
1303 let nested_type = pointee.format(ir, lifetime_to_name)?;
1304 let mut_ = mutability.format_for_pointer();
1305 Ok(quote! { * #mut_ #nested_type })
1306 }
1307 _ => bail!("Expected reference to format as raw ptr, got: {:?}", self),
1308 }
1309 }
1310
1311 /// Formats this RsTypeKind as the `self` parameter: usually, `&'a self` or
1312 /// `&'a mut self`.
1313 ///
1314 /// If this is !Unpin, however, it uses `self: Pin<&mut Self>` instead.
Lukasz Anforowiczcde4b1b2022-02-03 21:20:55 +00001315 pub fn format_as_self_param(
Lukasz Anforowicz231a3bb2022-01-12 14:05:59 +00001316 &self,
Lukasz Anforowiczce345392022-01-14 22:41:16 +00001317 func: &Func,
1318 ir: &IR,
Lukasz Anforowicz231a3bb2022-01-12 14:05:59 +00001319 lifetime_to_name: &HashMap<LifetimeId, String>,
Lukasz Anforowiczf9564622022-01-28 14:31:04 +00001320 ) -> Result<TokenStream> {
Lukasz Anforowicz732ca642022-02-03 20:58:38 +00001321 if func.name == UnqualifiedIdentifier::Destructor {
1322 let record = func
1323 .member_func_metadata
1324 .as_ref()
1325 .ok_or_else(|| anyhow!("Destructors must be member functions: {:?}", func))?
1326 .find_record(ir)?;
1327 if self.is_mut_ptr_to(record) {
1328 // Even in C++ it is UB to retain `this` pointer and dereference it
1329 // after a destructor runs. Therefore it is safe to use `&self` or
1330 // `&mut self` in Rust even if IR represents `__this` as a Rust
1331 // pointer (e.g. when lifetime annotations are missing - lifetime
1332 // annotations are required to represent it as a Rust reference).
1333 return Ok(quote! { &mut self });
1334 }
Lukasz Anforowicz231a3bb2022-01-12 14:05:59 +00001335 }
1336
1337 match self {
Devin Jeanpierre149950d2022-02-22 21:02:02 +00001338 RsTypeKind::Reference { referent, lifetime_id, mutability } => {
1339 let mut_ = mutability.format_for_reference();
Lukasz Anforowicz231a3bb2022-01-12 14:05:59 +00001340 let lifetime = Self::format_lifetime(lifetime_id, lifetime_to_name)?;
Devin Jeanpierre149950d2022-02-22 21:02:02 +00001341 if mutability == &Mutability::Mut && !referent.is_unpin(ir) && func.name != UnqualifiedIdentifier::Destructor {
1342 // TODO(b/200067242): Add a `use std::pin::Pin` to the crate, and use `Pin`.
1343 Ok(quote! {self: std::pin::Pin< & #lifetime #mut_ Self>})
1344 } else {
1345 Ok(quote! { & #lifetime #mut_ self })
1346 }
Lukasz Anforowicz231a3bb2022-01-12 14:05:59 +00001347 }
Lukasz Anforowicz732ca642022-02-03 20:58:38 +00001348 _ => bail!("Unexpected type of `self` parameter: {:?}", self),
Lukasz Anforowicz231a3bb2022-01-12 14:05:59 +00001349 }
1350 }
1351
Lukasz Anforowicz40c2eb82022-01-11 18:22:31 +00001352 fn format_lifetime(
1353 lifetime_id: &LifetimeId,
1354 lifetime_to_name: &HashMap<LifetimeId, String>,
1355 ) -> Result<TokenStream> {
1356 let lifetime_name = lifetime_to_name.get(lifetime_id).ok_or_else(|| {
1357 anyhow!("`lifetime_to_name` doesn't have an entry for {:?}", lifetime_id)
1358 })?;
Lukasz Anforowicz95551272022-01-20 00:02:24 +00001359 Ok(format_lifetime_name(lifetime_name))
Lukasz Anforowicz40c2eb82022-01-11 18:22:31 +00001360 }
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00001361
Lukasz Anforowicz90bdb962022-02-14 21:07:45 +00001362 /// Returns whether the type represented by `self` implements the `Copy`
1363 /// trait.
Lukasz Anforowicza94ab702022-01-14 22:40:25 +00001364 pub fn implements_copy(&self) -> bool {
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00001365 // TODO(b/212696226): Verify results of `implements_copy` via static
1366 // assertions in the generated Rust code (because incorrect results
1367 // can silently lead to unsafe behavior).
1368 match self {
1369 RsTypeKind::Unit => true,
1370 RsTypeKind::Pointer { .. } => true,
Lukasz Anforowiczcf230fd2022-02-18 19:20:39 +00001371 RsTypeKind::FuncPtr { .. } => true,
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00001372 RsTypeKind::Reference { mutability: Mutability::Const, .. } => true,
1373 RsTypeKind::Reference { mutability: Mutability::Mut, .. } => false,
1374 RsTypeKind::Record(record) => should_derive_copy(record),
1375 RsTypeKind::TypeAlias { underlying_type, .. } => underlying_type.implements_copy(),
Lukasz Anforowiczd81bea92022-02-11 08:57:58 +00001376 RsTypeKind::Other { type_args, .. } => {
Lukasz Anforowicz20651e32022-02-10 14:52:15 +00001377 // All types that may appear here without `type_args` (e.g.
1378 // primitive types like `i32`) implement `Copy`. Generic types
1379 // that may be present here (e.g. Option<...>) are `Copy` if all
1380 // of their `type_args` are `Copy`.
1381 type_args.iter().all(|t| t.implements_copy())
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00001382 }
1383 }
1384 }
Lukasz Anforowicza94ab702022-01-14 22:40:25 +00001385
Lukasz Anforowiczf9564622022-01-28 14:31:04 +00001386 pub fn is_mut_ptr_to(&self, expected_record: &Record) -> bool {
1387 match self {
1388 RsTypeKind::Pointer { pointee, mutability: Mutability::Mut, .. } => {
1389 pointee.is_record(expected_record)
1390 }
1391 _ => false,
1392 }
1393 }
1394
1395 pub fn is_ref_to(&self, expected_record: &Record) -> bool {
1396 match self {
1397 RsTypeKind::Reference { referent, .. } => referent.is_record(expected_record),
1398 _ => false,
1399 }
1400 }
1401
Lukasz Anforowicza94ab702022-01-14 22:40:25 +00001402 pub fn is_shared_ref_to(&self, expected_record: &Record) -> bool {
1403 match self {
1404 RsTypeKind::Reference { referent, mutability: Mutability::Const, .. } => {
Lukasz Anforowiczf9564622022-01-28 14:31:04 +00001405 referent.is_record(expected_record)
Lukasz Anforowicza94ab702022-01-14 22:40:25 +00001406 }
1407 _ => false,
1408 }
1409 }
Lukasz Anforowiczf9564622022-01-28 14:31:04 +00001410
1411 pub fn is_record(&self, expected_record: &Record) -> bool {
1412 match self {
1413 RsTypeKind::Record(actual_record) => actual_record.id == expected_record.id,
1414 _ => false,
1415 }
1416 }
Lukasz Anforowicz90bdb962022-02-14 21:07:45 +00001417
1418 /// Iterates over `self` and all the nested types (e.g. pointees, generic
1419 /// type args, etc.) in DFS order.
1420 pub fn dfs_iter<'ty>(&'ty self) -> impl Iterator<Item = &'ty RsTypeKind<'ir>> + '_ {
1421 RsTypeKindIter::new(self)
1422 }
1423
1424 /// Iterates over all `LifetimeId`s in `self` and in all the nested types.
1425 /// Note that the results might contain duplicate LifetimeId values (e.g.
1426 /// if the same LifetimeId is used in two `type_args`).
1427 pub fn lifetimes(&self) -> impl Iterator<Item = LifetimeId> + '_ {
1428 self.dfs_iter().filter_map(|t| match t {
1429 RsTypeKind::Reference { lifetime_id, .. } => Some(*lifetime_id),
1430 _ => None,
1431 })
1432 }
1433}
1434
1435struct RsTypeKindIter<'ty, 'ir> {
1436 todo: Vec<&'ty RsTypeKind<'ir>>,
1437}
1438
1439impl<'ty, 'ir> RsTypeKindIter<'ty, 'ir> {
1440 pub fn new(ty: &'ty RsTypeKind<'ir>) -> Self {
1441 Self { todo: vec![ty] }
1442 }
1443}
1444
1445impl<'ty, 'ir> Iterator for RsTypeKindIter<'ty, 'ir> {
1446 type Item = &'ty RsTypeKind<'ir>;
1447
1448 fn next(&mut self) -> Option<Self::Item> {
1449 match self.todo.pop() {
1450 None => None,
1451 Some(curr) => {
1452 match curr {
1453 RsTypeKind::Unit | RsTypeKind::Record(_) => (),
1454 RsTypeKind::Pointer { pointee, .. } => self.todo.push(pointee),
1455 RsTypeKind::Reference { referent, .. } => self.todo.push(referent),
1456 RsTypeKind::TypeAlias { underlying_type: t, .. } => self.todo.push(t),
Lukasz Anforowiczcf230fd2022-02-18 19:20:39 +00001457 RsTypeKind::FuncPtr { return_type, param_types, .. } => {
1458 self.todo.push(return_type);
1459 self.todo.extend(param_types.iter().rev());
1460 },
Lukasz Anforowicz90bdb962022-02-14 21:07:45 +00001461 RsTypeKind::Other { type_args, .. } => self.todo.extend(type_args.iter().rev()),
1462 };
1463 Some(curr)
1464 }
1465 }
1466 }
Lukasz Anforowicz40c2eb82022-01-11 18:22:31 +00001467}
1468
Lukasz Anforowicz95551272022-01-20 00:02:24 +00001469fn format_lifetime_name(lifetime_name: &str) -> TokenStream {
1470 let lifetime =
1471 syn::Lifetime::new(&format!("'{}", lifetime_name), proc_macro2::Span::call_site());
1472 quote! { #lifetime }
1473}
1474
Googler7cced422021-12-06 11:58:39 +00001475fn format_rs_type(
1476 ty: &ir::RsType,
1477 ir: &IR,
1478 lifetime_to_name: &HashMap<LifetimeId, String>,
1479) -> Result<TokenStream> {
Lukasz Anforowicz40c2eb82022-01-11 18:22:31 +00001480 RsTypeKind::new(ty, ir)
1481 .and_then(|kind| kind.format(ir, lifetime_to_name))
1482 .with_context(|| format!("Failed to format Rust type {:?}", ty))
Devin Jeanpierre7a7328e2021-09-17 07:10:08 +00001483}
1484
Googler6a0a5252022-01-11 14:08:09 +00001485fn cc_type_name_for_item(item: &ir::Item) -> Result<TokenStream> {
1486 let (disambiguator_fragment, identifier) = match item {
1487 Item::Record(record) => (quote! { class }, &record.identifier),
1488 Item::TypeAlias(type_alias) => (quote! {}, &type_alias.identifier),
1489 _ => bail!("Item does not define a type: {:?}", item),
1490 };
1491
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +00001492 let ident = format_cc_ident(identifier.identifier.as_str());
Googler6a0a5252022-01-11 14:08:09 +00001493 Ok(quote! { #disambiguator_fragment #ident })
1494}
1495
Marcel Hlopkoc0956cf2021-11-29 08:31:28 +00001496fn format_cc_type(ty: &ir::CcType, ir: &IR) -> Result<TokenStream> {
Devin Jeanpierre09c6f452021-09-29 07:34:24 +00001497 let const_fragment = if ty.is_const {
Devin Jeanpierre184f9ac2021-09-17 13:47:03 +00001498 quote! {const}
1499 } else {
1500 quote! {}
1501 };
Marcel Hlopkoc0956cf2021-11-29 08:31:28 +00001502 if let Some(ref name) = ty.name {
1503 match name.as_str() {
1504 "*" => {
Googlerff7fc232021-12-02 09:43:00 +00001505 if ty.type_args.len() != 1 {
1506 bail!("Invalid pointer type (need exactly 1 type argument): {:?}", ty);
Marcel Hlopkoc0956cf2021-11-29 08:31:28 +00001507 }
Googlerff7fc232021-12-02 09:43:00 +00001508 assert_eq!(ty.type_args.len(), 1);
1509 let nested_type = format_cc_type(&ty.type_args[0], ir)?;
Marcel Hlopkoc0956cf2021-11-29 08:31:28 +00001510 Ok(quote! {#nested_type * #const_fragment})
Devin Jeanpierre7a7328e2021-09-17 07:10:08 +00001511 }
Lukasz Anforowicz275fa922022-01-05 16:13:20 +00001512 "&" => {
1513 if ty.type_args.len() != 1 {
1514 bail!("Invalid reference type (need exactly 1 type argument): {:?}", ty);
1515 }
1516 let nested_type = format_cc_type(&ty.type_args[0], ir)?;
1517 Ok(quote! {#nested_type &})
1518 }
Lukasz Anforowicz957cbf22022-01-05 16:14:05 +00001519 cc_type_name => {
Googlerff7fc232021-12-02 09:43:00 +00001520 if !ty.type_args.is_empty() {
Marcel Hlopkoc0956cf2021-11-29 08:31:28 +00001521 bail!("Type not yet supported: {:?}", ty);
1522 }
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +00001523 let idents = cc_type_name.split_whitespace().map(format_cc_ident);
Lukasz Anforowicz957cbf22022-01-05 16:14:05 +00001524 Ok(quote! {#( #idents )* #const_fragment})
Devin Jeanpierre7a7328e2021-09-17 07:10:08 +00001525 }
Devin Jeanpierre7a7328e2021-09-17 07:10:08 +00001526 }
Marcel Hlopkoc0956cf2021-11-29 08:31:28 +00001527 } else {
Googler6a0a5252022-01-11 14:08:09 +00001528 let item = ir.item_for_type(ty)?;
1529 let type_name = cc_type_name_for_item(item)?;
1530 Ok(quote! {#const_fragment #type_name})
Devin Jeanpierre7a7328e2021-09-17 07:10:08 +00001531 }
1532}
1533
Marcel Hlopkoa0f38662021-12-03 08:45:26 +00001534fn cc_struct_layout_assertion(record: &Record, ir: &IR) -> TokenStream {
Googler6a0a5252022-01-11 14:08:09 +00001535 if !ir.is_current_target(&record.owning_target) && !ir.is_stdlib_target(&record.owning_target) {
Marcel Hlopkoa0f38662021-12-03 08:45:26 +00001536 return quote! {};
1537 }
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +00001538 let record_ident = format_cc_ident(&record.identifier.identifier);
Googler5ea88642021-09-29 08:05:59 +00001539 let size = Literal::usize_unsuffixed(record.size);
1540 let alignment = Literal::usize_unsuffixed(record.alignment);
Lukasz Anforowicz74704712021-12-22 15:30:31 +00001541 let field_assertions =
1542 record.fields.iter().filter(|f| f.access == AccessSpecifier::Public).map(|field| {
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +00001543 let field_ident = format_cc_ident(&field.identifier.identifier);
Lukasz Anforowicz74704712021-12-22 15:30:31 +00001544 let offset = Literal::usize_unsuffixed(field.offset);
1545 // The IR contains the offset in bits, while C++'s offsetof()
1546 // returns the offset in bytes, so we need to convert.
1547 quote! {
Googler972d3582022-01-11 10:17:22 +00001548 static_assert(offsetof(class #record_ident, #field_ident) * 8 == #offset);
Lukasz Anforowicz74704712021-12-22 15:30:31 +00001549 }
1550 });
Googler5ea88642021-09-29 08:05:59 +00001551 quote! {
Googler972d3582022-01-11 10:17:22 +00001552 static_assert(sizeof(class #record_ident) == #size);
1553 static_assert(alignof(class #record_ident) == #alignment);
Googler5ea88642021-09-29 08:05:59 +00001554 #( #field_assertions )*
1555 }
1556}
1557
Devin Jeanpierre58181ac2022-02-14 21:30:05 +00001558// Returns the accessor functions for no_unique_address member variables.
1559fn cc_struct_no_unique_address_impl(record: &Record, ir: &IR) -> Result<TokenStream> {
1560 let mut fields = vec![];
1561 let mut types = vec![];
1562 for field in &record.fields {
1563 if field.access != AccessSpecifier::Public || !field.is_no_unique_address {
1564 continue;
1565 }
1566 fields.push(make_rs_ident(&field.identifier.identifier));
1567 types.push(format_rs_type(&field.type_.rs_type, ir, &HashMap::new()).with_context(
1568 || format!("Failed to format type for field {:?} on record {:?}", field, record),
1569 )?)
1570 }
1571
1572 if fields.is_empty() {
1573 return Ok(quote! {});
1574 }
1575
1576 let ident = make_rs_ident(&record.identifier.identifier);
1577 Ok(quote! {
1578 impl #ident {
1579 #(
1580 pub fn #fields(&self) -> &#types {
1581 unsafe {&* (&self.#fields as *const _ as *const #types)}
1582 }
1583 )*
1584 }
1585 })
1586}
1587
Devin Jeanpierre56777022022-02-03 01:57:15 +00001588/// Returns the implementation of base class conversions, for converting a type
1589/// to its unambiguous public base classes.
1590///
1591/// TODO(b/216195042): Implement this in terms of a supporting trait which casts
1592/// raw pointers. Then, we would have blanket impls for reference, pinned mut
1593/// reference, etc. conversion. The current version is just enough to test the
1594/// logic in importer.
1595//
1596// TODO(b/216195042): Should this use, like, AsRef/AsMut (and some equivalent
1597// for Pin)?
1598fn cc_struct_upcast_impl(record: &Record, ir: &IR) -> Result<TokenStream> {
1599 let mut impls = Vec::with_capacity(record.unambiguous_public_bases.len());
1600 for base in &record.unambiguous_public_bases {
1601 let base_record: &Record = ir.find_decl(base.base_record_id)?.try_into()?;
1602 if let Some(offset) = base.offset {
1603 let offset = Literal::i64_unsuffixed(offset);
1604 // TODO(b/216195042): Correctly handle imported records, lifetimes.
1605 let base_name = make_rs_ident(&base_record.identifier.identifier);
1606 let derived_name = make_rs_ident(&record.identifier.identifier);
1607 impls.push(quote! {
1608 impl<'a> From<&'a #derived_name> for &'a #base_name {
1609 fn from(x: &'a #derived_name) -> Self {
1610 unsafe {
1611 &*((x as *const _ as *const u8).offset(#offset) as *const #base_name)
1612 }
1613 }
1614 }
1615 });
1616 } else {
1617 // TODO(b/216195042): determine offset dynamically / use a dynamic
1618 // cast. This requires a new C++ function to be
1619 // generated, so that we have something to call.
1620 }
1621 }
1622
1623 Ok(quote! {
1624 #(#impls)*
1625 })
1626}
1627
Googlera675ae02021-12-07 08:04:59 +00001628fn thunk_ident(func: &Func) -> Ident {
1629 format_ident!("__rust_thunk__{}", func.mangled_name)
Devin Jeanpierref2ec8712021-10-13 20:47:16 +00001630}
1631
Marcel Hlopko89547752021-12-10 09:39:41 +00001632fn generate_rs_api_impl(ir: &IR) -> Result<TokenStream> {
Michael Forsterbee84482021-10-13 08:35:38 +00001633 // This function uses quote! to generate C++ source code out of convenience.
1634 // This is a bold idea so we have to continously evaluate if it still makes
1635 // sense or the cost of working around differences in Rust and C++ tokens is
1636 // greather than the value added.
Marcel Hlopko3164eee2021-08-24 20:09:22 +00001637 //
Michael Forsterbee84482021-10-13 08:35:38 +00001638 // See rs_bindings_from_cc/
1639 // token_stream_printer.rs for a list of supported placeholders.
Marcel Hlopko3164eee2021-08-24 20:09:22 +00001640 let mut thunks = vec![];
Michael Forster7ef80732021-10-01 18:12:19 +00001641 for func in ir.functions() {
Lukasz Anforowiczdd9ae0f2022-02-17 15:52:53 +00001642 if can_skip_cc_thunk(func) {
Marcel Hlopko3164eee2021-08-24 20:09:22 +00001643 continue;
1644 }
1645
Googlera675ae02021-12-07 08:04:59 +00001646 let thunk_ident = thunk_ident(func);
Devin Jeanpierref2ec8712021-10-13 20:47:16 +00001647 let implementation_function = match &func.name {
Lukasz Anforowicz9c663ca2022-02-09 01:33:31 +00001648 UnqualifiedIdentifier::Operator(op) => {
1649 let name = syn::parse_str::<TokenStream>(&op.name)?;
1650 quote! { operator #name }
1651 }
Devin Jeanpierref2ec8712021-10-13 20:47:16 +00001652 UnqualifiedIdentifier::Identifier(id) => {
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +00001653 let fn_ident = format_cc_ident(&id.identifier);
Lukasz Anforowiczaab8ad22021-12-19 20:29:26 +00001654 let static_method_metadata = func
1655 .member_func_metadata
1656 .as_ref()
1657 .filter(|meta| meta.instance_method_metadata.is_none());
1658 match static_method_metadata {
1659 None => quote! {#fn_ident},
1660 Some(meta) => {
Marcel Hlopkoeaae9b72022-01-21 15:54:11 +00001661 let record_ident =
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +00001662 format_cc_ident(&meta.find_record(ir)?.identifier.identifier);
Lukasz Anforowiczaab8ad22021-12-19 20:29:26 +00001663 quote! { #record_ident :: #fn_ident }
1664 }
1665 }
Devin Jeanpierref2ec8712021-10-13 20:47:16 +00001666 }
Lukasz Anforowicz7b0042d2022-01-06 23:00:19 +00001667 // Use `destroy_at` to avoid needing to spell out the class name. Destructor identiifers
Devin Jeanpierrecc6cf092021-12-16 04:31:14 +00001668 // use the name of the type itself, without namespace qualification, template
1669 // parameters, or aliases. We do not need to use that naming scheme anywhere else in
1670 // the bindings, and it can be difficult (impossible?) to spell in the general case. By
1671 // using destroy_at, we avoid needing to determine or remember what the correct spelling
Lukasz Anforowicz7b0042d2022-01-06 23:00:19 +00001672 // is. Similar arguments apply to `construct_at`.
Lukasz Anforowicze643ec92021-12-22 15:45:15 +00001673 UnqualifiedIdentifier::Constructor => {
Lukasz Anforowicz7b0042d2022-01-06 23:00:19 +00001674 quote! { rs_api_impl_support::construct_at }
Lukasz Anforowicze643ec92021-12-22 15:45:15 +00001675 }
Devin Jeanpierref2ec8712021-10-13 20:47:16 +00001676 UnqualifiedIdentifier::Destructor => quote! {std::destroy_at},
Devin Jeanpierref2ec8712021-10-13 20:47:16 +00001677 };
Marcel Hlopkoc0956cf2021-11-29 08:31:28 +00001678 let return_type_name = format_cc_type(&func.return_type.cc_type, ir)?;
Lukasz Anforowicze643ec92021-12-22 15:45:15 +00001679 let return_stmt = if func.return_type.cc_type.is_void() {
1680 quote! {}
1681 } else {
1682 quote! { return }
1683 };
Marcel Hlopko3164eee2021-08-24 20:09:22 +00001684
1685 let param_idents =
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +00001686 func.params.iter().map(|p| format_cc_ident(&p.identifier.identifier)).collect_vec();
Marcel Hlopko3164eee2021-08-24 20:09:22 +00001687
Devin Jeanpierre09c6f452021-09-29 07:34:24 +00001688 let param_types = func
1689 .params
1690 .iter()
Marcel Hlopkoc0956cf2021-11-29 08:31:28 +00001691 .map(|p| format_cc_type(&p.type_.cc_type, ir))
Devin Jeanpierre09c6f452021-09-29 07:34:24 +00001692 .collect::<Result<Vec<_>>>()?;
Marcel Hlopko3164eee2021-08-24 20:09:22 +00001693
Lukasz Anforowiczb3d89aa2022-01-12 14:35:52 +00001694 let needs_this_deref = match &func.member_func_metadata {
1695 None => false,
1696 Some(meta) => match &func.name {
1697 UnqualifiedIdentifier::Constructor | UnqualifiedIdentifier::Destructor => false,
Marcel Hlopko14ee3c82022-02-09 09:46:23 +00001698 UnqualifiedIdentifier::Identifier(_) | UnqualifiedIdentifier::Operator(_) => {
1699 meta.instance_method_metadata.is_some()
1700 }
Lukasz Anforowiczb3d89aa2022-01-12 14:35:52 +00001701 },
1702 };
1703 let (implementation_function, arg_expressions) = if !needs_this_deref {
1704 (implementation_function, param_idents.clone())
1705 } else {
1706 let this_param = func
1707 .params
1708 .first()
1709 .ok_or_else(|| anyhow!("Instance methods must have `__this` param."))?;
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +00001710 let this_arg = format_cc_ident(&this_param.identifier.identifier);
Lukasz Anforowiczb3d89aa2022-01-12 14:35:52 +00001711 (
1712 quote! { #this_arg -> #implementation_function},
1713 param_idents.iter().skip(1).cloned().collect_vec(),
1714 )
1715 };
1716
Marcel Hlopko3164eee2021-08-24 20:09:22 +00001717 thunks.push(quote! {
1718 extern "C" #return_type_name #thunk_ident( #( #param_types #param_idents ),* ) {
Lukasz Anforowiczb3d89aa2022-01-12 14:35:52 +00001719 #return_stmt #implementation_function( #( #arg_expressions ),* );
Marcel Hlopko3164eee2021-08-24 20:09:22 +00001720 }
1721 });
1722 }
1723
Marcel Hlopkoa0f38662021-12-03 08:45:26 +00001724 let layout_assertions = ir.records().map(|record| cc_struct_layout_assertion(record, ir));
Googler5ea88642021-09-29 08:05:59 +00001725
Devin Jeanpierre231ef8d2021-10-27 10:50:44 +00001726 let mut standard_headers = <BTreeSet<Ident>>::new();
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +00001727 standard_headers.insert(format_ident!("memory")); // ubiquitous.
Devin Jeanpierre231ef8d2021-10-27 10:50:44 +00001728 if ir.records().next().is_some() {
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +00001729 standard_headers.insert(format_ident!("cstddef"));
Devin Jeanpierre231ef8d2021-10-27 10:50:44 +00001730 };
Googler5ea88642021-09-29 08:05:59 +00001731
Lukasz Anforowicz4457baf2021-12-23 17:24:04 +00001732 let mut includes =
1733 vec!["rs_bindings_from_cc/support/cxx20_backports.h"];
1734
Michael Forsterbee84482021-10-13 08:35:38 +00001735 // In order to generate C++ thunk in all the cases Clang needs to be able to
1736 // access declarations from public headers of the C++ library.
Lukasz Anforowicz4457baf2021-12-23 17:24:04 +00001737 includes.extend(ir.used_headers().map(|i| &i.name as &str));
Marcel Hlopko3164eee2021-08-24 20:09:22 +00001738
Marcel Hlopko89547752021-12-10 09:39:41 +00001739 Ok(quote! {
Googler5ea88642021-09-29 08:05:59 +00001740 #( __HASH_TOKEN__ include <#standard_headers> __NEWLINE__)*
Devin Jeanpierre7c74f842022-02-03 07:08:06 +00001741 __NEWLINE__
Michael Forsterdb8101a2021-10-08 06:56:03 +00001742 #( __HASH_TOKEN__ include #includes __NEWLINE__)* __NEWLINE__
Marcel Hlopkob8069ae2022-02-19 09:31:00 +00001743 __HASH_TOKEN__ pragma clang diagnostic push __NEWLINE__
1744 // Disable Clang thread-safety-analysis warnings that would otherwise
1745 // complain about thunks that call mutex locking functions in an unpaired way.
1746 __HASH_TOKEN__ pragma clang diagnostic ignored "-Wthread-safety-analysis" __NEWLINE__
Marcel Hlopko3164eee2021-08-24 20:09:22 +00001747
Michael Forsterdb8101a2021-10-08 06:56:03 +00001748 #( #thunks )* __NEWLINE__ __NEWLINE__
Googler5ea88642021-09-29 08:05:59 +00001749
Michael Forsterdb8101a2021-10-08 06:56:03 +00001750 #( #layout_assertions __NEWLINE__ __NEWLINE__ )*
Marcel Hlopkoc6b726c2021-10-07 06:53:09 +00001751
Marcel Hlopkob8069ae2022-02-19 09:31:00 +00001752 __NEWLINE__
1753 __HASH_TOKEN__ pragma clang diagnostic pop __NEWLINE__
Marcel Hlopkoc6b726c2021-10-07 06:53:09 +00001754 // To satisfy http://cs/symbol:devtools.metadata.Presubmit.CheckTerminatingNewline check.
1755 __NEWLINE__
Marcel Hlopko89547752021-12-10 09:39:41 +00001756 })
Marcel Hlopko3164eee2021-08-24 20:09:22 +00001757}
1758
Marcel Hlopko42abfc82021-08-09 07:03:17 +00001759#[cfg(test)]
1760mod tests {
Devin Jeanpierre45cb1162021-10-27 10:54:28 +00001761 use super::*;
Michael Forstered642022021-10-04 09:48:25 +00001762 use anyhow::anyhow;
Lukasz Anforowicz9c663ca2022-02-09 01:33:31 +00001763 use ir_testing::{ir_from_cc, ir_from_cc_dependency, ir_func, ir_record, retrieve_func};
Marcel Hlopko89547752021-12-10 09:39:41 +00001764 use token_stream_matchers::{
1765 assert_cc_matches, assert_cc_not_matches, assert_rs_matches, assert_rs_not_matches,
Devin Jeanpierre7e9a1de2021-12-03 08:04:22 +00001766 };
Michael Forsterdb8101a2021-10-08 06:56:03 +00001767 use token_stream_printer::tokens_to_string;
Marcel Hlopko42abfc82021-08-09 07:03:17 +00001768
1769 #[test]
Marcel Hlopkob8069ae2022-02-19 09:31:00 +00001770 fn test_disable_thread_safety_warnings() -> Result<()> {
1771 let ir = ir_from_cc("inline void foo() {}")?;
1772 let rs_api_impl = generate_rs_api_impl(&ir)?;
1773 assert_cc_matches!(
1774 rs_api_impl,
1775 quote! {
1776 ...
1777 __HASH_TOKEN__ pragma clang diagnostic push
1778 __HASH_TOKEN__ pragma clang diagnostic ignored "-Wthread-safety-analysis"
1779 ...
1780
1781 __HASH_TOKEN__ pragma clang diagnostic pop
1782 ...
1783 }
1784 );
1785 Ok(())
1786 }
1787
1788 #[test]
Marcel Hlopko3b9bf9e2021-11-29 08:25:14 +00001789 // TODO(hlopko): Move this test to a more principled place where it can access
1790 // `ir_testing`.
1791 fn test_duplicate_decl_ids_err() {
1792 let mut r1 = ir_record("R1");
Marcel Hlopko264b9ad2021-12-02 21:06:44 +00001793 r1.id = DeclId(42);
Marcel Hlopko3b9bf9e2021-11-29 08:25:14 +00001794 let mut r2 = ir_record("R2");
Marcel Hlopko264b9ad2021-12-02 21:06:44 +00001795 r2.id = DeclId(42);
Marcel Hlopko3b9bf9e2021-11-29 08:25:14 +00001796 let result = make_ir_from_items([r1.into(), r2.into()]);
1797 assert!(result.is_err());
1798 assert!(result.unwrap_err().to_string().contains("Duplicate decl_id found in"));
1799 }
1800
1801 #[test]
Marcel Hlopko45fba972021-08-23 19:52:20 +00001802 fn test_simple_function() -> Result<()> {
Marcel Hlopko89547752021-12-10 09:39:41 +00001803 let ir = ir_from_cc("int Add(int a, int b);")?;
1804 let rs_api = generate_rs_api(&ir)?;
1805 assert_rs_matches!(
1806 rs_api,
1807 quote! {
Michael Forsterdb8101a2021-10-08 06:56:03 +00001808 #[inline(always)]
Marcel Hlopko89547752021-12-10 09:39:41 +00001809 pub fn Add(a: i32, b: i32) -> i32 {
Googlera675ae02021-12-07 08:04:59 +00001810 unsafe { crate::detail::__rust_thunk___Z3Addii(a, b) }
Marcel Hlopko89547752021-12-10 09:39:41 +00001811 }
1812 }
1813 );
1814 assert_rs_matches!(
1815 rs_api,
1816 quote! {
Michael Forsterdb8101a2021-10-08 06:56:03 +00001817 mod detail {
Googler55647142022-01-11 12:37:39 +00001818 #[allow(unused_imports)]
Devin Jeanpierred4dde0e2021-10-13 20:48:25 +00001819 use super::*;
Michael Forsterdb8101a2021-10-08 06:56:03 +00001820 extern "C" {
1821 #[link_name = "_Z3Addii"]
Googlera675ae02021-12-07 08:04:59 +00001822 pub(crate) fn __rust_thunk___Z3Addii(a: i32, b: i32) -> i32;
Marcel Hlopko89547752021-12-10 09:39:41 +00001823 }
1824 }
1825 }
Marcel Hlopko42abfc82021-08-09 07:03:17 +00001826 );
Michael Forsterdb8101a2021-10-08 06:56:03 +00001827
Marcel Hlopko89547752021-12-10 09:39:41 +00001828 assert_cc_not_matches!(generate_rs_api_impl(&ir)?, quote! {__rust_thunk___Z3Addii});
Michael Forsterdb8101a2021-10-08 06:56:03 +00001829
Marcel Hlopko3164eee2021-08-24 20:09:22 +00001830 Ok(())
1831 }
1832
1833 #[test]
1834 fn test_inline_function() -> Result<()> {
Marcel Hlopko89547752021-12-10 09:39:41 +00001835 let ir = ir_from_cc("inline int Add(int a, int b);")?;
1836 let rs_api = generate_rs_api(&ir)?;
1837 assert_rs_matches!(
1838 rs_api,
1839 quote! {
Michael Forsterdb8101a2021-10-08 06:56:03 +00001840 #[inline(always)]
Marcel Hlopko89547752021-12-10 09:39:41 +00001841 pub fn Add(a: i32, b: i32) -> i32 {
Googlera675ae02021-12-07 08:04:59 +00001842 unsafe { crate::detail::__rust_thunk___Z3Addii(a, b) }
Marcel Hlopko89547752021-12-10 09:39:41 +00001843 }
1844 }
1845 );
1846 assert_rs_matches!(
1847 rs_api,
1848 quote! {
Michael Forsterdb8101a2021-10-08 06:56:03 +00001849 mod detail {
Googler55647142022-01-11 12:37:39 +00001850 #[allow(unused_imports)]
Devin Jeanpierred4dde0e2021-10-13 20:48:25 +00001851 use super::*;
Michael Forsterdb8101a2021-10-08 06:56:03 +00001852 extern "C" {
Googlera675ae02021-12-07 08:04:59 +00001853 pub(crate) fn __rust_thunk___Z3Addii(a: i32, b: i32) -> i32;
Marcel Hlopko89547752021-12-10 09:39:41 +00001854 }
1855 }
1856 }
Marcel Hlopko3164eee2021-08-24 20:09:22 +00001857 );
1858
Marcel Hlopko89547752021-12-10 09:39:41 +00001859 assert_cc_matches!(
1860 generate_rs_api_impl(&ir)?,
1861 quote! {
Googlera675ae02021-12-07 08:04:59 +00001862 extern "C" int __rust_thunk___Z3Addii(int a, int b) {
Marcel Hlopko89547752021-12-10 09:39:41 +00001863 return Add(a, b);
Marcel Hlopko3164eee2021-08-24 20:09:22 +00001864 }
Marcel Hlopko89547752021-12-10 09:39:41 +00001865 }
Marcel Hlopko3164eee2021-08-24 20:09:22 +00001866 );
Marcel Hlopko42abfc82021-08-09 07:03:17 +00001867 Ok(())
1868 }
Marcel Hlopkob4b28742021-09-15 12:45:20 +00001869
1870 #[test]
Marcel Hlopkoa0f38662021-12-03 08:45:26 +00001871 fn test_simple_function_with_types_from_other_target() -> Result<()> {
1872 let ir = ir_from_cc_dependency(
1873 "inline ReturnStruct DoSomething(ParamStruct param);",
1874 "struct ReturnStruct {}; struct ParamStruct {};",
1875 )?;
1876
Marcel Hlopko89547752021-12-10 09:39:41 +00001877 let rs_api = generate_rs_api(&ir)?;
1878 assert_rs_matches!(
1879 rs_api,
1880 quote! {
Marcel Hlopkoa0f38662021-12-03 08:45:26 +00001881 #[inline(always)]
1882 pub fn DoSomething(param: dependency::ParamStruct)
Marcel Hlopko89547752021-12-10 09:39:41 +00001883 -> dependency::ReturnStruct {
Googlera675ae02021-12-07 08:04:59 +00001884 unsafe { crate::detail::__rust_thunk___Z11DoSomething11ParamStruct(param) }
Marcel Hlopko89547752021-12-10 09:39:41 +00001885 }
1886 }
1887 );
1888 assert_rs_matches!(
1889 rs_api,
1890 quote! {
1891 mod detail {
Googler55647142022-01-11 12:37:39 +00001892 #[allow(unused_imports)]
Marcel Hlopko89547752021-12-10 09:39:41 +00001893 use super::*;
1894 extern "C" {
1895 pub(crate) fn __rust_thunk___Z11DoSomething11ParamStruct(param: dependency::ParamStruct)
1896 -> dependency::ReturnStruct;
1897 }
1898 }}
Marcel Hlopkoa0f38662021-12-03 08:45:26 +00001899 );
1900
Marcel Hlopko89547752021-12-10 09:39:41 +00001901 assert_cc_matches!(
1902 generate_rs_api_impl(&ir)?,
1903 quote! {
Googler972d3582022-01-11 10:17:22 +00001904 extern "C" class ReturnStruct __rust_thunk___Z11DoSomething11ParamStruct(class ParamStruct param) {
Marcel Hlopkoa0f38662021-12-03 08:45:26 +00001905 return DoSomething(param);
1906 }
Marcel Hlopko89547752021-12-10 09:39:41 +00001907 }
Marcel Hlopkoa0f38662021-12-03 08:45:26 +00001908 );
1909 Ok(())
1910 }
1911
1912 #[test]
Marcel Hlopkob4b28742021-09-15 12:45:20 +00001913 fn test_simple_struct() -> Result<()> {
Marcel Hlopko89547752021-12-10 09:39:41 +00001914 let ir = ir_from_cc(&tokens_to_string(quote! {
Devin Jeanpierre88343c72022-01-15 01:10:23 +00001915 struct SomeStruct final {
Marcel Hlopko89547752021-12-10 09:39:41 +00001916 int public_int;
1917 protected:
1918 int protected_int;
1919 private:
1920 int private_int;
1921 };
1922 })?)?;
Michael Forster028800b2021-10-05 12:39:59 +00001923
Marcel Hlopko89547752021-12-10 09:39:41 +00001924 let rs_api = generate_rs_api(&ir)?;
1925 assert_rs_matches!(
1926 rs_api,
1927 quote! {
Michael Forsterdb8101a2021-10-08 06:56:03 +00001928 #[derive(Clone, Copy)]
1929 #[repr(C)]
1930 pub struct SomeStruct {
1931 pub public_int: i32,
1932 protected_int: i32,
1933 private_int: i32,
Marcel Hlopko89547752021-12-10 09:39:41 +00001934 }
1935 }
1936 );
1937 assert_rs_matches!(
1938 rs_api,
1939 quote! {
Googler454f2652021-12-06 12:53:12 +00001940 const _: () = assert!(std::mem::size_of::<Option<&i32>>() == std::mem::size_of::<&i32>());
Googler209b10a2021-12-06 09:11:57 +00001941 const _: () = assert!(std::mem::size_of::<SomeStruct>() == 12usize);
1942 const _: () = assert!(std::mem::align_of::<SomeStruct>() == 4usize);
1943 const _: () = assert!(offset_of!(SomeStruct, public_int) * 8 == 0usize);
1944 const _: () = assert!(offset_of!(SomeStruct, protected_int) * 8 == 32usize);
1945 const _: () = assert!(offset_of!(SomeStruct, private_int) * 8 == 64usize);
Marcel Hlopko89547752021-12-10 09:39:41 +00001946 }
Marcel Hlopkob4b28742021-09-15 12:45:20 +00001947 );
Marcel Hlopko89547752021-12-10 09:39:41 +00001948 let rs_api_impl = generate_rs_api_impl(&ir)?;
1949 assert_cc_matches!(
1950 rs_api_impl,
1951 quote! {
Googler972d3582022-01-11 10:17:22 +00001952 extern "C" void __rust_thunk___ZN10SomeStructD1Ev(class SomeStruct * __this) {
Lukasz Anforowicze643ec92021-12-22 15:45:15 +00001953 std :: destroy_at (__this) ;
Marcel Hlopko89547752021-12-10 09:39:41 +00001954 }
1955 }
1956 );
1957 assert_cc_matches!(
1958 rs_api_impl,
1959 quote! {
Googler972d3582022-01-11 10:17:22 +00001960 static_assert(sizeof(class SomeStruct) == 12);
1961 static_assert(alignof(class SomeStruct) == 4);
1962 static_assert(offsetof(class SomeStruct, public_int) * 8 == 0);
Marcel Hlopko89547752021-12-10 09:39:41 +00001963 }
Googler5ea88642021-09-29 08:05:59 +00001964 );
Marcel Hlopkob4b28742021-09-15 12:45:20 +00001965 Ok(())
1966 }
Devin Jeanpierre7a7328e2021-09-17 07:10:08 +00001967
Devin Jeanpierre2ed14ec2021-10-06 11:32:19 +00001968 #[test]
Lukasz Anforowicz275fa922022-01-05 16:13:20 +00001969 fn test_ref_to_struct_in_thunk_impls() -> Result<()> {
Googler972d3582022-01-11 10:17:22 +00001970 let ir = ir_from_cc("struct S{}; inline void foo(class S& s) {} ")?;
Lukasz Anforowicz275fa922022-01-05 16:13:20 +00001971 let rs_api_impl = generate_rs_api_impl(&ir)?;
1972 assert_cc_matches!(
1973 rs_api_impl,
1974 quote! {
Googler972d3582022-01-11 10:17:22 +00001975 extern "C" void __rust_thunk___Z3fooR1S(class S& s) {
Lukasz Anforowicz275fa922022-01-05 16:13:20 +00001976 foo(s);
1977 }
1978 }
1979 );
1980 Ok(())
1981 }
1982
1983 #[test]
1984 fn test_const_ref_to_struct_in_thunk_impls() -> Result<()> {
Googler972d3582022-01-11 10:17:22 +00001985 let ir = ir_from_cc("struct S{}; inline void foo(const class S& s) {} ")?;
Lukasz Anforowicz275fa922022-01-05 16:13:20 +00001986 let rs_api_impl = generate_rs_api_impl(&ir)?;
1987 assert_cc_matches!(
1988 rs_api_impl,
1989 quote! {
Googler972d3582022-01-11 10:17:22 +00001990 extern "C" void __rust_thunk___Z3fooRK1S(const class S& s) {
Lukasz Anforowicz275fa922022-01-05 16:13:20 +00001991 foo(s);
1992 }
1993 }
1994 );
1995 Ok(())
1996 }
1997
1998 #[test]
Lukasz Anforowicz957cbf22022-01-05 16:14:05 +00001999 fn test_unsigned_int_in_thunk_impls() -> Result<()> {
2000 let ir = ir_from_cc("inline void foo(unsigned int i) {} ")?;
2001 let rs_api_impl = generate_rs_api_impl(&ir)?;
2002 assert_cc_matches!(
2003 rs_api_impl,
2004 quote! {
2005 extern "C" void __rust_thunk___Z3fooj(unsigned int i) {
2006 foo(i);
2007 }
2008 }
2009 );
2010 Ok(())
2011 }
2012
2013 #[test]
Marcel Hlopkodd1fcb12021-12-22 14:13:59 +00002014 fn test_record_static_methods_qualify_call_in_thunk() -> Result<()> {
2015 let ir = ir_from_cc(&tokens_to_string(quote! {
2016 struct SomeStruct {
2017 static inline int some_func() { return 42; }
2018 };
2019 })?)?;
2020
2021 assert_cc_matches!(
2022 generate_rs_api_impl(&ir)?,
2023 quote! {
2024 extern "C" int __rust_thunk___ZN10SomeStruct9some_funcEv() {
2025 return SomeStruct::some_func();
2026 }
2027 }
2028 );
2029 Ok(())
2030 }
2031
2032 #[test]
Lukasz Anforowiczb3d89aa2022-01-12 14:35:52 +00002033 fn test_record_instance_methods_deref_this_in_thunk() -> Result<()> {
2034 let ir = ir_from_cc(&tokens_to_string(quote! {
2035 struct SomeStruct {
2036 inline int some_func(int arg) const { return 42 + arg; }
2037 };
2038 })?)?;
2039
2040 assert_cc_matches!(
2041 generate_rs_api_impl(&ir)?,
2042 quote! {
2043 extern "C" int __rust_thunk___ZNK10SomeStruct9some_funcEi(
2044 const class SomeStruct* __this, int arg) {
2045 return __this->some_func(arg);
2046 }
2047 }
2048 );
2049 Ok(())
2050 }
2051
2052 #[test]
Marcel Hlopkoa0f38662021-12-03 08:45:26 +00002053 fn test_struct_from_other_target() -> Result<()> {
2054 let ir = ir_from_cc_dependency("// intentionally empty", "struct SomeStruct {};")?;
Marcel Hlopko89547752021-12-10 09:39:41 +00002055 assert_rs_not_matches!(generate_rs_api(&ir)?, quote! { SomeStruct });
2056 assert_cc_not_matches!(generate_rs_api_impl(&ir)?, quote! { SomeStruct });
Marcel Hlopkoa0f38662021-12-03 08:45:26 +00002057 Ok(())
2058 }
2059
2060 #[test]
Devin Jeanpierre2ed14ec2021-10-06 11:32:19 +00002061 fn test_copy_derives() {
Devin Jeanpierreccfefc82021-10-27 10:54:00 +00002062 let record = ir_record("S");
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00002063 assert_eq!(generate_derives(&record), &["Clone", "Copy"]);
Devin Jeanpierre2ed14ec2021-10-06 11:32:19 +00002064 }
2065
2066 #[test]
2067 fn test_copy_derives_not_is_trivial_abi() {
Devin Jeanpierreccfefc82021-10-27 10:54:00 +00002068 let mut record = ir_record("S");
Devin Jeanpierre2ed14ec2021-10-06 11:32:19 +00002069 record.is_trivial_abi = false;
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00002070 assert_eq!(generate_derives(&record), &[""; 0]);
Devin Jeanpierre2ed14ec2021-10-06 11:32:19 +00002071 }
2072
Devin Jeanpierre88343c72022-01-15 01:10:23 +00002073 /// Even if it's trivially relocatable, !Unpin C++ type cannot be
2074 /// cloned/copied or otherwise used by value, because values would allow
2075 /// assignment into the Pin.
2076 ///
2077 /// All !Unpin C++ types, not just non trivially relocatable ones, are
2078 /// unsafe to assign in the Rust sense.
Devin Jeanpierree6e16652021-12-22 15:54:46 +00002079 #[test]
Devin Jeanpierre88343c72022-01-15 01:10:23 +00002080 fn test_copy_derives_not_final() {
Devin Jeanpierree6e16652021-12-22 15:54:46 +00002081 let mut record = ir_record("S");
2082 record.is_final = false;
Devin Jeanpierre88343c72022-01-15 01:10:23 +00002083 assert_eq!(generate_derives(&record), &[""; 0]);
Devin Jeanpierree6e16652021-12-22 15:54:46 +00002084 }
2085
Devin Jeanpierre2ed14ec2021-10-06 11:32:19 +00002086 #[test]
2087 fn test_copy_derives_ctor_nonpublic() {
Devin Jeanpierreccfefc82021-10-27 10:54:00 +00002088 let mut record = ir_record("S");
Devin Jeanpierre2ed14ec2021-10-06 11:32:19 +00002089 for access in [ir::AccessSpecifier::Protected, ir::AccessSpecifier::Private] {
2090 record.copy_constructor.access = access;
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00002091 assert_eq!(generate_derives(&record), &[""; 0]);
Devin Jeanpierre2ed14ec2021-10-06 11:32:19 +00002092 }
2093 }
2094
2095 #[test]
2096 fn test_copy_derives_ctor_deleted() {
Devin Jeanpierreccfefc82021-10-27 10:54:00 +00002097 let mut record = ir_record("S");
Devin Jeanpierre2ed14ec2021-10-06 11:32:19 +00002098 record.copy_constructor.definition = ir::SpecialMemberDefinition::Deleted;
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00002099 assert_eq!(generate_derives(&record), &[""; 0]);
Devin Jeanpierre2ed14ec2021-10-06 11:32:19 +00002100 }
2101
2102 #[test]
Devin Jeanpierrebe2f33b2021-10-21 12:54:19 +00002103 fn test_copy_derives_ctor_nontrivial_members() {
Devin Jeanpierreccfefc82021-10-27 10:54:00 +00002104 let mut record = ir_record("S");
Devin Jeanpierrebe2f33b2021-10-21 12:54:19 +00002105 record.copy_constructor.definition = ir::SpecialMemberDefinition::NontrivialMembers;
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00002106 assert_eq!(generate_derives(&record), &[""; 0]);
Devin Jeanpierrebe2f33b2021-10-21 12:54:19 +00002107 }
2108
2109 #[test]
2110 fn test_copy_derives_ctor_nontrivial_self() {
Devin Jeanpierreccfefc82021-10-27 10:54:00 +00002111 let mut record = ir_record("S");
Devin Jeanpierre7b62e952021-12-08 21:43:30 +00002112 record.copy_constructor.definition = ir::SpecialMemberDefinition::NontrivialUserDefined;
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00002113 assert_eq!(generate_derives(&record), &[""; 0]);
Devin Jeanpierre2ed14ec2021-10-06 11:32:19 +00002114 }
2115
Devin Jeanpierre7a7328e2021-09-17 07:10:08 +00002116 #[test]
2117 fn test_ptr_func() -> Result<()> {
Marcel Hlopko89547752021-12-10 09:39:41 +00002118 let ir = ir_from_cc(&tokens_to_string(quote! {
2119 inline int* Deref(int*const* p);
2120 })?)?;
Devin Jeanpierred6da7002021-10-21 12:55:20 +00002121
Marcel Hlopko89547752021-12-10 09:39:41 +00002122 let rs_api = generate_rs_api(&ir)?;
2123 assert_rs_matches!(
2124 rs_api,
2125 quote! {
Michael Forsterdb8101a2021-10-08 06:56:03 +00002126 #[inline(always)]
Lukasz Anforowiczf7bdd392022-01-21 00:33:39 +00002127 pub unsafe fn Deref(p: *const *mut i32) -> *mut i32 {
2128 crate::detail::__rust_thunk___Z5DerefPKPi(p)
Marcel Hlopko89547752021-12-10 09:39:41 +00002129 }
2130 }
2131 );
2132 assert_rs_matches!(
2133 rs_api,
2134 quote! {
Michael Forsterdb8101a2021-10-08 06:56:03 +00002135 mod detail {
Googler55647142022-01-11 12:37:39 +00002136 #[allow(unused_imports)]
Devin Jeanpierred4dde0e2021-10-13 20:48:25 +00002137 use super::*;
Michael Forsterdb8101a2021-10-08 06:56:03 +00002138 extern "C" {
Googlera675ae02021-12-07 08:04:59 +00002139 pub(crate) fn __rust_thunk___Z5DerefPKPi(p: *const *mut i32) -> *mut i32;
Marcel Hlopko89547752021-12-10 09:39:41 +00002140 }
2141 }
2142 }
Devin Jeanpierre7a7328e2021-09-17 07:10:08 +00002143 );
Devin Jeanpierre184f9ac2021-09-17 13:47:03 +00002144
Marcel Hlopko89547752021-12-10 09:39:41 +00002145 assert_cc_matches!(
2146 generate_rs_api_impl(&ir)?,
2147 quote! {
Googlera675ae02021-12-07 08:04:59 +00002148 extern "C" int* __rust_thunk___Z5DerefPKPi(int* const * p) {
Devin Jeanpierre184f9ac2021-09-17 13:47:03 +00002149 return Deref(p);
2150 }
Marcel Hlopko89547752021-12-10 09:39:41 +00002151 }
Devin Jeanpierre184f9ac2021-09-17 13:47:03 +00002152 );
Devin Jeanpierre7a7328e2021-09-17 07:10:08 +00002153 Ok(())
2154 }
Michael Forstered642022021-10-04 09:48:25 +00002155
2156 #[test]
Googlerdb111532022-01-05 06:12:13 +00002157 fn test_const_char_ptr_func() -> Result<()> {
2158 // This is a regression test: We used to include the "const" in the name
2159 // of the CcType, which caused a panic in the code generator
2160 // ('"const char" is not a valid Ident').
2161 // It's therefore important that f() is inline so that we need to
2162 // generate a thunk for it (where we then process the CcType).
2163 let ir = ir_from_cc(&tokens_to_string(quote! {
2164 inline void f(const char *str);
2165 })?)?;
2166
2167 let rs_api = generate_rs_api(&ir)?;
2168 assert_rs_matches!(
2169 rs_api,
2170 quote! {
2171 #[inline(always)]
Lukasz Anforowiczf7bdd392022-01-21 00:33:39 +00002172 pub unsafe fn f(str: *const i8) {
2173 crate::detail::__rust_thunk___Z1fPKc(str)
Googlerdb111532022-01-05 06:12:13 +00002174 }
2175 }
2176 );
2177 assert_rs_matches!(
2178 rs_api,
2179 quote! {
2180 extern "C" {
2181 pub(crate) fn __rust_thunk___Z1fPKc(str: *const i8);
2182 }
2183 }
2184 );
2185
2186 assert_cc_matches!(
2187 generate_rs_api_impl(&ir)?,
2188 quote! {
2189 extern "C" void __rust_thunk___Z1fPKc(char const * str){ f(str) ; }
2190 }
2191 );
2192 Ok(())
2193 }
2194
2195 #[test]
Lukasz Anforowiczcf230fd2022-02-18 19:20:39 +00002196 fn test_func_ptr_where_params_are_primitive_types() -> Result<()> {
2197 let ir = ir_from_cc(r#" int (*get_ptr_to_func())(float, double); "#)?;
2198 let rs_api = generate_rs_api(&ir)?;
2199 let rs_api_impl = generate_rs_api_impl(&ir)?;
2200 assert_rs_matches!(
2201 rs_api,
2202 quote! {
2203 #[inline(always)]
2204 pub fn get_ptr_to_func() -> Option<extern "C" fn (f32, f64) -> i32> {
2205 unsafe { crate::detail::__rust_thunk___Z15get_ptr_to_funcv() }
2206 }
2207 }
2208 );
2209 assert_rs_matches!(
2210 rs_api,
2211 quote! {
2212 mod detail {
2213 #[allow(unused_imports)]
2214 use super::*;
2215 extern "C" {
2216 #[link_name = "_Z15get_ptr_to_funcv"]
2217 pub(crate) fn __rust_thunk___Z15get_ptr_to_funcv()
2218 -> Option<extern "C" fn(f32, f64) -> i32>;
2219 }
2220 }
2221 }
2222 );
2223 // Verify that no C++ thunk got generated.
2224 assert_cc_not_matches!(rs_api_impl, quote! { __rust_thunk___Z15get_ptr_to_funcv });
2225
2226 // TODO(b/217419782): Add another test for more exotic calling conventions /
2227 // abis.
2228
2229 // TODO(b/217419782): Add another test for pointer to a function that
2230 // takes/returns non-trivially-movable types by value. See also
2231 // <internal link>
2232
2233 Ok(())
2234 }
2235
2236 #[test]
2237 fn test_func_ptr_with_non_static_lifetime() -> Result<()> {
2238 let ir = ir_from_cc(
2239 r#"
Googler53f65942022-02-23 11:23:30 +00002240 [[clang::annotate("lifetimes", "-> a")]]
Lukasz Anforowiczcf230fd2022-02-18 19:20:39 +00002241 int (*get_ptr_to_func())(float, double); "#,
2242 )?;
2243 let rs_api = generate_rs_api(&ir)?;
2244 assert_rs_matches!(
2245 rs_api,
2246 quote! {
2247 // Error while generating bindings for item 'get_ptr_to_func':
2248 // Return type is not supported: Function pointers with non-'static lifetimes are not supported: int (*)(float, double)
2249 }
2250 );
2251 Ok(())
2252 }
2253
2254 #[test]
2255 fn test_func_ptr_where_params_are_raw_ptrs() -> Result<()> {
2256 let ir = ir_from_cc(r#" const int* (*get_ptr_to_func())(const int*); "#)?;
2257 let rs_api = generate_rs_api(&ir)?;
2258 let rs_api_impl = generate_rs_api_impl(&ir)?;
2259 assert_rs_matches!(
2260 rs_api,
2261 quote! {
2262 #[inline(always)]
2263 pub fn get_ptr_to_func() -> Option<extern "C" fn (*const i32) -> *const i32> {
2264 unsafe { crate::detail::__rust_thunk___Z15get_ptr_to_funcv() }
2265 }
2266 }
2267 );
2268 assert_rs_matches!(
2269 rs_api,
2270 quote! {
2271 mod detail {
2272 #[allow(unused_imports)]
2273 use super::*;
2274 extern "C" {
2275 #[link_name = "_Z15get_ptr_to_funcv"]
2276 pub(crate) fn __rust_thunk___Z15get_ptr_to_funcv()
2277 -> Option<extern "C" fn(*const i32) -> *const i32>;
2278 }
2279 }
2280 }
2281 );
2282 // Verify that no C++ thunk got generated.
2283 assert_cc_not_matches!(rs_api_impl, quote! { __rust_thunk___Z15get_ptr_to_funcv });
2284
2285 // TODO(b/217419782): Add another test where params (and the return
2286 // type) are references with lifetimes. Something like this:
2287 // #pragma clang lifetime_elision
2288 // const int& (*get_ptr_to_func())(const int&, const int&); "#)?;
2289 // 1) Need to investigate why this fails - seeing raw pointers in Rust
2290 // seems to indicate that no lifetimes are present at the `importer.cc`
2291 // level. Maybe lifetime elision doesn't support this scenario? Unclear
Googler53f65942022-02-23 11:23:30 +00002292 // how to explicitly apply [[clang::annotate("lifetimes", "a, b -> a")]]
Lukasz Anforowiczcf230fd2022-02-18 19:20:39 +00002293 // to the _inner_ function.
2294 // 2) It is important to have 2 reference parameters, so see if the problem
2295 // of passing `lifetimes` by value would have been caught - see:
2296 // cl/428079010/depot/rs_bindings_from_cc/
2297 // importer.cc?version=s6#823
2298
2299 // TODO(b/217419782): Decide what to do if the C++ pointer is *not*
2300 // annotated with a lifetime - emit `unsafe fn(...) -> ...` in that
2301 // case?
2302
2303 Ok(())
2304 }
2305
2306 #[test]
Michael Forstered642022021-10-04 09:48:25 +00002307 fn test_item_order() -> Result<()> {
Marcel Hlopko89547752021-12-10 09:39:41 +00002308 let ir = ir_from_cc(
2309 "int first_func();
2310 struct FirstStruct {};
2311 int second_func();
2312 struct SecondStruct {};",
2313 )?;
Michael Forstered642022021-10-04 09:48:25 +00002314
Marcel Hlopko89547752021-12-10 09:39:41 +00002315 let rs_api = rs_tokens_to_formatted_string(generate_rs_api(&ir)?)?;
2316
Lukasz Anforowiczdd9ae0f2022-02-17 15:52:53 +00002317 let idx = |s: &str| rs_api.find(s).ok_or_else(|| anyhow!("'{}' missing", s));
Michael Forstered642022021-10-04 09:48:25 +00002318
2319 let f1 = idx("fn first_func")?;
2320 let f2 = idx("fn second_func")?;
2321 let s1 = idx("struct FirstStruct")?;
2322 let s2 = idx("struct SecondStruct")?;
Googlera675ae02021-12-07 08:04:59 +00002323 let t1 = idx("fn __rust_thunk___Z10first_funcv")?;
2324 let t2 = idx("fn __rust_thunk___Z11second_funcv")?;
Michael Forstered642022021-10-04 09:48:25 +00002325
2326 assert!(f1 < s1);
2327 assert!(s1 < f2);
2328 assert!(f2 < s2);
2329 assert!(s2 < t1);
2330 assert!(t1 < t2);
2331
2332 Ok(())
2333 }
Michael Forster028800b2021-10-05 12:39:59 +00002334
2335 #[test]
Devin Jeanpierrec80e6242022-02-03 01:56:40 +00002336 fn test_base_class_subobject_layout() -> Result<()> {
2337 let ir = ir_from_cc(
2338 r#"
2339 // We use a class here to force `Derived::z` to live inside the tail padding of `Base`.
2340 // On the Itanium ABI, this would not happen if `Base` were a POD type.
Devin Jeanpierre56777022022-02-03 01:57:15 +00002341 class Base {__INT64_TYPE__ x; char y;};
2342 struct Derived final : Base {__INT16_TYPE__ z;};
Devin Jeanpierrec80e6242022-02-03 01:56:40 +00002343 "#,
2344 )?;
2345 let rs_api = generate_rs_api(&ir)?;
2346 assert_rs_matches!(
2347 rs_api,
2348 quote! {
2349 #[repr(C, align(8))]
2350 pub struct Derived {
2351 __base_class_subobjects: [std::mem::MaybeUninit<u8>; 10],
2352 pub z: i16,
2353 }
2354 }
2355 );
2356 Ok(())
2357 }
2358
2359 /// The same as test_base_class_subobject_layout, but with multiple
2360 /// inheritance.
2361 #[test]
2362 fn test_base_class_multiple_inheritance_subobject_layout() -> Result<()> {
2363 let ir = ir_from_cc(
2364 r#"
Devin Jeanpierre56777022022-02-03 01:57:15 +00002365 class Base1 {__INT64_TYPE__ x;};
Devin Jeanpierrec80e6242022-02-03 01:56:40 +00002366 class Base2 {char y;};
Devin Jeanpierre56777022022-02-03 01:57:15 +00002367 struct Derived final : Base1, Base2 {__INT16_TYPE__ z;};
Devin Jeanpierrec80e6242022-02-03 01:56:40 +00002368 "#,
2369 )?;
2370 let rs_api = generate_rs_api(&ir)?;
2371 assert_rs_matches!(
2372 rs_api,
2373 quote! {
2374 #[repr(C, align(8))]
2375 pub struct Derived {
2376 __base_class_subobjects: [std::mem::MaybeUninit<u8>; 10],
2377 pub z: i16,
2378 }
2379 }
2380 );
2381 Ok(())
2382 }
2383
2384 /// The same as test_base_class_subobject_layout, but with a chain of
2385 /// inheritance.
2386 #[test]
2387 fn test_base_class_deep_inheritance_subobject_layout() -> Result<()> {
2388 let ir = ir_from_cc(
2389 r#"
Devin Jeanpierre56777022022-02-03 01:57:15 +00002390 class Base1 {__INT64_TYPE__ x;};
Devin Jeanpierrec80e6242022-02-03 01:56:40 +00002391 class Base2 : Base1 {char y;};
Devin Jeanpierre56777022022-02-03 01:57:15 +00002392 struct Derived final : Base2 {__INT16_TYPE__ z;};
Devin Jeanpierrec80e6242022-02-03 01:56:40 +00002393 "#,
2394 )?;
2395 let rs_api = generate_rs_api(&ir)?;
2396 assert_rs_matches!(
2397 rs_api,
2398 quote! {
2399 #[repr(C, align(8))]
2400 pub struct Derived {
2401 __base_class_subobjects: [std::mem::MaybeUninit<u8>; 10],
2402 pub z: i16,
2403 }
2404 }
2405 );
2406 Ok(())
2407 }
2408
2409 /// For derived classes with no data members, we can't use the offset of the
2410 /// first member to determine the size of the base class subobjects.
2411 #[test]
2412 fn test_base_class_subobject_fieldless_layout() -> Result<()> {
2413 let ir = ir_from_cc(
2414 r#"
Devin Jeanpierre56777022022-02-03 01:57:15 +00002415 class Base {__INT64_TYPE__ x; char y;};
Devin Jeanpierrec80e6242022-02-03 01:56:40 +00002416 struct Derived final : Base {};
2417 "#,
2418 )?;
2419 let rs_api = generate_rs_api(&ir)?;
2420 assert_rs_matches!(
2421 rs_api,
2422 quote! {
2423 #[repr(C, align(8))]
2424 pub struct Derived {
2425 __base_class_subobjects: [std::mem::MaybeUninit<u8>; 9],
2426 }
2427 }
2428 );
2429 Ok(())
2430 }
2431
2432 #[test]
2433 fn test_base_class_subobject_empty_fieldless() -> Result<()> {
2434 let ir = ir_from_cc(
2435 r#"
2436 class Base {};
2437 struct Derived final : Base {};
2438 "#,
2439 )?;
2440 let rs_api = generate_rs_api(&ir)?;
2441 assert_rs_matches!(
2442 rs_api,
2443 quote! {
2444 #[repr(C)]
2445 pub struct Derived {
2446 __base_class_subobjects: [std::mem::MaybeUninit<u8>; 0],
2447 /// Prevent empty C++ struct being zero-size in Rust.
2448 placeholder: std::mem::MaybeUninit<u8>,
2449 }
2450 }
2451 );
2452 Ok(())
2453 }
2454
2455 #[test]
2456 fn test_base_class_subobject_empty() -> Result<()> {
2457 let ir = ir_from_cc(
2458 r#"
2459 class Base {};
2460 struct Derived final : Base {};
2461 "#,
2462 )?;
2463 let rs_api = generate_rs_api(&ir)?;
2464 assert_rs_matches!(
2465 rs_api,
2466 quote! {
2467 #[repr(C)]
2468 pub struct Derived {
2469 __base_class_subobjects: [std::mem::MaybeUninit<u8>; 0],
2470 /// Prevent empty C++ struct being zero-size in Rust.
2471 placeholder: std::mem::MaybeUninit<u8>,
2472 }
2473 }
2474 );
2475 Ok(())
2476 }
2477
Devin Jeanpierreb69bcae2022-02-03 09:45:50 +00002478 /// When a field is [[no_unique_address]], it occupies the space up to the
2479 /// next field.
2480 #[test]
2481 fn test_no_unique_address() -> Result<()> {
2482 let ir = ir_from_cc(
2483 r#"
2484 class Field1 {__INT64_TYPE__ x;};
2485 class Field2 {char y;};
2486 struct Struct final {
2487 [[no_unique_address]] Field1 field1;
2488 [[no_unique_address]] Field2 field2;
2489 __INT16_TYPE__ z;
2490 };
2491 "#,
2492 )?;
2493 let rs_api = generate_rs_api(&ir)?;
2494 assert_rs_matches!(
2495 rs_api,
2496 quote! {
2497 #[derive(Clone, Copy)]
2498 #[repr(C, align(8))]
2499 pub struct Struct {
2500 field1: [std::mem::MaybeUninit<u8>; 8],
2501 field2: [std::mem::MaybeUninit<u8>; 2],
2502 pub z: i16,
2503 }
Devin Jeanpierre58181ac2022-02-14 21:30:05 +00002504
2505 impl Struct {
2506 pub fn field1(&self) -> &Field1 {
2507 unsafe {&* (&self.field1 as *const _ as *const Field1)}
2508 }
2509 pub fn field2(&self) -> &Field2 {
2510 unsafe {&* (&self.field2 as *const _ as *const Field2)}
2511 }
2512 }
Devin Jeanpierreb69bcae2022-02-03 09:45:50 +00002513 }
2514 );
2515 Ok(())
2516 }
2517
2518 /// When a [[no_unique_address]] field is the last one, it occupies the rest
2519 /// of the object.
2520 #[test]
2521 fn test_no_unique_address_last_field() -> Result<()> {
2522 let ir = ir_from_cc(
2523 r#"
2524 class Field1 {__INT64_TYPE__ x;};
2525 class Field2 {char y;};
2526 struct Struct final {
2527 [[no_unique_address]] Field1 field1;
2528 [[no_unique_address]] Field2 field2;
2529 };
2530 "#,
2531 )?;
2532 let rs_api = generate_rs_api(&ir)?;
2533 assert_rs_matches!(
2534 rs_api,
2535 quote! {
2536 #[derive(Clone, Copy)]
2537 #[repr(C, align(8))]
2538 pub struct Struct {
2539 field1: [std::mem::MaybeUninit<u8>; 8],
2540 field2: [std::mem::MaybeUninit<u8>; 8],
2541 }
2542 }
2543 );
2544 Ok(())
2545 }
2546
2547 #[test]
2548 fn test_no_unique_address_empty() -> Result<()> {
2549 let ir = ir_from_cc(
2550 r#"
2551 class Field {};
2552 struct Struct final {
2553 [[no_unique_address]] Field field;
2554 int x;
2555 };
2556 "#,
2557 )?;
2558 let rs_api = generate_rs_api(&ir)?;
2559 assert_rs_matches!(
2560 rs_api,
2561 quote! {
2562 #[repr(C, align(4))]
2563 pub struct Struct {
2564 field: [std::mem::MaybeUninit<u8>; 0],
2565 pub x: i32,
2566 }
2567 }
2568 );
2569 Ok(())
2570 }
2571
2572 #[test]
2573 fn test_base_class_subobject_empty_last_field() -> Result<()> {
2574 let ir = ir_from_cc(
2575 r#"
2576 class Field {};
2577 struct Struct final {
2578 [[no_unique_address]] Field field;
2579 };
2580 "#,
2581 )?;
2582 let rs_api = generate_rs_api(&ir)?;
2583 assert_rs_matches!(
2584 rs_api,
2585 quote! {
2586 #[repr(C)]
2587 pub struct Struct {
2588 field: [std::mem::MaybeUninit<u8>; 1],
2589 }
2590 }
2591 );
2592 Ok(())
2593 }
2594
Devin Jeanpierrec80e6242022-02-03 01:56:40 +00002595 #[test]
Teddy Katz76fa42b2022-02-23 01:22:56 +00002596 fn test_generate_enum_basic() -> Result<()> {
2597 let ir = ir_from_cc("enum Color { kRed = 5, kBlue };")?;
2598 let rs_api = generate_rs_api(&ir)?;
2599 assert_rs_matches!(
2600 rs_api,
2601 quote! {
2602 #[repr(transparent)]
2603 #[derive(Debug, PartialEq, Eq, Copy, Clone, Hash, PartialOrd, Ord)]
2604 pub struct Color(u32);
2605 impl Color {
2606 pub const kRed: Color = Color(5);
2607 pub const kBlue: Color = Color(6);
2608 }
2609 impl From<u32> for Color {
2610 fn from(value: u32) -> Color {
2611 Color(v)
2612 }
2613 }
2614 impl From<Color> for u32 {
2615 fn from(value: Color) -> u32 {
2616 v.0
2617 }
2618 }
2619 }
2620 );
2621 Ok(())
2622 }
2623
2624 #[test]
2625 fn test_generate_scoped_enum_basic() -> Result<()> {
2626 let ir = ir_from_cc("enum class Color { kRed = -5, kBlue };")?;
2627 let rs_api = generate_rs_api(&ir)?;
2628 assert_rs_matches!(
2629 rs_api,
2630 quote! {
2631 #[repr(transparent)]
2632 #[derive(Debug, PartialEq, Eq, Copy, Clone, Hash, PartialOrd, Ord)]
2633 pub struct Color(i32);
2634 impl Color {
2635 pub const kRed: Color = Color(-5);
2636 pub const kBlue: Color = Color(-4);
2637 }
2638 impl From<i32> for Color {
2639 fn from(value: i32) -> Color {
2640 Color(v)
2641 }
2642 }
2643 impl From<Color> for i32 {
2644 fn from(value: Color) -> i32 {
2645 v.0
2646 }
2647 }
2648 }
2649 );
2650 Ok(())
2651 }
2652
2653 #[test]
2654 fn test_generate_enum_with_64_bit_signed_vals() -> Result<()> {
2655 let ir = ir_from_cc(
2656 "enum Color : long { kViolet = -9223372036854775807 - 1LL, kRed = -5, kBlue, kGreen = 3, kMagenta = 9223372036854775807 };",
2657 )?;
2658 let rs_api = generate_rs_api(&ir)?;
2659 assert_rs_matches!(
2660 rs_api,
2661 quote! {
2662 #[repr(transparent)]
2663 #[derive(Debug, PartialEq, Eq, Copy, Clone, Hash, PartialOrd, Ord)]
2664 pub struct Color(i64);
2665 impl Color {
2666 pub const kViolet: Color = Color(-9223372036854775808);
2667 pub const kRed: Color = Color(-5);
2668 pub const kBlue: Color = Color(-4);
2669 pub const kGreen: Color = Color(3);
2670 pub const kMagenta: Color = Color(9223372036854775807);
2671 }
2672 impl From<i64> for Color {
2673 fn from(value: i64) -> Color {
2674 Color(v)
2675 }
2676 }
2677 impl From<Color> for i64 {
2678 fn from(value: Color) -> i64 {
2679 v.0
2680 }
2681 }
2682 }
2683 );
2684 Ok(())
2685 }
2686
2687 #[test]
2688 fn test_generate_enum_with_64_bit_unsigned_vals() -> Result<()> {
2689 let ir = ir_from_cc(
2690 "enum Color: unsigned long { kRed, kBlue, kLimeGreen = 18446744073709551615 };",
2691 )?;
2692 let rs_api = generate_rs_api(&ir)?;
2693 assert_rs_matches!(
2694 rs_api,
2695 quote! {
2696 #[repr(transparent)]
2697 #[derive(Debug, PartialEq, Eq, Copy, Clone, Hash, PartialOrd, Ord)]
2698 pub struct Color(u64);
2699 impl Color {
2700 pub const kRed: Color = Color(0);
2701 pub const kBlue: Color = Color(1);
2702 pub const kLimeGreen: Color = Color(18446744073709551615);
2703 }
2704 impl From<u64> for Color {
2705 fn from(value: u64) -> Color {
2706 Color(v)
2707 }
2708 }
2709 impl From<Color> for u64 {
2710 fn from(value: Color) -> u64 {
2711 v.0
2712 }
2713 }
2714 }
2715 );
2716 Ok(())
2717 }
2718
2719 #[test]
2720 fn test_generate_enum_with_32_bit_signed_vals() -> Result<()> {
2721 let ir = ir_from_cc(
2722 "enum Color { kViolet = -2147483647 - 1, kRed = -5, kBlue, kGreen = 3, kMagenta = 2147483647 };",
2723 )?;
2724 let rs_api = generate_rs_api(&ir)?;
2725 assert_rs_matches!(
2726 rs_api,
2727 quote! {
2728 #[repr(transparent)]
2729 #[derive(Debug, PartialEq, Eq, Copy, Clone, Hash, PartialOrd, Ord)]
2730 pub struct Color(i32);
2731 impl Color {
2732 pub const kViolet: Color = Color(-2147483648);
2733 pub const kRed: Color = Color(-5);
2734 pub const kBlue: Color = Color(-4);
2735 pub const kGreen: Color = Color(3);
2736 pub const kMagenta: Color = Color(2147483647);
2737 }
2738 impl From<i32> for Color {
2739 fn from(value: i32) -> Color {
2740 Color(v)
2741 }
2742 }
2743 impl From<Color> for i32 {
2744 fn from(value: Color) -> i32 {
2745 v.0
2746 }
2747 }
2748 }
2749 );
2750 Ok(())
2751 }
2752
2753 #[test]
2754 fn test_generate_enum_with_32_bit_unsigned_vals() -> Result<()> {
2755 let ir = ir_from_cc("enum Color: unsigned int { kRed, kBlue, kLimeGreen = 4294967295 };")?;
2756 let rs_api = generate_rs_api(&ir)?;
2757 assert_rs_matches!(
2758 rs_api,
2759 quote! {
2760 #[repr(transparent)]
2761 #[derive(Debug, PartialEq, Eq, Copy, Clone, Hash, PartialOrd, Ord)]
2762 pub struct Color(u32);
2763 impl Color {
2764 pub const kRed: Color = Color(0);
2765 pub const kBlue: Color = Color(1);
2766 pub const kLimeGreen: Color = Color(4294967295);
2767 }
2768 impl From<u32> for Color {
2769 fn from(value: u32) -> Color {
2770 Color(v)
2771 }
2772 }
2773 impl From<Color> for u32 {
2774 fn from(value: Color) -> u32 {
2775 v.0
2776 }
2777 }
2778 }
2779 );
2780 Ok(())
2781 }
2782
2783 #[test]
Michael Forster409d9412021-10-07 08:35:29 +00002784 fn test_doc_comment_func() -> Result<()> {
Marcel Hlopko89547752021-12-10 09:39:41 +00002785 let ir = ir_from_cc(
2786 "
2787 // Doc Comment
2788 // with two lines
2789 int func();",
2790 )?;
Michael Forster409d9412021-10-07 08:35:29 +00002791
Marcel Hlopko89547752021-12-10 09:39:41 +00002792 assert_rs_matches!(
2793 generate_rs_api(&ir)?,
2794 // leading space is intentional so there is a space between /// and the text of the
2795 // comment
2796 quote! {
2797 #[doc = " Doc Comment\n with two lines"]
2798 #[inline(always)]
2799 pub fn func
2800 }
Michael Forster409d9412021-10-07 08:35:29 +00002801 );
2802
2803 Ok(())
2804 }
2805
2806 #[test]
2807 fn test_doc_comment_record() -> Result<()> {
Marcel Hlopko89547752021-12-10 09:39:41 +00002808 let ir = ir_from_cc(
2809 "// Doc Comment\n\
2810 //\n\
2811 // * with bullet\n\
Devin Jeanpierre88343c72022-01-15 01:10:23 +00002812 struct SomeStruct final {\n\
Marcel Hlopko89547752021-12-10 09:39:41 +00002813 // Field doc\n\
2814 int field;\
2815 };",
2816 )?;
Michael Forster028800b2021-10-05 12:39:59 +00002817
Marcel Hlopko89547752021-12-10 09:39:41 +00002818 assert_rs_matches!(
2819 generate_rs_api(&ir)?,
2820 quote! {
2821 #[doc = " Doc Comment\n \n * with bullet"]
2822 #[derive(Clone, Copy)]
2823 #[repr(C)]
2824 pub struct SomeStruct {
2825 # [doc = " Field doc"]
2826 pub field: i32,
2827 }
2828 }
Michael Forstercc5941a2021-10-07 07:12:24 +00002829 );
Michael Forster028800b2021-10-05 12:39:59 +00002830 Ok(())
2831 }
Devin Jeanpierre91de7012021-10-21 12:53:51 +00002832
Devin Jeanpierre96839c12021-12-14 00:27:38 +00002833 #[test]
Devin Jeanpierre56777022022-02-03 01:57:15 +00002834 fn test_unambiguous_public_bases() -> Result<()> {
2835 let ir = ir_from_cc_dependency(
2836 "
2837 struct VirtualBase {};
2838 struct PrivateBase {};
2839 struct ProtectedBase {};
2840 struct UnambiguousPublicBase {};
2841 struct AmbiguousPublicBase {};
2842 struct MultipleInheritance : UnambiguousPublicBase, AmbiguousPublicBase {};
2843 struct Derived : private PrivateBase, protected ProtectedBase, MultipleInheritance, AmbiguousPublicBase, virtual VirtualBase {};
2844 ",
2845 "",
2846 )?;
2847 let rs_api = generate_rs_api(&ir)?;
2848 // TODO(b/216195042): virtual bases.
2849 assert_rs_not_matches!(rs_api, quote! { From<&'a Derived> for &'a VirtualBase });
2850 assert_rs_matches!(rs_api, quote! { From<&'a Derived> for &'a UnambiguousPublicBase });
2851 assert_rs_matches!(rs_api, quote! { From<&'a Derived> for &'a MultipleInheritance });
2852 assert_rs_not_matches!(rs_api, quote! {From<&'a Derived> for &'a PrivateBase});
2853 assert_rs_not_matches!(rs_api, quote! {From<&'a Derived> for &'a ProtectedBase});
2854 assert_rs_not_matches!(rs_api, quote! {From<&'a Derived> for &'a AmbiguousPublicBase});
2855 Ok(())
2856 }
2857
2858 /// Contrary to intuitions: a base class conversion is ambiguous even if the
2859 /// ambiguity is from a private base class cast that you can't even
2860 /// perform.
2861 ///
2862 /// Explanation (courtesy James Dennett):
2863 ///
2864 /// > Once upon a time, there was a rule in C++ that changing all access
2865 /// > specifiers to "public" would not change the meaning of code.
2866 /// > That's no longer true, but some of its effects can still be seen.
2867 ///
2868 /// So, we need to be sure to not allow casting to privately-ambiguous
2869 /// bases.
2870 #[test]
2871 fn test_unambiguous_public_bases_private_ambiguity() -> Result<()> {
2872 let ir = ir_from_cc_dependency(
2873 "
2874 struct Base {};
2875 struct Intermediate : public Base {};
2876 struct Derived : Base, private Intermediate {};
2877 ",
2878 "",
2879 )?;
2880 let rs_api = generate_rs_api(&ir)?;
2881 assert_rs_not_matches!(rs_api, quote! { From<&'a Derived> for &'a Base });
2882 Ok(())
2883 }
2884
2885 #[test]
Devin Jeanpierre96839c12021-12-14 00:27:38 +00002886 fn test_virtual_thunk() -> Result<()> {
2887 let ir = ir_from_cc("struct Polymorphic { virtual void Foo(); };")?;
2888
2889 assert_cc_matches!(
2890 generate_rs_api_impl(&ir)?,
2891 quote! {
Googler972d3582022-01-11 10:17:22 +00002892 extern "C" void __rust_thunk___ZN11Polymorphic3FooEv(class Polymorphic * __this)
Devin Jeanpierre96839c12021-12-14 00:27:38 +00002893 }
2894 );
2895 Ok(())
2896 }
2897
Devin Jeanpierree6e16652021-12-22 15:54:46 +00002898 /// A trivially relocatable final struct is safe to use in Rust as normal,
2899 /// and is Unpin.
2900 #[test]
2901 fn test_no_negative_impl_unpin() -> Result<()> {
2902 let ir = ir_from_cc("struct Trivial final {};")?;
2903 let rs_api = generate_rs_api(&ir)?;
2904 assert_rs_not_matches!(rs_api, quote! {impl !Unpin});
2905 Ok(())
2906 }
2907
2908 /// A non-final struct, even if it's trivial, is not usable by mut
2909 /// reference, and so is !Unpin.
2910 #[test]
2911 fn test_negative_impl_unpin_nonfinal() -> Result<()> {
2912 let ir = ir_from_cc("struct Nonfinal {};")?;
2913 let rs_api = generate_rs_api(&ir)?;
2914 assert_rs_matches!(rs_api, quote! {impl !Unpin for Nonfinal {}});
2915 Ok(())
2916 }
2917
Devin Jeanpierre91de7012021-10-21 12:53:51 +00002918 /// At the least, a trivial type should have no drop impl if or until we add
2919 /// empty drop impls.
2920 #[test]
2921 fn test_no_impl_drop() -> Result<()> {
Googler7cced422021-12-06 11:58:39 +00002922 let ir = ir_from_cc("struct Trivial {};")?;
Marcel Hlopko89547752021-12-10 09:39:41 +00002923 let rs_api = rs_tokens_to_formatted_string(generate_rs_api(&ir)?)?;
Devin Jeanpierre91de7012021-10-21 12:53:51 +00002924 assert!(!rs_api.contains("impl Drop"));
2925 Ok(())
2926 }
2927
Devin Jeanpierre7e9a1de2021-12-03 08:04:22 +00002928 /// User-defined destructors *must* become Drop impls with ManuallyDrop
2929 /// fields
Devin Jeanpierre91de7012021-10-21 12:53:51 +00002930 #[test]
Devin Jeanpierre7e9a1de2021-12-03 08:04:22 +00002931 fn test_impl_drop_user_defined_destructor() -> Result<()> {
Googler7cced422021-12-06 11:58:39 +00002932 let ir = ir_from_cc(
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00002933 r#" struct NontrivialStruct { ~NontrivialStruct(); };
2934 struct UserDefinedDestructor {
Devin Jeanpierre91de7012021-10-21 12:53:51 +00002935 ~UserDefinedDestructor();
Devin Jeanpierre7e9a1de2021-12-03 08:04:22 +00002936 int x;
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00002937 NontrivialStruct nts;
Devin Jeanpierre91de7012021-10-21 12:53:51 +00002938 };"#,
2939 )?;
2940 let rs_api = generate_rs_api(&ir)?;
Lukasz Anforowicz6d553632022-01-06 21:36:14 +00002941 assert_rs_matches!(
2942 rs_api,
2943 quote! {
2944 impl Drop for UserDefinedDestructor {
2945 #[inline(always)]
2946 fn drop(&mut self) {
2947 unsafe { crate::detail::__rust_thunk___ZN21UserDefinedDestructorD1Ev(self) }
2948 }
2949 }
2950 }
2951 );
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00002952 assert_rs_matches!(rs_api, quote! {pub x: i32,});
2953 assert_rs_matches!(rs_api, quote! {pub nts: std::mem::ManuallyDrop<NontrivialStruct>,});
Devin Jeanpierre7e9a1de2021-12-03 08:04:22 +00002954 Ok(())
2955 }
2956
Lukasz Anforowicz6d553632022-01-06 21:36:14 +00002957 /// nontrivial types without user-defined destructors should invoke
2958 /// the C++ destructor to preserve the order of field destructions.
Devin Jeanpierre7e9a1de2021-12-03 08:04:22 +00002959 #[test]
2960 fn test_impl_drop_nontrivial_member_destructor() -> Result<()> {
2961 // TODO(jeanpierreda): This would be cleaner if the UserDefinedDestructor code were
2962 // omitted. For example, we simulate it so that UserDefinedDestructor
2963 // comes from another library.
Googler7cced422021-12-06 11:58:39 +00002964 let ir = ir_from_cc(
Devin Jeanpierre88343c72022-01-15 01:10:23 +00002965 r#"struct UserDefinedDestructor final {
Devin Jeanpierre7e9a1de2021-12-03 08:04:22 +00002966 ~UserDefinedDestructor();
2967 };
Devin Jeanpierre88343c72022-01-15 01:10:23 +00002968 struct TrivialStruct final { int i; };
2969 struct NontrivialMembers final {
Devin Jeanpierre7e9a1de2021-12-03 08:04:22 +00002970 UserDefinedDestructor udd;
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00002971 TrivialStruct ts;
Devin Jeanpierre7e9a1de2021-12-03 08:04:22 +00002972 int x;
2973 };"#,
2974 )?;
2975 let rs_api = generate_rs_api(&ir)?;
Lukasz Anforowicz6d553632022-01-06 21:36:14 +00002976 assert_rs_matches!(
2977 rs_api,
2978 quote! {
2979 impl Drop for NontrivialMembers {
2980 #[inline(always)]
2981 fn drop(&mut self) {
2982 unsafe { crate::detail::__rust_thunk___ZN17NontrivialMembersD1Ev(self) }
2983 }
2984 }
2985 }
2986 );
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00002987 assert_rs_matches!(rs_api, quote! {pub x: i32,});
2988 assert_rs_matches!(rs_api, quote! {pub ts: TrivialStruct,});
Lukasz Anforowicz6d553632022-01-06 21:36:14 +00002989 assert_rs_matches!(
2990 rs_api,
2991 quote! {pub udd: std::mem::ManuallyDrop<UserDefinedDestructor>,}
2992 );
Devin Jeanpierre7e9a1de2021-12-03 08:04:22 +00002993 Ok(())
2994 }
2995
2996 /// Trivial types (at least those that are mapped to Copy rust types) do not
2997 /// get a Drop impl.
2998 #[test]
2999 fn test_impl_drop_trivial() -> Result<()> {
Googler7cced422021-12-06 11:58:39 +00003000 let ir = ir_from_cc(
Devin Jeanpierre88343c72022-01-15 01:10:23 +00003001 r#"struct Trivial final {
Devin Jeanpierre7e9a1de2021-12-03 08:04:22 +00003002 ~Trivial() = default;
3003 int x;
3004 };"#,
3005 )?;
3006 let rs_api = generate_rs_api(&ir)?;
Marcel Hlopko89547752021-12-10 09:39:41 +00003007 assert_rs_not_matches!(rs_api, quote! {impl Drop});
3008 assert_rs_matches!(rs_api, quote! {pub x: i32});
Lukasz Anforowicz2f074162022-01-06 22:50:51 +00003009 let rs_api_impl = generate_rs_api_impl(&ir)?;
3010 // TODO(b/213326125): Avoid generating thunk impls that are never called.
3011 // (The test assertion below should be reversed once this bug is fixed.)
3012 assert_cc_matches!(rs_api_impl, quote! { std::destroy_at });
Devin Jeanpierre91de7012021-10-21 12:53:51 +00003013 Ok(())
3014 }
Devin Jeanpierre45cb1162021-10-27 10:54:28 +00003015
3016 #[test]
Lukasz Anforowicze643ec92021-12-22 15:45:15 +00003017 fn test_impl_default_explicitly_defaulted_constructor() -> Result<()> {
3018 let ir = ir_from_cc(
Lukasz Anforowicz95551272022-01-20 00:02:24 +00003019 r#"#pragma clang lifetime_elision
3020 struct DefaultedConstructor final {
Lukasz Anforowicze643ec92021-12-22 15:45:15 +00003021 DefaultedConstructor() = default;
3022 };"#,
3023 )?;
3024 let rs_api = generate_rs_api(&ir)?;
3025 assert_rs_matches!(
3026 rs_api,
3027 quote! {
3028 impl Default for DefaultedConstructor {
3029 #[inline(always)]
3030 fn default() -> Self {
Lukasz Anforowiczbedbdee2022-01-05 01:14:52 +00003031 let mut tmp = std::mem::MaybeUninit::<Self>::zeroed();
Lukasz Anforowicze643ec92021-12-22 15:45:15 +00003032 unsafe {
Lukasz Anforowicz40c2eb82022-01-11 18:22:31 +00003033 crate::detail::__rust_thunk___ZN20DefaultedConstructorC1Ev(&mut tmp);
Lukasz Anforowicze643ec92021-12-22 15:45:15 +00003034 tmp.assume_init()
3035 }
3036 }
3037 }
3038 }
3039 );
3040 let rs_api_impl = generate_rs_api_impl(&ir)?;
3041 assert_cc_matches!(
3042 rs_api_impl,
3043 quote! {
3044 extern "C" void __rust_thunk___ZN20DefaultedConstructorC1Ev(
Googler972d3582022-01-11 10:17:22 +00003045 class DefaultedConstructor* __this) {
Lukasz Anforowicz4457baf2021-12-23 17:24:04 +00003046 rs_api_impl_support::construct_at (__this) ;
Lukasz Anforowicze643ec92021-12-22 15:45:15 +00003047 }
3048 }
3049 );
3050 Ok(())
3051 }
3052
3053 #[test]
Lukasz Anforowicz326c4e42022-01-27 14:43:00 +00003054 fn test_impl_clone_that_propagates_lifetime() -> Result<()> {
3055 // This test covers the case where a single lifetime applies to 1)
3056 // the `__this` parameter and 2) other constructor parameters. For
3057 // example, maybe the newly constructed object needs to have the
3058 // same lifetime as the constructor's parameter. (This might require
3059 // annotating the whole C++ struct with a lifetime, so maybe the
3060 // example below is not fully realistic/accurate...).
3061 let mut ir = ir_from_cc(
3062 r#"#pragma clang lifetime_elision
3063 struct Foo final {
Googler53f65942022-02-23 11:23:30 +00003064 [[clang::annotate("lifetimes", "a: a")]]
Lukasz Anforowicz326c4e42022-01-27 14:43:00 +00003065 Foo(const int& i);
3066 };"#,
3067 )?;
3068 let ctor: &mut Func = ir
3069 .items_mut()
3070 .filter_map(|item| match item {
3071 Item::Func(func) => Some(func),
3072 _ => None,
3073 })
3074 .find(|f| {
3075 matches!(&f.name, UnqualifiedIdentifier::Constructor)
3076 && f.params.get(1).map(|p| p.identifier.identifier == "i").unwrap_or_default()
3077 })
3078 .unwrap();
3079 {
3080 // Double-check that the test scenario set up above uses the same lifetime
3081 // for both of the constructor's parameters: `__this` and `i`.
3082 assert_eq!(ctor.params.len(), 2);
3083 let this_lifetime: LifetimeId =
3084 *ctor.params[0].type_.rs_type.lifetime_args.first().unwrap();
3085 let i_lifetime: LifetimeId =
3086 *ctor.params[1].type_.rs_type.lifetime_args.first_mut().unwrap();
3087 assert_eq!(i_lifetime, this_lifetime);
3088 }
3089
3090 // Before cl/423346348 the generated Rust code would incorrectly look
3091 // like this (note the mismatched 'a and 'b lifetimes):
3092 // fn from<'b>(i: &'a i32) -> Self
3093 // After this CL, this scenario will result in an explicit error.
3094 let err = generate_rs_api(&ir).unwrap_err();
3095 let msg = format!("{}", err);
3096 assert!(
3097 msg.contains("The lifetime of `__this` is unexpectedly also used by another parameter")
3098 );
3099 Ok(())
3100 }
3101
3102 #[test]
Lukasz Anforowicz9bab8352021-12-22 17:35:31 +00003103 fn test_impl_default_non_trivial_struct() -> Result<()> {
3104 let ir = ir_from_cc(
Lukasz Anforowicz71716b72022-01-26 17:05:05 +00003105 r#"#pragma clang lifetime_elision
3106 struct NonTrivialStructWithConstructors final {
Lukasz Anforowicz9bab8352021-12-22 17:35:31 +00003107 NonTrivialStructWithConstructors();
3108 ~NonTrivialStructWithConstructors(); // Non-trivial
3109 };"#,
3110 )?;
3111 let rs_api = generate_rs_api(&ir)?;
3112 assert_rs_not_matches!(rs_api, quote! {impl Default});
3113 Ok(())
3114 }
3115
3116 #[test]
Lukasz Anforowicz71716b72022-01-26 17:05:05 +00003117 fn test_impl_from_for_explicit_conversion_constructor() -> Result<()> {
3118 let ir = ir_from_cc(
3119 r#"#pragma clang lifetime_elision
3120 struct SomeStruct final {
3121 explicit SomeStruct(int i);
3122 };"#,
3123 )?;
3124 let rs_api = generate_rs_api(&ir)?;
3125 // As discussed in b/214020567 for now we only generate `From::from` bindings
3126 // for *implicit* C++ conversion constructors.
3127 assert_rs_not_matches!(rs_api, quote! {impl From});
3128 Ok(())
3129 }
3130
3131 #[test]
3132 fn test_impl_from_for_implicit_conversion_constructor() -> Result<()> {
3133 let ir = ir_from_cc(
3134 r#"#pragma clang lifetime_elision
3135 struct SomeStruct final {
3136 SomeStruct(int i); // implicit - no `explicit` keyword
3137 };"#,
3138 )?;
3139 let rs_api = generate_rs_api(&ir)?;
3140 // As discussed in b/214020567 we generate `From::from` bindings for
3141 // *implicit* C++ conversion constructors.
3142 assert_rs_matches!(
3143 rs_api,
3144 quote! {
3145 impl From<i32> for SomeStruct {
3146 #[inline(always)]
3147 fn from(i: i32) -> Self {
3148 let mut tmp = std::mem::MaybeUninit::<Self>::zeroed();
3149 unsafe {
3150 crate::detail::__rust_thunk___ZN10SomeStructC1Ei(&mut tmp, i);
3151 tmp.assume_init()
3152 }
3153 }
3154 }
3155 }
3156 );
3157 Ok(())
3158 }
3159
3160 #[test]
Lukasz Anforowicz732ca642022-02-03 20:58:38 +00003161 fn test_impl_eq_for_member_function() -> Result<()> {
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +00003162 let ir = ir_from_cc(
3163 r#"#pragma clang lifetime_elision
3164 struct SomeStruct final {
3165 inline bool operator==(const SomeStruct& other) const {
3166 return i == other.i;
3167 }
3168 int i;
3169 };"#,
3170 )?;
3171 let rs_api = generate_rs_api(&ir)?;
3172 assert_rs_matches!(
3173 rs_api,
3174 quote! {
3175 impl PartialEq<SomeStruct> for SomeStruct {
3176 #[inline(always)]
3177 fn eq<'a, 'b>(&'a self, other: &'b SomeStruct) -> bool {
3178 unsafe { crate::detail::__rust_thunk___ZNK10SomeStructeqERKS_(self, other) }
3179 }
3180 }
3181 }
3182 );
3183 let rs_api_impl = generate_rs_api_impl(&ir)?;
3184 assert_cc_matches!(
3185 rs_api_impl,
3186 quote! {
3187 extern "C" bool __rust_thunk___ZNK10SomeStructeqERKS_(
3188 const class SomeStruct* __this, const class SomeStruct& other) {
3189 return __this->operator==(other);
3190 }
3191 }
3192 );
3193 Ok(())
3194 }
3195
3196 #[test]
Lukasz Anforowicz732ca642022-02-03 20:58:38 +00003197 fn test_impl_eq_for_free_function() -> Result<()> {
3198 let ir = ir_from_cc(
3199 r#"#pragma clang lifetime_elision
3200 struct SomeStruct final { int i; };
3201 bool operator==(const SomeStruct& lhs, const SomeStruct& rhs) {
3202 return lhs.i == rhs.i;
3203 }"#,
3204 )?;
3205 let rs_api = generate_rs_api(&ir)?;
3206 assert_rs_matches!(
3207 rs_api,
3208 quote! {
3209 impl PartialEq<SomeStruct> for SomeStruct {
3210 #[inline(always)]
3211 fn eq<'a, 'b>(&'a self, rhs: &'b SomeStruct) -> bool {
3212 unsafe { crate::detail::__rust_thunk___ZeqRK10SomeStructS1_(self, rhs) }
3213 }
3214 }
3215 }
3216 );
3217 Ok(())
3218 }
3219
3220 #[test]
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +00003221 fn test_impl_eq_non_const_member_function() -> Result<()> {
3222 let ir = ir_from_cc(
3223 r#"#pragma clang lifetime_elision
3224 struct SomeStruct final {
3225 bool operator==(const SomeStruct& other) /* no `const` here */;
3226 };"#,
3227 )?;
3228 let rs_api = generate_rs_api(&ir)?;
3229 assert_rs_not_matches!(rs_api, quote! {impl PartialEq});
3230 Ok(())
3231 }
3232
3233 #[test]
3234 fn test_impl_eq_rhs_by_value() -> Result<()> {
3235 let ir = ir_from_cc(
3236 r#"#pragma clang lifetime_elision
3237 struct SomeStruct final {
3238 bool operator==(SomeStruct other) const;
3239 };"#,
3240 )?;
3241 let rs_api = generate_rs_api(&ir)?;
3242 assert_rs_not_matches!(rs_api, quote! {impl PartialEq});
3243 Ok(())
3244 }
3245
3246 #[test]
Devin Jeanpierre45cb1162021-10-27 10:54:28 +00003247 fn test_thunk_ident_function() {
3248 let func = ir_func("foo");
Marcel Hlopkoeaae9b72022-01-21 15:54:11 +00003249 assert_eq!(thunk_ident(&func), make_rs_ident("__rust_thunk___Z3foov"));
Devin Jeanpierre45cb1162021-10-27 10:54:28 +00003250 }
3251
3252 #[test]
3253 fn test_thunk_ident_special_names() {
Marcel Hlopko4b13b962021-12-06 12:40:56 +00003254 let ir = ir_from_cc("struct Class {};").unwrap();
Devin Jeanpierre45cb1162021-10-27 10:54:28 +00003255
Googler45ad2752021-12-06 12:12:35 +00003256 let destructor =
3257 ir.functions().find(|f| f.name == UnqualifiedIdentifier::Destructor).unwrap();
Lukasz Anforowiczdd9ae0f2022-02-17 15:52:53 +00003258 assert_eq!(thunk_ident(destructor), make_rs_ident("__rust_thunk___ZN5ClassD1Ev"));
Devin Jeanpierre45cb1162021-10-27 10:54:28 +00003259
Lukasz Anforowicz49b5bbc2022-02-04 23:40:10 +00003260 let default_constructor = ir
3261 .functions()
3262 .find(|f| f.name == UnqualifiedIdentifier::Constructor && f.params.len() == 1)
3263 .unwrap();
Lukasz Anforowiczdd9ae0f2022-02-17 15:52:53 +00003264 assert_eq!(thunk_ident(default_constructor), make_rs_ident("__rust_thunk___ZN5ClassC1Ev"));
Devin Jeanpierre45cb1162021-10-27 10:54:28 +00003265 }
Googler7cced422021-12-06 11:58:39 +00003266
3267 #[test]
Marcel Hlopko89547752021-12-10 09:39:41 +00003268 fn test_elided_lifetimes() -> Result<()> {
Googler7cced422021-12-06 11:58:39 +00003269 let ir = ir_from_cc(
3270 r#"#pragma clang lifetime_elision
Devin Jeanpierre88343c72022-01-15 01:10:23 +00003271 struct S final {
Googler7cced422021-12-06 11:58:39 +00003272 int& f(int& i);
3273 };"#,
Marcel Hlopko89547752021-12-10 09:39:41 +00003274 )?;
3275 let rs_api = generate_rs_api(&ir)?;
3276 assert_rs_matches!(
3277 rs_api,
3278 quote! {
Lukasz Anforowicz231a3bb2022-01-12 14:05:59 +00003279 pub fn f<'a, 'b>(&'a mut self, i: &'b mut i32) -> &'a mut i32 { ... }
Marcel Hlopko89547752021-12-10 09:39:41 +00003280 }
Googler7cced422021-12-06 11:58:39 +00003281 );
Marcel Hlopko89547752021-12-10 09:39:41 +00003282 assert_rs_matches!(
3283 rs_api,
3284 quote! {
Googler6804a012022-01-05 07:04:36 +00003285 pub(crate) fn __rust_thunk___ZN1S1fERi<'a, 'b>(__this: &'a mut S, i: &'b mut i32)
3286 -> &'a mut i32;
Marcel Hlopko89547752021-12-10 09:39:41 +00003287 }
Googler7cced422021-12-06 11:58:39 +00003288 );
Marcel Hlopko89547752021-12-10 09:39:41 +00003289 Ok(())
Googler7cced422021-12-06 11:58:39 +00003290 }
Lukasz Anforowiczdaff0402021-12-23 00:37:50 +00003291
3292 #[test]
3293 fn test_format_generic_params() -> Result<()> {
3294 assert_rs_matches!(format_generic_params(std::iter::empty::<syn::Ident>()), quote! {});
3295
Marcel Hlopkoeaae9b72022-01-21 15:54:11 +00003296 let idents = ["T1", "T2"].iter().map(|s| make_rs_ident(s));
Lukasz Anforowiczdaff0402021-12-23 00:37:50 +00003297 assert_rs_matches!(format_generic_params(idents), quote! { < T1, T2 > });
3298
3299 let lifetimes = ["a", "b"]
3300 .iter()
3301 .map(|s| syn::Lifetime::new(&format!("'{}", s), proc_macro2::Span::call_site()));
3302 assert_rs_matches!(format_generic_params(lifetimes), quote! { < 'a, 'b > });
3303
3304 Ok(())
3305 }
Googlerd03d05b2022-01-07 10:10:57 +00003306
3307 #[test]
3308 fn test_overloaded_functions() -> Result<()> {
3309 // TODO(b/213280424): We don't support creating bindings for overloaded
3310 // functions yet, except in the case of overloaded constructors with a
3311 // single parameter.
3312 let ir = ir_from_cc(
Lukasz Anforowicz55673c92022-01-27 19:37:26 +00003313 r#" #pragma clang lifetime_elision
3314 void f();
Googlerd03d05b2022-01-07 10:10:57 +00003315 void f(int i);
Devin Jeanpierre88343c72022-01-15 01:10:23 +00003316 struct S1 final {
Googlerd03d05b2022-01-07 10:10:57 +00003317 void f();
3318 void f(int i);
3319 };
Devin Jeanpierre88343c72022-01-15 01:10:23 +00003320 struct S2 final {
Googlerd03d05b2022-01-07 10:10:57 +00003321 void f();
3322 };
Devin Jeanpierre88343c72022-01-15 01:10:23 +00003323 struct S3 final {
Googlerd03d05b2022-01-07 10:10:57 +00003324 S3(int i);
3325 S3(double d);
3326 };
3327 "#,
3328 )?;
3329 let rs_api = generate_rs_api(&ir)?;
3330 let rs_api_str = tokens_to_string(rs_api.clone())?;
3331
3332 // Cannot overload free functions.
3333 assert!(rs_api_str.contains("Error while generating bindings for item 'f'"));
3334 assert_rs_not_matches!(rs_api, quote! {pub fn f()});
3335 assert_rs_not_matches!(rs_api, quote! {pub fn f(i: i32)});
3336
3337 // Cannot overload member functions.
3338 assert!(rs_api_str.contains("Error while generating bindings for item 'S1::f'"));
3339 assert_rs_not_matches!(rs_api, quote! {pub fn f(... S1 ...)});
3340
3341 // But we can import member functions that have the same name as a free
3342 // function.
Lukasz Anforowicz55673c92022-01-27 19:37:26 +00003343 assert_rs_matches!(rs_api, quote! {pub fn f<'a>(&'a mut self)});
Googlerd03d05b2022-01-07 10:10:57 +00003344
3345 // We can also import overloaded single-parameter constructors.
3346 assert_rs_matches!(rs_api, quote! {impl From<i32> for S3});
3347 assert_rs_matches!(rs_api, quote! {impl From<f64> for S3});
3348 Ok(())
3349 }
Googlerdcca7f72022-01-10 12:30:43 +00003350
3351 #[test]
3352 fn test_type_alias() -> Result<()> {
3353 let ir = ir_from_cc(
3354 r#"
3355 typedef int MyTypedefDecl;
3356 using MyTypeAliasDecl = int;
Googler6a0a5252022-01-11 14:08:09 +00003357 using MyTypeAliasDecl_Alias = MyTypeAliasDecl;
3358
Devin Jeanpierre88343c72022-01-15 01:10:23 +00003359 struct S final {};
Googler6a0a5252022-01-11 14:08:09 +00003360 using S_Alias = S;
3361 using S_Alias_Alias = S_Alias;
3362
3363 inline void f(MyTypedefDecl t) {}
Googlerdcca7f72022-01-10 12:30:43 +00003364 "#,
3365 )?;
3366 let rs_api = generate_rs_api(&ir)?;
Googler6a0a5252022-01-11 14:08:09 +00003367 assert_rs_matches!(rs_api, quote! { pub type MyTypedefDecl = i32; });
3368 assert_rs_matches!(rs_api, quote! { pub type MyTypeAliasDecl = i32; });
3369 assert_rs_matches!(rs_api, quote! { pub type MyTypeAliasDecl_Alias = MyTypeAliasDecl; });
3370 assert_rs_matches!(rs_api, quote! { pub type S_Alias = S; });
3371 assert_rs_matches!(rs_api, quote! { pub type S_Alias_Alias = S_Alias; });
3372 assert_rs_matches!(rs_api, quote! { pub fn f(t: MyTypedefDecl) });
3373 assert_cc_matches!(
3374 generate_rs_api_impl(&ir)?,
3375 quote! {
3376 extern "C" void __rust_thunk___Z1fi(MyTypedefDecl t){ f (t) ; }
3377 }
3378 );
Googlerdcca7f72022-01-10 12:30:43 +00003379 Ok(())
3380 }
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00003381
3382 #[test]
3383 fn test_rs_type_kind_implements_copy() -> Result<()> {
Lukasz Anforowicz20651e32022-02-10 14:52:15 +00003384 let template = r#" LIFETIMES
Devin Jeanpierre88343c72022-01-15 01:10:23 +00003385 struct [[clang::trivial_abi]] TrivialStruct final { int i; };
3386 struct [[clang::trivial_abi]] UserDefinedCopyConstructor final {
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00003387 UserDefinedCopyConstructor(const UserDefinedCopyConstructor&);
3388 };
3389 using IntAlias = int;
3390 using TrivialAlias = TrivialStruct;
3391 using NonTrivialAlias = UserDefinedCopyConstructor;
3392 void func(PARAM_TYPE some_param);
3393 "#;
3394 assert_impl_all!(i32: Copy);
3395 assert_impl_all!(&i32: Copy);
3396 assert_not_impl_all!(&mut i32: Copy);
Lukasz Anforowicz20651e32022-02-10 14:52:15 +00003397 assert_impl_all!(Option<&i32>: Copy);
3398 assert_not_impl_all!(Option<&mut i32>: Copy);
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00003399 assert_impl_all!(*const i32: Copy);
3400 assert_impl_all!(*mut i32: Copy);
Lukasz Anforowicz20651e32022-02-10 14:52:15 +00003401 struct Test {
3402 // Test inputs:
3403 cc: &'static str,
3404 lifetimes: bool,
3405 // Expected test outputs:
3406 rs: &'static str,
3407 is_copy: bool,
3408 }
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00003409 let tests = vec![
3410 // Validity of the next few tests is verified via
3411 // `assert_[not_]impl_all!` static assertions above.
Lukasz Anforowicz20651e32022-02-10 14:52:15 +00003412 Test { cc: "int", lifetimes: true, rs: "i32", is_copy: true },
3413 Test { cc: "const int&", lifetimes: true, rs: "&'a i32", is_copy: true },
3414 Test { cc: "int&", lifetimes: true, rs: "&'a mut i32", is_copy: false },
3415 Test { cc: "const int*", lifetimes: true, rs: "Option<&'a i32>", is_copy: true },
3416 Test { cc: "int*", lifetimes: true, rs: "Option<&'a mut i32>", is_copy: false },
3417 Test { cc: "const int*", lifetimes: false, rs: "*const i32", is_copy: true },
3418 Test { cc: "int*", lifetimes: false, rs: "*mut i32", is_copy: true },
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00003419 // Tests below have been thought-through and verified "manually".
Lukasz Anforowicz20651e32022-02-10 14:52:15 +00003420 // TrivialStruct is expected to derive Copy.
3421 Test { cc: "TrivialStruct", lifetimes: true, rs: "TrivialStruct", is_copy: true },
3422 Test {
3423 cc: "UserDefinedCopyConstructor",
3424 lifetimes: true,
3425 rs: "UserDefinedCopyConstructor",
3426 is_copy: false,
3427 },
3428 Test { cc: "IntAlias", lifetimes: true, rs: "IntAlias", is_copy: true },
3429 Test { cc: "TrivialAlias", lifetimes: true, rs: "TrivialAlias", is_copy: true },
3430 Test { cc: "NonTrivialAlias", lifetimes: true, rs: "NonTrivialAlias", is_copy: false },
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00003431 ];
Lukasz Anforowicz20651e32022-02-10 14:52:15 +00003432 for test in tests.iter() {
3433 let test_name = format!("cc='{}', lifetimes={}", test.cc, test.lifetimes);
3434 let cc_input = template.replace("PARAM_TYPE", test.cc).replace(
3435 "LIFETIMES",
3436 if test.lifetimes { "#pragma clang lifetime_elision" } else { "" },
3437 );
3438 let ir = ir_from_cc(&cc_input)?;
Lukasz Anforowicz9c663ca2022-02-09 01:33:31 +00003439 let f = retrieve_func(&ir, "func");
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00003440 let t = RsTypeKind::new(&f.params[0].type_.rs_type, &ir)?;
Lukasz Anforowicz20651e32022-02-10 14:52:15 +00003441
Lukasz Anforowicz90bdb962022-02-14 21:07:45 +00003442 let lifetime_to_name: HashMap::<LifetimeId, String> = t.lifetimes().map(
3443 |lifetime_id| (lifetime_id, "a".to_string())).collect();
3444
Lukasz Anforowicz20651e32022-02-10 14:52:15 +00003445 let fmt = tokens_to_string(t.format(&ir, &lifetime_to_name)?)?;
3446 assert_eq!(test.rs, fmt, "Testing: {}", test_name);
3447
3448 assert_eq!(test.is_copy, t.implements_copy(), "Testing: {}", test_name);
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00003449 }
3450 Ok(())
3451 }
Lukasz Anforowicza94ab702022-01-14 22:40:25 +00003452
3453 #[test]
3454 fn test_rs_type_kind_is_shared_ref_to_with_lifetimes() -> Result<()> {
3455 let ir = ir_from_cc(
3456 "#pragma clang lifetime_elision
3457 struct SomeStruct {};
3458 void foo(const SomeStruct& foo_param);
3459 void bar(SomeStruct& bar_param);",
3460 )?;
3461 let record = ir.records().next().unwrap();
Lukasz Anforowicz9c663ca2022-02-09 01:33:31 +00003462 let foo_func = retrieve_func(&ir, "foo");
3463 let bar_func = retrieve_func(&ir, "bar");
Lukasz Anforowicza94ab702022-01-14 22:40:25 +00003464
3465 // const-ref + lifetimes in C++ ===> shared-ref in Rust
3466 assert_eq!(foo_func.params.len(), 1);
3467 let foo_param = &foo_func.params[0];
3468 assert_eq!(&foo_param.identifier.identifier, "foo_param");
3469 let foo_type = RsTypeKind::new(&foo_param.type_.rs_type, &ir)?;
3470 assert!(foo_type.is_shared_ref_to(record));
3471 assert!(matches!(foo_type, RsTypeKind::Reference { mutability: Mutability::Const, .. }));
3472
3473 // non-const-ref + lifetimes in C++ ===> mutable-ref in Rust
3474 assert_eq!(bar_func.params.len(), 1);
3475 let bar_param = &bar_func.params[0];
3476 assert_eq!(&bar_param.identifier.identifier, "bar_param");
3477 let bar_type = RsTypeKind::new(&bar_param.type_.rs_type, &ir)?;
3478 assert!(!bar_type.is_shared_ref_to(record));
3479 assert!(matches!(bar_type, RsTypeKind::Reference { mutability: Mutability::Mut, .. }));
3480
3481 Ok(())
3482 }
3483
3484 #[test]
3485 fn test_rs_type_kind_is_shared_ref_to_without_lifetimes() -> Result<()> {
3486 let ir = ir_from_cc(
3487 "struct SomeStruct {};
3488 void foo(const SomeStruct& foo_param);",
3489 )?;
3490 let record = ir.records().next().unwrap();
Lukasz Anforowicz9c663ca2022-02-09 01:33:31 +00003491 let foo_func = retrieve_func(&ir, "foo");
Lukasz Anforowicza94ab702022-01-14 22:40:25 +00003492
3493 // const-ref + *no* lifetimes in C++ ===> const-pointer in Rust
3494 assert_eq!(foo_func.params.len(), 1);
3495 let foo_param = &foo_func.params[0];
3496 assert_eq!(&foo_param.identifier.identifier, "foo_param");
3497 let foo_type = RsTypeKind::new(&foo_param.type_.rs_type, &ir)?;
3498 assert!(!foo_type.is_shared_ref_to(record));
3499 assert!(matches!(foo_type, RsTypeKind::Pointer { mutability: Mutability::Const, .. }));
3500
3501 Ok(())
3502 }
Marcel Hlopkoeaae9b72022-01-21 15:54:11 +00003503
3504 #[test]
Lukasz Anforowicz90bdb962022-02-14 21:07:45 +00003505 fn test_rs_type_kind_dfs_iter_ordering() {
3506 // Set up a test input representing: A<B<C>, D<E>>.
3507 let a = {
3508 let b = {
3509 let c = RsTypeKind::Other { name: "C", type_args: vec![] };
3510 RsTypeKind::Other { name: "B", type_args: vec![c] }
3511 };
3512 let d = {
3513 let e = RsTypeKind::Other { name: "E", type_args: vec![] };
3514 RsTypeKind::Other { name: "D", type_args: vec![e] }
3515 };
3516 RsTypeKind::Other { name: "A", type_args: vec![b, d] }
3517 };
3518 let dfs_names = a
3519 .dfs_iter()
3520 .map(|t| match t {
3521 RsTypeKind::Other { name, .. } => *name,
3522 _ => unreachable!("Only 'other' types are used in this test"),
3523 })
3524 .collect_vec();
3525 assert_eq!(vec!["A", "B", "C", "D", "E"], dfs_names);
3526 }
3527
3528 #[test]
Lukasz Anforowiczcf230fd2022-02-18 19:20:39 +00003529 fn test_rs_type_kind_dfs_iter_ordering_for_func_ptr() {
3530 // Set up a test input representing: fn(A, B) -> C
3531 let f = {
3532 let a = RsTypeKind::Other { name: "A", type_args: vec![] };
3533 let b = RsTypeKind::Other { name: "B", type_args: vec![] };
3534 let c = RsTypeKind::Other { name: "C", type_args: vec![] };
3535 RsTypeKind::FuncPtr { abi: "blah", param_types: vec![a, b], return_type: Box::new(c) }
3536 };
3537 let dfs_names = f
3538 .dfs_iter()
3539 .map(|t| match t {
3540 RsTypeKind::FuncPtr { .. } => "fn",
3541 RsTypeKind::Other { name, .. } => *name,
3542 _ => unreachable!("Only FuncPtr and Other kinds are used in this test"),
3543 })
3544 .collect_vec();
3545 assert_eq!(vec!["fn", "A", "B", "C"], dfs_names);
3546 }
3547
3548 #[test]
Lukasz Anforowicz90bdb962022-02-14 21:07:45 +00003549 fn test_rs_type_kind_lifetimes() -> Result<()> {
3550 let ir = ir_from_cc(
3551 r#"
3552 #pragma clang lifetime_elision
3553 using TypeAlias = int&;
3554 struct SomeStruct {};
3555 void foo(int a, int& b, int* c, int** d, TypeAlias e, SomeStruct f); "#,
3556 )?;
3557 let f = retrieve_func(&ir, "foo");
3558 let ret = RsTypeKind::new(&f.return_type.rs_type, &ir)?;
3559 let a = RsTypeKind::new(&f.params[0].type_.rs_type, &ir)?;
3560 let b = RsTypeKind::new(&f.params[1].type_.rs_type, &ir)?;
3561 let c = RsTypeKind::new(&f.params[2].type_.rs_type, &ir)?;
3562 let d = RsTypeKind::new(&f.params[3].type_.rs_type, &ir)?;
3563 let e = RsTypeKind::new(&f.params[4].type_.rs_type, &ir)?;
3564 let f = RsTypeKind::new(&f.params[5].type_.rs_type, &ir)?;
3565
3566 assert_eq!(0, ret.lifetimes().count()); // No lifetimes on `void`.
3567 assert_eq!(0, a.lifetimes().count()); // No lifetimes on `int`.
3568 assert_eq!(1, b.lifetimes().count()); // `&'a i32` has a single lifetime.
3569 assert_eq!(1, c.lifetimes().count()); // `Option<&'b i32>` has a single lifetime.
3570 assert_eq!(2, d.lifetimes().count()); // `&'c Option<&'d i32>` has two lifetimes.
3571 assert_eq!(1, e.lifetimes().count()); // Lifetime of underlying type should show through.
3572 assert_eq!(0, f.lifetimes().count()); // No lifetimes on structs (yet).
3573 Ok(())
3574 }
3575
3576 #[test]
3577 fn test_rs_type_kind_lifetimes_raw_ptr() -> Result<()> {
3578 let ir = ir_from_cc("void foo(int* a);")?;
3579 let f = retrieve_func(&ir, "foo");
3580 let a = RsTypeKind::new(&f.params[0].type_.rs_type, &ir)?;
3581 assert_eq!(0, a.lifetimes().count()); // No lifetimes on `int*`.
3582 Ok(())
3583 }
3584
3585 #[test]
Marcel Hlopkoeaae9b72022-01-21 15:54:11 +00003586 fn test_rust_keywords_are_escaped_in_rs_api_file() -> Result<()> {
3587 let ir = ir_from_cc("struct type { int dyn; };")?;
3588 let rs_api = generate_rs_api(&ir)?;
3589 assert_rs_matches!(rs_api, quote! { struct r#type { ... r#dyn: i32 ... } });
3590 Ok(())
3591 }
3592
3593 #[test]
3594 fn test_rust_keywords_are_not_escaped_in_rs_api_impl_file() -> Result<()> {
3595 let ir = ir_from_cc("struct type { int dyn; };")?;
3596 let rs_api_impl = generate_rs_api_impl(&ir)?;
3597 assert_cc_matches!(rs_api_impl, quote! { static_assert(offsetof(class type, dyn) ... ) });
3598 Ok(())
3599 }
Marcel Hlopko14ee3c82022-02-09 09:46:23 +00003600
3601 #[test]
3602 fn test_no_aligned_attr() {
3603 let ir = ir_from_cc("struct SomeStruct {};").unwrap();
3604 let rs_api = generate_rs_api(&ir).unwrap();
3605
3606 assert_rs_matches! {rs_api, quote! {
3607 #[repr(C)]
3608 pub struct SomeStruct { ... }
3609 }};
3610 }
3611
3612 #[test]
3613 fn test_aligned_attr() {
3614 let ir = ir_from_cc("struct SomeStruct {} __attribute__((aligned(64)));").unwrap();
3615 let rs_api = generate_rs_api(&ir).unwrap();
3616
3617 assert_rs_matches! {rs_api, quote! {
3618 #[repr(C, align(64))]
3619 pub struct SomeStruct { ... }
3620 }
3621 };
3622 }
Devin Jeanpierre149950d2022-02-22 21:02:02 +00003623
3624 /// !Unpin references should not be pinned.
3625 #[test]
3626 fn test_nonunpin_ref_param() -> Result<()> {
3627 let rs_api_impl = generate_rs_api(&ir_from_cc(
3628 r#"
3629 #pragma clang lifetime_elision
3630 struct S {~S();};
3631 void Function(const S& s);
3632 "#,
3633 )?)?;
3634 assert_rs_matches!(
3635 rs_api_impl,
3636 quote! {
3637 fn Function<'a>(s: &'a S) { ... }
3638 }
3639 );
3640 Ok(())
3641 }
3642
3643 /// !Unpin mut references must be pinned.
3644 #[test]
3645 fn test_nonunpin_mut_param() -> Result<()> {
3646 let rs_api_impl = generate_rs_api(&ir_from_cc(
3647 r#"
3648 #pragma clang lifetime_elision
3649 struct S {~S();};
3650 void Function(S& s);
3651 "#,
3652 )?)?;
3653 assert_rs_matches!(
3654 rs_api_impl,
3655 quote! {
3656 fn Function<'a>(s: std::pin::Pin<&'a mut S>) { ... }
3657 }
3658 );
3659 Ok(())
3660 }
3661
3662 /// !Unpin &self should not be pinned.
3663 #[test]
3664 fn test_nonunpin_ref_self() -> Result<()> {
3665 let rs_api_impl = generate_rs_api(&ir_from_cc(
3666 r#"
3667 #pragma clang lifetime_elision
3668 struct S {
3669 ~S();
3670 void Function() const;
3671 };
3672 "#,
3673 )?)?;
3674 assert_rs_matches!(
3675 rs_api_impl,
3676 quote! {
3677 fn Function<'a>(&'a self) { ... }
3678 }
3679 );
3680 Ok(())
3681 }
3682
3683 /// !Unpin &mut self must be pinned.
3684 #[test]
3685 fn test_nonunpin_mut_self() -> Result<()> {
3686 let rs_api_impl = generate_rs_api(&ir_from_cc(
3687 r#"
3688 #pragma clang lifetime_elision
3689 struct S {
3690 ~S();
3691 void Function();
3692 };
3693 "#,
3694 )?)?;
3695 assert_rs_matches!(
3696 rs_api_impl,
3697 quote! {
3698 fn Function<'a>(self: std::pin::Pin<&'a mut Self>) { ... }
3699 }
3700 );
3701 Ok(())
3702 }
3703
3704 /// Drop::drop must not use self : Pin<...>.
3705 #[test]
3706 fn test_nonunpin_drop() -> Result<()> {
3707 let rs_api_impl = generate_rs_api(&ir_from_cc(
3708 r#"
3709 struct S {~S();};
3710 "#,
3711 )?)?;
3712 assert_rs_matches!(
3713 rs_api_impl,
3714 quote! {
3715 fn drop(&mut self) { ... }
3716 }
3717 );
3718 Ok(())
3719 }
Marcel Hlopko42abfc82021-08-09 07:03:17 +00003720}