blob: 7c846de9f40cf361650d05b65885bb69adde76f7 [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(|| {
582 format!("Failed to format `__this` param for a thunk: {:?}", func.params[0])
583 })?;
Lukasz Anforowicz40c2eb82022-01-11 18:22:31 +0000584 }
585
Lukasz Anforowicz95551272022-01-20 00:02:24 +0000586 let lifetimes = func.lifetime_params.iter().map(|l| format_lifetime_name(&l.name));
587 let generic_params = format_generic_params(lifetimes);
588
Devin Jeanpierre7e9a1de2021-12-03 08:04:22 +0000589 quote! {
590 #thunk_attr
Lukasz Anforowicz4ad012b2021-12-15 18:13:40 +0000591 pub(crate) fn #thunk_ident #generic_params( #( #param_idents: #param_types ),*
592 ) #return_type_fragment ;
Devin Jeanpierre7e9a1de2021-12-03 08:04:22 +0000593 }
Michael Forstered642022021-10-04 09:48:25 +0000594 };
595
Lukasz Anforowiczeb19ac62022-02-05 00:10:16 +0000596 Ok(GeneratedFunc::Some { api_func: api_func.into(), thunk: thunk.into(), function_id })
Michael Forstered642022021-10-04 09:48:25 +0000597}
598
Michael Forstercc5941a2021-10-07 07:12:24 +0000599fn generate_doc_comment(comment: &Option<String>) -> TokenStream {
600 match comment {
Michael Forster028800b2021-10-05 12:39:59 +0000601 Some(text) => {
Marcel Hlopko89547752021-12-10 09:39:41 +0000602 // token_stream_printer (and rustfmt) don't put a space between /// and the doc
603 // comment, let's add it here so our comments are pretty.
Lukasz Anforowiczdd9ae0f2022-02-17 15:52:53 +0000604 let doc = format!(" {}", text.replace('\n', "\n "));
Michael Forster028800b2021-10-05 12:39:59 +0000605 quote! {#[doc=#doc]}
606 }
607 None => quote! {},
Michael Forstercc5941a2021-10-07 07:12:24 +0000608 }
609}
Lukasz Anforowiczdaff0402021-12-23 00:37:50 +0000610
611fn format_generic_params<T: quote::ToTokens>(params: impl IntoIterator<Item = T>) -> TokenStream {
612 let mut params = params.into_iter().peekable();
613 if params.peek().is_none() {
614 quote! {}
615 } else {
616 quote! { < #( #params ),* > }
617 }
618}
619
Lukasz Anforowicze57215c2022-01-12 14:54:16 +0000620fn should_implement_drop(record: &Record) -> bool {
621 match record.destructor.definition {
622 // TODO(b/202258760): Only omit destructor if `Copy` is specified.
623 SpecialMemberDefinition::Trivial => false,
624
625 // TODO(b/212690698): Avoid calling into the C++ destructor (e.g. let
626 // Rust drive `drop`-ing) to avoid (somewhat unergonomic) ManuallyDrop
627 // if we can ask Rust to preserve C++ field destruction order in
628 // NontrivialMembers case.
629 SpecialMemberDefinition::NontrivialMembers => true,
630
631 // The `impl Drop` for NontrivialUserDefined needs to call into the
632 // user-defined destructor on C++ side.
633 SpecialMemberDefinition::NontrivialUserDefined => true,
634
635 // TODO(b/213516512): Today the IR doesn't contain Func entries for
636 // deleted functions/destructors/etc. But, maybe we should generate
637 // `impl Drop` in this case? With `unreachable!`? With
638 // `std::mem::forget`?
639 SpecialMemberDefinition::Deleted => false,
640 }
641}
642
643/// Returns whether fields of type `ty` need to be wrapped in `ManuallyDrop<T>`
644/// to prevent the fields from being destructed twice (once by the C++
645/// destructor calkled from the `impl Drop` of the struct and once by `drop` on
646/// the Rust side).
647///
648/// A type is safe to destroy twice if it implements `Copy`. Fields of such
649/// don't need to be wrapped in `ManuallyDrop<T>` even if the struct
650/// containing the fields provides an `impl Drop` that calles into a C++
651/// destructor (in addition to dropping the fields on the Rust side).
652///
653/// Note that it is not enough to just be `!needs_drop<T>()`: Rust only
654/// guarantees that it is safe to use-after-destroy for `Copy` types. See
655/// e.g. the documentation for
656/// [`drop_in_place`](https://doc.rust-lang.org/std/ptr/fn.drop_in_place.html):
657///
658/// > if `T` is not `Copy`, using the pointed-to value after calling
659/// > `drop_in_place` can cause undefined behavior
660fn needs_manually_drop(ty: &ir::RsType, ir: &IR) -> Result<bool> {
661 let ty_implements_copy = RsTypeKind::new(ty, ir)?.implements_copy();
662 Ok(!ty_implements_copy)
663}
664
Michael Forsterbee84482021-10-13 08:35:38 +0000665/// Generates Rust source code for a given `Record` and associated assertions as
666/// a tuple.
Marcel Hlopkoc0956cf2021-11-29 08:31:28 +0000667fn generate_record(record: &Record, ir: &IR) -> Result<(RsSnippet, RsSnippet)> {
Marcel Hlopkoeaae9b72022-01-21 15:54:11 +0000668 let ident = make_rs_ident(&record.identifier.identifier);
Michael Forstercc5941a2021-10-07 07:12:24 +0000669 let doc_comment = generate_doc_comment(&record.doc_comment);
Marcel Hlopkob4b28742021-09-15 12:45:20 +0000670 let field_idents =
Marcel Hlopkoeaae9b72022-01-21 15:54:11 +0000671 record.fields.iter().map(|f| make_rs_ident(&f.identifier.identifier)).collect_vec();
Michael Forstercc5941a2021-10-07 07:12:24 +0000672 let field_doc_coments =
673 record.fields.iter().map(|f| generate_doc_comment(&f.doc_comment)).collect_vec();
Devin Jeanpierre09c6f452021-09-29 07:34:24 +0000674 let field_types = record
675 .fields
676 .iter()
Devin Jeanpierreb69bcae2022-02-03 09:45:50 +0000677 .enumerate()
678 .map(|(i, f)| {
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +0000679 // [[no_unique_address]] fields are replaced by an unaligned block of memory
680 // which fills space up to the next field.
Devin Jeanpierreb69bcae2022-02-03 09:45:50 +0000681 // See: docs/struct_layout
682 if f.is_no_unique_address {
683 let next_offset = if let Some(next) = record.fields.get(i + 1) {
684 next.offset
685 } else {
686 record.size * 8
687 };
688 let width = Literal::usize_unsuffixed((next_offset - f.offset) / 8);
689 return Ok(quote! {[std::mem::MaybeUninit<u8>; #width]});
690 }
Lukasz Anforowicze57215c2022-01-12 14:54:16 +0000691 let mut formatted = format_rs_type(&f.type_.rs_type, ir, &HashMap::new())
692 .with_context(|| {
Googlerb7e361d2022-01-04 14:02:59 +0000693 format!("Failed to format type for field {:?} on record {:?}", f, record)
694 })?;
Lukasz Anforowicze57215c2022-01-12 14:54:16 +0000695 // TODO(b/212696226): Verify cases where ManuallyDrop<T> is skipped
696 // via static asserts in the generated code.
697 if should_implement_drop(record) && needs_manually_drop(&f.type_.rs_type, ir)? {
698 // TODO(b/212690698): Avoid (somewhat unergonomic) ManuallyDrop
699 // if we can ask Rust to preserve field destruction order if the
700 // destructor is the SpecialMemberDefinition::NontrivialMembers
701 // case.
702 formatted = quote! { std::mem::ManuallyDrop<#formatted> }
Lukasz Anforowicz6d553632022-01-06 21:36:14 +0000703 };
Devin Jeanpierre7e9a1de2021-12-03 08:04:22 +0000704 Ok(formatted)
705 })
Devin Jeanpierre09c6f452021-09-29 07:34:24 +0000706 .collect::<Result<Vec<_>>>()?;
Googlerec589eb2021-09-17 07:45:39 +0000707 let field_accesses = record
708 .fields
709 .iter()
710 .map(|f| {
Devin Jeanpierreb69bcae2022-02-03 09:45:50 +0000711 if f.access == AccessSpecifier::Public && !f.is_no_unique_address {
Googlerec589eb2021-09-17 07:45:39 +0000712 quote! { pub }
713 } else {
714 quote! {}
715 }
716 })
717 .collect_vec();
Googlerec648ff2021-09-23 07:19:53 +0000718 let size = record.size;
719 let alignment = record.alignment;
Googleraaa0a532021-10-01 09:11:27 +0000720 let field_assertions =
721 record.fields.iter().zip(field_idents.iter()).map(|(field, field_ident)| {
722 let offset = field.offset;
723 quote! {
724 // The IR contains the offset in bits, while offset_of!()
725 // returns the offset in bytes, so we need to convert.
Googler209b10a2021-12-06 09:11:57 +0000726 const _: () = assert!(offset_of!(#ident, #field_ident) * 8 == #offset);
Googleraaa0a532021-10-01 09:11:27 +0000727 }
728 });
Michael Forsterdb8101a2021-10-08 06:56:03 +0000729 let mut record_features = BTreeSet::new();
730 let mut assertion_features = BTreeSet::new();
Devin Jeanpierre273eeae2021-10-06 13:29:35 +0000731
732 // TODO(mboehme): For the time being, we're using unstable features to
733 // be able to use offset_of!() in static assertions. This is fine for a
734 // prototype, but longer-term we want to either get those features
735 // stabilized or find an alternative. For more details, see
736 // b/200120034#comment15
Marcel Hlopkoeaae9b72022-01-21 15:54:11 +0000737 assertion_features.insert(make_rs_ident("const_ptr_offset_from"));
Devin Jeanpierre2ed14ec2021-10-06 11:32:19 +0000738
Lukasz Anforowicze57215c2022-01-12 14:54:16 +0000739 let derives = generate_derives(record);
Devin Jeanpierre9227d2c2021-10-06 12:26:05 +0000740 let derives = if derives.is_empty() {
741 quote! {}
742 } else {
743 quote! {#[derive( #(#derives),* )]}
744 };
Lukasz Anforowiczdd9ae0f2022-02-17 15:52:53 +0000745 let unpin_impl = if record.is_unpin() {
746 quote! {}
Devin Jeanpierreea700d32021-10-06 11:33:56 +0000747 } else {
Michael Forsterbee84482021-10-13 08:35:38 +0000748 // negative_impls are necessary for universal initialization due to Rust's
749 // coherence rules: PhantomPinned isn't enough to prove to Rust that a
750 // blanket impl that requires Unpin doesn't apply. See http://<internal link>=h.f6jp8ifzgt3n
Marcel Hlopkoeaae9b72022-01-21 15:54:11 +0000751 record_features.insert(make_rs_ident("negative_impls"));
Lukasz Anforowiczdd9ae0f2022-02-17 15:52:53 +0000752 quote! {
Michael Forsterdb8101a2021-10-08 06:56:03 +0000753 __NEWLINE__ __NEWLINE__
754 impl !Unpin for #ident {}
Lukasz Anforowiczdd9ae0f2022-02-17 15:52:53 +0000755 }
756 };
Devin Jeanpierre273eeae2021-10-06 13:29:35 +0000757
Devin Jeanpierrec80e6242022-02-03 01:56:40 +0000758 let mut repr_attributes = vec![quote! {C}];
759 if record.override_alignment && record.alignment > 1 {
760 let alignment = Literal::usize_unsuffixed(record.alignment);
761 repr_attributes.push(quote! {align(#alignment)});
762 }
763
764 // Adjust the struct to also include base class subobjects. We use an opaque
765 // field because subobjects can live in the alignment of base class
766 // subobjects.
767 let base_subobjects_field = if let Some(base_size) = record.base_size {
768 let n = proc_macro2::Literal::usize_unsuffixed(base_size);
769 quote! {
770 __base_class_subobjects: [std::mem::MaybeUninit<u8>; #n],
771 }
772 } else {
773 quote! {}
774 };
775
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +0000776 let empty_struct_placeholder_field =
777 if record.fields.is_empty() && record.base_size.unwrap_or(0) == 0 {
778 quote! {
779 /// Prevent empty C++ struct being zero-size in Rust.
780 placeholder: std::mem::MaybeUninit<u8>,
781 }
782 } else {
783 quote! {}
784 };
Googlerf4792062021-10-20 07:21:21 +0000785
Devin Jeanpierre58181ac2022-02-14 21:30:05 +0000786 let no_unique_address_accessors = cc_struct_no_unique_address_impl(record, ir)?;
Devin Jeanpierre56777022022-02-03 01:57:15 +0000787 let base_class_into = cc_struct_upcast_impl(record, ir)?;
788
Michael Forsterdb8101a2021-10-08 06:56:03 +0000789 let record_tokens = quote! {
Michael Forster028800b2021-10-05 12:39:59 +0000790 #doc_comment
Devin Jeanpierre9227d2c2021-10-06 12:26:05 +0000791 #derives
Devin Jeanpierrec80e6242022-02-03 01:56:40 +0000792 #[repr(#( #repr_attributes ),*)]
Marcel Hlopkob4b28742021-09-15 12:45:20 +0000793 pub struct #ident {
Devin Jeanpierrec80e6242022-02-03 01:56:40 +0000794 #base_subobjects_field
Michael Forstercc5941a2021-10-07 07:12:24 +0000795 #( #field_doc_coments #field_accesses #field_idents: #field_types, )*
Googlerf4792062021-10-20 07:21:21 +0000796 #empty_struct_placeholder_field
Marcel Hlopkob4b28742021-09-15 12:45:20 +0000797 }
Googlerec648ff2021-09-23 07:19:53 +0000798
Devin Jeanpierre58181ac2022-02-14 21:30:05 +0000799 #no_unique_address_accessors
800
Devin Jeanpierre56777022022-02-03 01:57:15 +0000801 #base_class_into
802
Devin Jeanpierreea700d32021-10-06 11:33:56 +0000803 #unpin_impl
Devin Jeanpierre273eeae2021-10-06 13:29:35 +0000804 };
805
Michael Forsterdb8101a2021-10-08 06:56:03 +0000806 let assertion_tokens = quote! {
Googler209b10a2021-12-06 09:11:57 +0000807 const _: () = assert!(std::mem::size_of::<#ident>() == #size);
808 const _: () = assert!(std::mem::align_of::<#ident>() == #alignment);
Michael Forsterdb8101a2021-10-08 06:56:03 +0000809 #( #field_assertions )*
810 };
811
812 Ok((
813 RsSnippet { features: record_features, tokens: record_tokens },
814 RsSnippet { features: assertion_features, tokens: assertion_tokens },
815 ))
Marcel Hlopkob4b28742021-09-15 12:45:20 +0000816}
817
Lukasz Anforowicz2e41bb62022-01-11 18:23:07 +0000818fn should_derive_clone(record: &Record) -> bool {
Devin Jeanpierre88343c72022-01-15 01:10:23 +0000819 record.is_unpin()
Devin Jeanpierre2ed14ec2021-10-06 11:32:19 +0000820 && record.copy_constructor.access == ir::AccessSpecifier::Public
821 && record.copy_constructor.definition == SpecialMemberDefinition::Trivial
Lukasz Anforowicz2e41bb62022-01-11 18:23:07 +0000822}
823
Lukasz Anforowicze57215c2022-01-12 14:54:16 +0000824fn should_derive_copy(record: &Record) -> bool {
825 // TODO(b/202258760): Make `Copy` inclusion configurable.
826 should_derive_clone(record)
827}
828
829fn generate_derives(record: &Record) -> Vec<Ident> {
830 let mut derives = vec![];
Lukasz Anforowicz2e41bb62022-01-11 18:23:07 +0000831 if should_derive_clone(record) {
Marcel Hlopkoeaae9b72022-01-21 15:54:11 +0000832 derives.push(make_rs_ident("Clone"));
Devin Jeanpierre2ed14ec2021-10-06 11:32:19 +0000833 }
Lukasz Anforowicze57215c2022-01-12 14:54:16 +0000834 if should_derive_copy(record) {
Marcel Hlopkoeaae9b72022-01-21 15:54:11 +0000835 derives.push(make_rs_ident("Copy"));
Lukasz Anforowicze57215c2022-01-12 14:54:16 +0000836 }
837 derives
Devin Jeanpierre2ed14ec2021-10-06 11:32:19 +0000838}
839
Googler6a0a5252022-01-11 14:08:09 +0000840fn generate_type_alias(type_alias: &TypeAlias, ir: &IR) -> Result<TokenStream> {
Marcel Hlopkoeaae9b72022-01-21 15:54:11 +0000841 let ident = make_rs_ident(&type_alias.identifier.identifier);
Googler6a0a5252022-01-11 14:08:09 +0000842 let underlying_type = format_rs_type(&type_alias.underlying_type.rs_type, ir, &HashMap::new())
843 .with_context(|| format!("Failed to format underlying type for {:?}", type_alias))?;
844 Ok(quote! {pub type #ident = #underlying_type;})
845}
846
Michael Forster523dbd42021-10-12 11:05:44 +0000847/// Generates Rust source code for a given `UnsupportedItem`.
848fn generate_unsupported(item: &UnsupportedItem) -> Result<TokenStream> {
Googler48a74dd2021-10-25 07:31:53 +0000849 let location = if item.source_loc.filename.is_empty() {
850 "<unknown location>".to_string()
851 } else {
852 // TODO(forster): The "google3" prefix should probably come from a command line
853 // argument.
854 // TODO(forster): Consider linking to the symbol instead of to the line number
855 // to avoid wrong links while generated files have not caught up.
856 format!("google3/{};l={}", &item.source_loc.filename, &item.source_loc.line)
857 };
Michael Forster6a184ad2021-10-12 13:04:05 +0000858 let message = format!(
Googler48a74dd2021-10-25 07:31:53 +0000859 "{}\nError while generating bindings for item '{}':\n{}",
860 &location, &item.name, &item.message
Michael Forster6a184ad2021-10-12 13:04:05 +0000861 );
Michael Forster523dbd42021-10-12 11:05:44 +0000862 Ok(quote! { __COMMENT__ #message })
863}
864
Michael Forsterf1dce422021-10-13 09:50:16 +0000865/// Generates Rust source code for a given `Comment`.
866fn generate_comment(comment: &Comment) -> Result<TokenStream> {
867 let text = &comment.text;
868 Ok(quote! { __COMMENT__ #text })
869}
870
Marcel Hlopko89547752021-12-10 09:39:41 +0000871fn generate_rs_api(ir: &IR) -> Result<TokenStream> {
Michael Forstered642022021-10-04 09:48:25 +0000872 let mut items = vec![];
Marcel Hlopko42abfc82021-08-09 07:03:17 +0000873 let mut thunks = vec![];
Michael Forsterdb8101a2021-10-08 06:56:03 +0000874 let mut assertions = vec![];
Marcel Hlopko42abfc82021-08-09 07:03:17 +0000875
Googler454f2652021-12-06 12:53:12 +0000876 // We import nullable pointers as an Option<&T> and assume that at the ABI
877 // level, None is represented as a zero pointer value whereas Some is
878 // represented as as non-zero pointer value. This seems like a pretty safe
879 // assumption to make, but to provide some safeguard, assert that
880 // `Option<&i32>` and `&i32` have the same size.
881 assertions.push(quote! {
882 const _: () = assert!(std::mem::size_of::<Option<&i32>>() == std::mem::size_of::<&i32>());
883 });
884
Michael Forsterbee84482021-10-13 08:35:38 +0000885 // TODO(jeanpierreda): Delete has_record, either in favor of using RsSnippet, or not
886 // having uses. See https://chat.google.com/room/AAAAnQmj8Qs/6QbkSvWcfhA
Devin Jeanpierreea700d32021-10-06 11:33:56 +0000887 let mut has_record = false;
Devin Jeanpierre273eeae2021-10-06 13:29:35 +0000888 let mut features = BTreeSet::new();
Marcel Hlopko42abfc82021-08-09 07:03:17 +0000889
Lukasz Anforowicz72c4d222022-02-18 19:07:28 +0000890 // For #![rustfmt::skip].
891 features.insert(make_rs_ident("custom_inner_attributes"));
892
Googlerd03d05b2022-01-07 10:10:57 +0000893 // Identify all functions having overloads that we can't import (yet).
894 // TODO(b/213280424): Implement support for overloaded functions.
895 let mut seen_funcs = HashSet::new();
896 let mut overloaded_funcs = HashSet::new();
897 for func in ir.functions() {
Lukasz Anforowiczeb19ac62022-02-05 00:10:16 +0000898 if let GeneratedFunc::Some { function_id, .. } = generate_func(func, ir)? {
Googlerd03d05b2022-01-07 10:10:57 +0000899 if !seen_funcs.insert(function_id.clone()) {
900 overloaded_funcs.insert(function_id);
901 }
902 }
903 }
904
Marcel Hlopko3b9bf9e2021-11-29 08:25:14 +0000905 for item in ir.items() {
Michael Forstered642022021-10-04 09:48:25 +0000906 match item {
Lukasz Anforowiczeb19ac62022-02-05 00:10:16 +0000907 Item::Func(func) => match generate_func(func, ir)? {
908 GeneratedFunc::None => (),
909 GeneratedFunc::Unsupported(unsupported) => {
910 items.push(generate_unsupported(&unsupported)?)
911 }
912 GeneratedFunc::Some { api_func, thunk, function_id } => {
Googlerd03d05b2022-01-07 10:10:57 +0000913 if overloaded_funcs.contains(&function_id) {
Lukasz Anforowiczeb19ac62022-02-05 00:10:16 +0000914 items.push(generate_unsupported(&make_unsupported_fn(
915 func,
916 ir,
917 "Cannot generate bindings for overloaded function",
918 )?)?);
Googlerd03d05b2022-01-07 10:10:57 +0000919 continue;
920 }
Lukasz Anforowiczeb19ac62022-02-05 00:10:16 +0000921 features.extend(api_func.features);
Googlerd03d05b2022-01-07 10:10:57 +0000922 features.extend(thunk.features);
Lukasz Anforowiczeb19ac62022-02-05 00:10:16 +0000923 items.push(api_func.tokens);
Googlerd03d05b2022-01-07 10:10:57 +0000924 thunks.push(thunk.tokens);
925 }
Lukasz Anforowiczeb19ac62022-02-05 00:10:16 +0000926 },
Michael Forstered642022021-10-04 09:48:25 +0000927 Item::Record(record) => {
Googler6a0a5252022-01-11 14:08:09 +0000928 if !ir.is_current_target(&record.owning_target)
929 && !ir.is_stdlib_target(&record.owning_target)
930 {
Marcel Hlopkoa0f38662021-12-03 08:45:26 +0000931 continue;
932 }
Marcel Hlopkoc0956cf2021-11-29 08:31:28 +0000933 let (snippet, assertions_snippet) = generate_record(record, ir)?;
Devin Jeanpierre273eeae2021-10-06 13:29:35 +0000934 features.extend(snippet.features);
Michael Forsterdb8101a2021-10-08 06:56:03 +0000935 features.extend(assertions_snippet.features);
Devin Jeanpierre273eeae2021-10-06 13:29:35 +0000936 items.push(snippet.tokens);
Michael Forsterdb8101a2021-10-08 06:56:03 +0000937 assertions.push(assertions_snippet.tokens);
Devin Jeanpierreea700d32021-10-06 11:33:56 +0000938 has_record = true;
Michael Forstered642022021-10-04 09:48:25 +0000939 }
Googler098c4582022-01-10 12:29:34 +0000940 Item::TypeAlias(type_alias) => {
Googler6a0a5252022-01-11 14:08:09 +0000941 if !ir.is_current_target(&type_alias.owning_target)
942 && !ir.is_stdlib_target(&type_alias.owning_target)
943 {
944 continue;
945 }
946 items.push(generate_type_alias(type_alias, ir)?);
Googler098c4582022-01-10 12:29:34 +0000947 }
Michael Forster523dbd42021-10-12 11:05:44 +0000948 Item::UnsupportedItem(unsupported) => items.push(generate_unsupported(unsupported)?),
Michael Forsterf1dce422021-10-13 09:50:16 +0000949 Item::Comment(comment) => items.push(generate_comment(comment)?),
Michael Forstered642022021-10-04 09:48:25 +0000950 }
Marcel Hlopko42abfc82021-08-09 07:03:17 +0000951 }
952
Marcel Hlopkob4b28742021-09-15 12:45:20 +0000953 let mod_detail = if thunks.is_empty() {
954 quote! {}
955 } else {
956 quote! {
957 mod detail {
Googler55647142022-01-11 12:37:39 +0000958 #[allow(unused_imports)]
Devin Jeanpierred4dde0e2021-10-13 20:48:25 +0000959 use super::*;
Marcel Hlopkob4b28742021-09-15 12:45:20 +0000960 extern "C" {
961 #( #thunks )*
962 }
Marcel Hlopko42abfc82021-08-09 07:03:17 +0000963 }
964 }
965 };
966
Devin Jeanpierreea700d32021-10-06 11:33:56 +0000967 let imports = if has_record {
Googlerec648ff2021-09-23 07:19:53 +0000968 quote! {
Googleraaa0a532021-10-01 09:11:27 +0000969 use memoffset_unstable_const::offset_of;
Googlerec648ff2021-09-23 07:19:53 +0000970 }
Michael Forstered642022021-10-04 09:48:25 +0000971 } else {
972 quote! {}
Googlerec648ff2021-09-23 07:19:53 +0000973 };
974
Devin Jeanpierre273eeae2021-10-06 13:29:35 +0000975 let features = if features.is_empty() {
976 quote! {}
977 } else {
978 quote! {
979 #![feature( #(#features),* )]
980 }
981 };
982
Marcel Hlopko89547752021-12-10 09:39:41 +0000983 Ok(quote! {
Googler55647142022-01-11 12:37:39 +0000984 #features __NEWLINE__
985 #![allow(non_camel_case_types)] __NEWLINE__
986 #![allow(non_snake_case)] __NEWLINE__ __NEWLINE__
987
Michael Forsterdb8101a2021-10-08 06:56:03 +0000988 #imports __NEWLINE__ __NEWLINE__
Googlerec648ff2021-09-23 07:19:53 +0000989
Michael Forsterdb8101a2021-10-08 06:56:03 +0000990 #( #items __NEWLINE__ __NEWLINE__ )*
Marcel Hlopkob4b28742021-09-15 12:45:20 +0000991
Michael Forsterdb8101a2021-10-08 06:56:03 +0000992 #mod_detail __NEWLINE__ __NEWLINE__
993
994 #( #assertions __NEWLINE__ __NEWLINE__ )*
Marcel Hlopko89547752021-12-10 09:39:41 +0000995 })
Marcel Hlopko42abfc82021-08-09 07:03:17 +0000996}
997
Marcel Hlopkoeaae9b72022-01-21 15:54:11 +0000998/// Makes an 'Ident' to be used in the Rust source code. Escapes Rust keywords.
999fn make_rs_ident(ident: &str) -> Ident {
1000 // TODO(https://github.com/dtolnay/syn/pull/1098): Remove the hardcoded list once syn recognizes
1001 // 2018 and 2021 keywords.
1002 if ["async", "await", "try", "dyn"].contains(&ident) {
1003 return format_ident!("r#{}", ident);
1004 }
1005 match syn::parse_str::<syn::Ident>(ident) {
1006 Ok(_) => format_ident!("{}", ident),
1007 Err(_) => format_ident!("r#{}", ident),
1008 }
1009}
1010
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +00001011/// Formats a C++ identifier. Does not escape C++ keywords.
1012fn format_cc_ident(ident: &str) -> TokenStream {
1013 ident.parse().unwrap()
Marcel Hlopko42abfc82021-08-09 07:03:17 +00001014}
1015
Googler6a0a5252022-01-11 14:08:09 +00001016fn rs_type_name_for_target_and_identifier(
1017 owning_target: &BlazeLabel,
1018 identifier: &ir::Identifier,
1019 ir: &IR,
1020) -> Result<TokenStream> {
Marcel Hlopkoeaae9b72022-01-21 15:54:11 +00001021 let ident = make_rs_ident(identifier.identifier.as_str());
Googler6a0a5252022-01-11 14:08:09 +00001022
1023 if ir.is_current_target(owning_target) || ir.is_stdlib_target(owning_target) {
1024 Ok(quote! {#ident})
1025 } else {
Marcel Hlopkod906b892022-01-27 08:52:36 +00001026 let owning_crate_name = owning_target.target_name()?;
1027 // TODO(b/216587072): Remove this hacky escaping and use the import! macro once
1028 // available
Lukasz Anforowiczdd9ae0f2022-02-17 15:52:53 +00001029 let escaped_owning_crate_name = owning_crate_name.replace('-', "_");
Marcel Hlopkod906b892022-01-27 08:52:36 +00001030 let owning_crate = make_rs_ident(&escaped_owning_crate_name);
Googler6a0a5252022-01-11 14:08:09 +00001031 Ok(quote! {#owning_crate::#ident})
1032 }
1033}
1034
Lukasz Anforowicz40c2eb82022-01-11 18:22:31 +00001035#[derive(Debug, Eq, PartialEq)]
1036enum Mutability {
1037 Const,
1038 Mut,
1039}
1040
1041impl Mutability {
1042 fn format_for_pointer(&self) -> TokenStream {
1043 match self {
1044 Mutability::Mut => quote! {mut},
1045 Mutability::Const => quote! {const},
1046 }
1047 }
1048
1049 fn format_for_reference(&self) -> TokenStream {
1050 match self {
1051 Mutability::Mut => quote! {mut},
1052 Mutability::Const => quote! {},
1053 }
1054 }
1055}
1056
1057// TODO(b/213947473): Instead of having a separate RsTypeKind here, consider
1058// changing ir::RsType into a similar `enum`, with fields that contain
1059// references (e.g. &'ir Record`) instead of DeclIds.
1060#[derive(Debug)]
1061enum RsTypeKind<'ir> {
1062 Pointer { pointee: Box<RsTypeKind<'ir>>, mutability: Mutability },
1063 Reference { referent: Box<RsTypeKind<'ir>>, mutability: Mutability, lifetime_id: LifetimeId },
Lukasz Anforowiczcf230fd2022-02-18 19:20:39 +00001064 FuncPtr { abi: &'ir str, return_type: Box<RsTypeKind<'ir>>, param_types: Vec<RsTypeKind<'ir>> },
Lukasz Anforowicz40c2eb82022-01-11 18:22:31 +00001065 Record(&'ir Record),
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00001066 TypeAlias { type_alias: &'ir TypeAlias, underlying_type: Box<RsTypeKind<'ir>> },
Lukasz Anforowicz40c2eb82022-01-11 18:22:31 +00001067 Unit,
1068 Other { name: &'ir str, type_args: Vec<RsTypeKind<'ir>> },
1069}
1070
1071impl<'ir> RsTypeKind<'ir> {
1072 pub fn new(ty: &'ir ir::RsType, ir: &'ir IR) -> Result<Self> {
1073 // The lambdas deduplicate code needed by multiple `match` branches.
1074 let get_type_args = || -> Result<Vec<RsTypeKind<'ir>>> {
1075 ty.type_args.iter().map(|type_arg| RsTypeKind::<'ir>::new(type_arg, ir)).collect()
1076 };
1077 let get_pointee = || -> Result<Box<RsTypeKind<'ir>>> {
1078 if ty.type_args.len() != 1 {
1079 bail!("Missing pointee/referent type (need exactly 1 type argument): {:?}", ty);
1080 }
1081 Ok(Box::new(get_type_args()?.remove(0)))
1082 };
1083 let get_lifetime = || -> Result<LifetimeId> {
1084 if ty.lifetime_args.len() != 1 {
1085 bail!("Missing reference lifetime (need exactly 1 lifetime argument): {:?}", ty);
1086 }
1087 Ok(ty.lifetime_args[0])
1088 };
1089
1090 let result = match ty.name.as_deref() {
1091 None => {
1092 ensure!(
1093 ty.type_args.is_empty(),
1094 "Type arguments on records nor type aliases are not yet supported: {:?}",
1095 ty
1096 );
1097 match ir.item_for_type(ty)? {
1098 Item::Record(record) => RsTypeKind::Record(record),
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00001099 Item::TypeAlias(type_alias) => RsTypeKind::TypeAlias {
1100 type_alias,
1101 underlying_type: Box::new(RsTypeKind::new(
1102 &type_alias.underlying_type.rs_type,
1103 ir,
1104 )?),
1105 },
Lukasz Anforowicz40c2eb82022-01-11 18:22:31 +00001106 other_item => bail!("Item does not define a type: {:?}", other_item),
1107 }
1108 }
1109 Some(name) => match name {
1110 "()" => {
1111 if !ty.type_args.is_empty() {
1112 bail!("Unit type must not have type arguments: {:?}", ty);
1113 }
1114 RsTypeKind::Unit
1115 }
1116 "*mut" => {
1117 RsTypeKind::Pointer { pointee: get_pointee()?, mutability: Mutability::Mut }
1118 }
1119 "*const" => {
1120 RsTypeKind::Pointer { pointee: get_pointee()?, mutability: Mutability::Const }
1121 }
1122 "&mut" => RsTypeKind::Reference {
1123 referent: get_pointee()?,
1124 mutability: Mutability::Mut,
1125 lifetime_id: get_lifetime()?,
1126 },
1127 "&" => RsTypeKind::Reference {
1128 referent: get_pointee()?,
1129 mutability: Mutability::Const,
1130 lifetime_id: get_lifetime()?,
1131 },
Lukasz Anforowiczcf230fd2022-02-18 19:20:39 +00001132 name => {
1133 let mut type_args = get_type_args()?;
1134 match name.strip_prefix("#funcPtr ") {
1135 None => RsTypeKind::Other { name, type_args },
1136 Some(abi) => {
1137 // TODO(b/217419782): Consider enforcing `'static` lifetime.
1138 ensure!(!type_args.is_empty(), "No return type in fn type: {:?}", ty);
1139 RsTypeKind::FuncPtr {
1140 abi,
1141 return_type: Box::new(type_args.remove(type_args.len() - 1)),
1142 param_types: type_args,
1143 }
1144 },
1145 }
1146 },
Lukasz Anforowicz40c2eb82022-01-11 18:22:31 +00001147 },
1148 };
1149 Ok(result)
1150 }
1151
1152 pub fn format(
1153 &self,
1154 ir: &IR,
1155 lifetime_to_name: &HashMap<LifetimeId, String>,
1156 ) -> Result<TokenStream> {
1157 let result = match self {
1158 RsTypeKind::Pointer { pointee, mutability } => {
1159 let mutability = mutability.format_for_pointer();
1160 let nested_type = pointee.format(ir, lifetime_to_name)?;
1161 quote! {* #mutability #nested_type}
1162 }
1163 RsTypeKind::Reference { referent, mutability, lifetime_id } => {
1164 let mutability = mutability.format_for_reference();
1165 let lifetime = Self::format_lifetime(lifetime_id, lifetime_to_name)?;
1166 let nested_type = referent.format(ir, lifetime_to_name)?;
1167 quote! {& #lifetime #mutability #nested_type}
1168 }
Lukasz Anforowiczcf230fd2022-02-18 19:20:39 +00001169 RsTypeKind::FuncPtr { abi, return_type, param_types } => {
1170 let return_frag = return_type.format_as_return_type_fragment(ir, lifetime_to_name)?;
1171 let param_types = param_types
1172 .iter()
1173 .map(|t| t.format(ir, lifetime_to_name))
1174 .collect::<Result<Vec<_>>>()?;
1175 quote!{ extern #abi fn( #( #param_types ),* ) #return_frag }
1176 },
Lukasz Anforowicz40c2eb82022-01-11 18:22:31 +00001177 RsTypeKind::Record(record) => rs_type_name_for_target_and_identifier(
1178 &record.owning_target,
1179 &record.identifier,
1180 ir,
1181 )?,
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00001182 RsTypeKind::TypeAlias { type_alias, .. } => rs_type_name_for_target_and_identifier(
Lukasz Anforowicz40c2eb82022-01-11 18:22:31 +00001183 &type_alias.owning_target,
1184 &type_alias.identifier,
1185 ir,
1186 )?,
1187 RsTypeKind::Unit => quote! {()},
1188 RsTypeKind::Other { name, type_args } => {
Marcel Hlopkoeaae9b72022-01-21 15:54:11 +00001189 let ident = make_rs_ident(name);
Lukasz Anforowicz40c2eb82022-01-11 18:22:31 +00001190 let generic_params = format_generic_params(
1191 type_args
1192 .iter()
1193 .map(|type_arg| type_arg.format(ir, lifetime_to_name))
1194 .collect::<Result<Vec<_>>>()?,
1195 );
1196 quote! {#ident #generic_params}
1197 }
1198 };
1199 Ok(result)
1200 }
1201
Lukasz Anforowiczcf230fd2022-02-18 19:20:39 +00001202 pub fn format_as_return_type_fragment(
1203 &self,
1204 ir: &IR,
1205 lifetime_to_name: &HashMap<LifetimeId, String>,
1206 ) -> Result<TokenStream> {
1207 match self {
1208 RsTypeKind::Unit => Ok(quote! {}),
1209 other_type => {
1210 let return_type = other_type.format(ir, lifetime_to_name)?;
1211 Ok(quote! { -> #return_type })
1212 }
1213 }
1214 }
1215
Lukasz Anforowiczcde4b1b2022-02-03 21:20:55 +00001216 /// Formats this RsTypeKind as `&'a mut MaybeUninit<SomeStruct>`. This is
1217 /// used to format `__this` parameter in a constructor thunk.
1218 pub fn format_mut_ref_as_uninitialized(
Lukasz Anforowicz40c2eb82022-01-11 18:22:31 +00001219 &self,
1220 ir: &IR,
1221 lifetime_to_name: &HashMap<LifetimeId, String>,
1222 ) -> Result<TokenStream> {
Lukasz Anforowiczcde4b1b2022-02-03 21:20:55 +00001223 match self {
1224 RsTypeKind::Reference { referent, lifetime_id, mutability: Mutability::Mut } => {
1225 let nested_type = referent.format(ir, lifetime_to_name)?;
1226 let lifetime = Self::format_lifetime(lifetime_id, lifetime_to_name)?;
1227 Ok(quote! { & #lifetime mut std::mem::MaybeUninit< #nested_type > })
Lukasz Anforowicz40c2eb82022-01-11 18:22:31 +00001228 }
Lukasz Anforowicz40c2eb82022-01-11 18:22:31 +00001229 _ => bail!("Unexpected type of `__this` parameter in a constructor: {:?}", self),
Lukasz Anforowiczcde4b1b2022-02-03 21:20:55 +00001230 }
Lukasz Anforowicz40c2eb82022-01-11 18:22:31 +00001231 }
1232
Lukasz Anforowicz231a3bb2022-01-12 14:05:59 +00001233 /// Formats this RsTypeKind as either `&'a self` or `&'a mut self`.
Lukasz Anforowiczcde4b1b2022-02-03 21:20:55 +00001234 pub fn format_as_self_param(
Lukasz Anforowicz231a3bb2022-01-12 14:05:59 +00001235 &self,
Lukasz Anforowiczce345392022-01-14 22:41:16 +00001236 func: &Func,
1237 ir: &IR,
Lukasz Anforowicz231a3bb2022-01-12 14:05:59 +00001238 lifetime_to_name: &HashMap<LifetimeId, String>,
Lukasz Anforowiczf9564622022-01-28 14:31:04 +00001239 ) -> Result<TokenStream> {
Lukasz Anforowicz732ca642022-02-03 20:58:38 +00001240 if func.name == UnqualifiedIdentifier::Destructor {
1241 let record = func
1242 .member_func_metadata
1243 .as_ref()
1244 .ok_or_else(|| anyhow!("Destructors must be member functions: {:?}", func))?
1245 .find_record(ir)?;
1246 if self.is_mut_ptr_to(record) {
1247 // Even in C++ it is UB to retain `this` pointer and dereference it
1248 // after a destructor runs. Therefore it is safe to use `&self` or
1249 // `&mut self` in Rust even if IR represents `__this` as a Rust
1250 // pointer (e.g. when lifetime annotations are missing - lifetime
1251 // annotations are required to represent it as a Rust reference).
1252 return Ok(quote! { &mut self });
1253 }
Lukasz Anforowicz231a3bb2022-01-12 14:05:59 +00001254 }
1255
1256 match self {
Lukasz Anforowicz231a3bb2022-01-12 14:05:59 +00001257 RsTypeKind::Reference { mutability, lifetime_id, .. } => {
1258 let mutability = mutability.format_for_reference();
1259 let lifetime = Self::format_lifetime(lifetime_id, lifetime_to_name)?;
Lukasz Anforowiczf9564622022-01-28 14:31:04 +00001260 Ok(quote! { & #lifetime #mutability self })
Lukasz Anforowicz231a3bb2022-01-12 14:05:59 +00001261 }
Lukasz Anforowicz732ca642022-02-03 20:58:38 +00001262 _ => bail!("Unexpected type of `self` parameter: {:?}", self),
Lukasz Anforowicz231a3bb2022-01-12 14:05:59 +00001263 }
1264 }
1265
Lukasz Anforowicz40c2eb82022-01-11 18:22:31 +00001266 fn format_lifetime(
1267 lifetime_id: &LifetimeId,
1268 lifetime_to_name: &HashMap<LifetimeId, String>,
1269 ) -> Result<TokenStream> {
1270 let lifetime_name = lifetime_to_name.get(lifetime_id).ok_or_else(|| {
1271 anyhow!("`lifetime_to_name` doesn't have an entry for {:?}", lifetime_id)
1272 })?;
Lukasz Anforowicz95551272022-01-20 00:02:24 +00001273 Ok(format_lifetime_name(lifetime_name))
Lukasz Anforowicz40c2eb82022-01-11 18:22:31 +00001274 }
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00001275
Lukasz Anforowicz90bdb962022-02-14 21:07:45 +00001276 /// Returns whether the type represented by `self` implements the `Copy`
1277 /// trait.
Lukasz Anforowicza94ab702022-01-14 22:40:25 +00001278 pub fn implements_copy(&self) -> bool {
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00001279 // TODO(b/212696226): Verify results of `implements_copy` via static
1280 // assertions in the generated Rust code (because incorrect results
1281 // can silently lead to unsafe behavior).
1282 match self {
1283 RsTypeKind::Unit => true,
1284 RsTypeKind::Pointer { .. } => true,
Lukasz Anforowiczcf230fd2022-02-18 19:20:39 +00001285 RsTypeKind::FuncPtr { .. } => true,
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00001286 RsTypeKind::Reference { mutability: Mutability::Const, .. } => true,
1287 RsTypeKind::Reference { mutability: Mutability::Mut, .. } => false,
1288 RsTypeKind::Record(record) => should_derive_copy(record),
1289 RsTypeKind::TypeAlias { underlying_type, .. } => underlying_type.implements_copy(),
Lukasz Anforowiczd81bea92022-02-11 08:57:58 +00001290 RsTypeKind::Other { type_args, .. } => {
Lukasz Anforowicz20651e32022-02-10 14:52:15 +00001291 // All types that may appear here without `type_args` (e.g.
1292 // primitive types like `i32`) implement `Copy`. Generic types
1293 // that may be present here (e.g. Option<...>) are `Copy` if all
1294 // of their `type_args` are `Copy`.
1295 type_args.iter().all(|t| t.implements_copy())
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00001296 }
1297 }
1298 }
Lukasz Anforowicza94ab702022-01-14 22:40:25 +00001299
Lukasz Anforowiczf9564622022-01-28 14:31:04 +00001300 pub fn is_mut_ptr_to(&self, expected_record: &Record) -> bool {
1301 match self {
1302 RsTypeKind::Pointer { pointee, mutability: Mutability::Mut, .. } => {
1303 pointee.is_record(expected_record)
1304 }
1305 _ => false,
1306 }
1307 }
1308
1309 pub fn is_ref_to(&self, expected_record: &Record) -> bool {
1310 match self {
1311 RsTypeKind::Reference { referent, .. } => referent.is_record(expected_record),
1312 _ => false,
1313 }
1314 }
1315
Lukasz Anforowicza94ab702022-01-14 22:40:25 +00001316 pub fn is_shared_ref_to(&self, expected_record: &Record) -> bool {
1317 match self {
1318 RsTypeKind::Reference { referent, mutability: Mutability::Const, .. } => {
Lukasz Anforowiczf9564622022-01-28 14:31:04 +00001319 referent.is_record(expected_record)
Lukasz Anforowicza94ab702022-01-14 22:40:25 +00001320 }
1321 _ => false,
1322 }
1323 }
Lukasz Anforowiczf9564622022-01-28 14:31:04 +00001324
1325 pub fn is_record(&self, expected_record: &Record) -> bool {
1326 match self {
1327 RsTypeKind::Record(actual_record) => actual_record.id == expected_record.id,
1328 _ => false,
1329 }
1330 }
Lukasz Anforowicz90bdb962022-02-14 21:07:45 +00001331
1332 /// Iterates over `self` and all the nested types (e.g. pointees, generic
1333 /// type args, etc.) in DFS order.
1334 pub fn dfs_iter<'ty>(&'ty self) -> impl Iterator<Item = &'ty RsTypeKind<'ir>> + '_ {
1335 RsTypeKindIter::new(self)
1336 }
1337
1338 /// Iterates over all `LifetimeId`s in `self` and in all the nested types.
1339 /// Note that the results might contain duplicate LifetimeId values (e.g.
1340 /// if the same LifetimeId is used in two `type_args`).
1341 pub fn lifetimes(&self) -> impl Iterator<Item = LifetimeId> + '_ {
1342 self.dfs_iter().filter_map(|t| match t {
1343 RsTypeKind::Reference { lifetime_id, .. } => Some(*lifetime_id),
1344 _ => None,
1345 })
1346 }
1347}
1348
1349struct RsTypeKindIter<'ty, 'ir> {
1350 todo: Vec<&'ty RsTypeKind<'ir>>,
1351}
1352
1353impl<'ty, 'ir> RsTypeKindIter<'ty, 'ir> {
1354 pub fn new(ty: &'ty RsTypeKind<'ir>) -> Self {
1355 Self { todo: vec![ty] }
1356 }
1357}
1358
1359impl<'ty, 'ir> Iterator for RsTypeKindIter<'ty, 'ir> {
1360 type Item = &'ty RsTypeKind<'ir>;
1361
1362 fn next(&mut self) -> Option<Self::Item> {
1363 match self.todo.pop() {
1364 None => None,
1365 Some(curr) => {
1366 match curr {
1367 RsTypeKind::Unit | RsTypeKind::Record(_) => (),
1368 RsTypeKind::Pointer { pointee, .. } => self.todo.push(pointee),
1369 RsTypeKind::Reference { referent, .. } => self.todo.push(referent),
1370 RsTypeKind::TypeAlias { underlying_type: t, .. } => self.todo.push(t),
Lukasz Anforowiczcf230fd2022-02-18 19:20:39 +00001371 RsTypeKind::FuncPtr { return_type, param_types, .. } => {
1372 self.todo.push(return_type);
1373 self.todo.extend(param_types.iter().rev());
1374 },
Lukasz Anforowicz90bdb962022-02-14 21:07:45 +00001375 RsTypeKind::Other { type_args, .. } => self.todo.extend(type_args.iter().rev()),
1376 };
1377 Some(curr)
1378 }
1379 }
1380 }
Lukasz Anforowicz40c2eb82022-01-11 18:22:31 +00001381}
1382
Lukasz Anforowicz95551272022-01-20 00:02:24 +00001383fn format_lifetime_name(lifetime_name: &str) -> TokenStream {
1384 let lifetime =
1385 syn::Lifetime::new(&format!("'{}", lifetime_name), proc_macro2::Span::call_site());
1386 quote! { #lifetime }
1387}
1388
Googler7cced422021-12-06 11:58:39 +00001389fn format_rs_type(
1390 ty: &ir::RsType,
1391 ir: &IR,
1392 lifetime_to_name: &HashMap<LifetimeId, String>,
1393) -> Result<TokenStream> {
Lukasz Anforowicz40c2eb82022-01-11 18:22:31 +00001394 RsTypeKind::new(ty, ir)
1395 .and_then(|kind| kind.format(ir, lifetime_to_name))
1396 .with_context(|| format!("Failed to format Rust type {:?}", ty))
Devin Jeanpierre7a7328e2021-09-17 07:10:08 +00001397}
1398
Googler6a0a5252022-01-11 14:08:09 +00001399fn cc_type_name_for_item(item: &ir::Item) -> Result<TokenStream> {
1400 let (disambiguator_fragment, identifier) = match item {
1401 Item::Record(record) => (quote! { class }, &record.identifier),
1402 Item::TypeAlias(type_alias) => (quote! {}, &type_alias.identifier),
1403 _ => bail!("Item does not define a type: {:?}", item),
1404 };
1405
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +00001406 let ident = format_cc_ident(identifier.identifier.as_str());
Googler6a0a5252022-01-11 14:08:09 +00001407 Ok(quote! { #disambiguator_fragment #ident })
1408}
1409
Marcel Hlopkoc0956cf2021-11-29 08:31:28 +00001410fn format_cc_type(ty: &ir::CcType, ir: &IR) -> Result<TokenStream> {
Devin Jeanpierre09c6f452021-09-29 07:34:24 +00001411 let const_fragment = if ty.is_const {
Devin Jeanpierre184f9ac2021-09-17 13:47:03 +00001412 quote! {const}
1413 } else {
1414 quote! {}
1415 };
Marcel Hlopkoc0956cf2021-11-29 08:31:28 +00001416 if let Some(ref name) = ty.name {
1417 match name.as_str() {
1418 "*" => {
Googlerff7fc232021-12-02 09:43:00 +00001419 if ty.type_args.len() != 1 {
1420 bail!("Invalid pointer type (need exactly 1 type argument): {:?}", ty);
Marcel Hlopkoc0956cf2021-11-29 08:31:28 +00001421 }
Googlerff7fc232021-12-02 09:43:00 +00001422 assert_eq!(ty.type_args.len(), 1);
1423 let nested_type = format_cc_type(&ty.type_args[0], ir)?;
Marcel Hlopkoc0956cf2021-11-29 08:31:28 +00001424 Ok(quote! {#nested_type * #const_fragment})
Devin Jeanpierre7a7328e2021-09-17 07:10:08 +00001425 }
Lukasz Anforowicz275fa922022-01-05 16:13:20 +00001426 "&" => {
1427 if ty.type_args.len() != 1 {
1428 bail!("Invalid reference type (need exactly 1 type argument): {:?}", ty);
1429 }
1430 let nested_type = format_cc_type(&ty.type_args[0], ir)?;
1431 Ok(quote! {#nested_type &})
1432 }
Lukasz Anforowicz957cbf22022-01-05 16:14:05 +00001433 cc_type_name => {
Googlerff7fc232021-12-02 09:43:00 +00001434 if !ty.type_args.is_empty() {
Marcel Hlopkoc0956cf2021-11-29 08:31:28 +00001435 bail!("Type not yet supported: {:?}", ty);
1436 }
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +00001437 let idents = cc_type_name.split_whitespace().map(format_cc_ident);
Lukasz Anforowicz957cbf22022-01-05 16:14:05 +00001438 Ok(quote! {#( #idents )* #const_fragment})
Devin Jeanpierre7a7328e2021-09-17 07:10:08 +00001439 }
Devin Jeanpierre7a7328e2021-09-17 07:10:08 +00001440 }
Marcel Hlopkoc0956cf2021-11-29 08:31:28 +00001441 } else {
Googler6a0a5252022-01-11 14:08:09 +00001442 let item = ir.item_for_type(ty)?;
1443 let type_name = cc_type_name_for_item(item)?;
1444 Ok(quote! {#const_fragment #type_name})
Devin Jeanpierre7a7328e2021-09-17 07:10:08 +00001445 }
1446}
1447
Marcel Hlopkoa0f38662021-12-03 08:45:26 +00001448fn cc_struct_layout_assertion(record: &Record, ir: &IR) -> TokenStream {
Googler6a0a5252022-01-11 14:08:09 +00001449 if !ir.is_current_target(&record.owning_target) && !ir.is_stdlib_target(&record.owning_target) {
Marcel Hlopkoa0f38662021-12-03 08:45:26 +00001450 return quote! {};
1451 }
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +00001452 let record_ident = format_cc_ident(&record.identifier.identifier);
Googler5ea88642021-09-29 08:05:59 +00001453 let size = Literal::usize_unsuffixed(record.size);
1454 let alignment = Literal::usize_unsuffixed(record.alignment);
Lukasz Anforowicz74704712021-12-22 15:30:31 +00001455 let field_assertions =
1456 record.fields.iter().filter(|f| f.access == AccessSpecifier::Public).map(|field| {
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +00001457 let field_ident = format_cc_ident(&field.identifier.identifier);
Lukasz Anforowicz74704712021-12-22 15:30:31 +00001458 let offset = Literal::usize_unsuffixed(field.offset);
1459 // The IR contains the offset in bits, while C++'s offsetof()
1460 // returns the offset in bytes, so we need to convert.
1461 quote! {
Googler972d3582022-01-11 10:17:22 +00001462 static_assert(offsetof(class #record_ident, #field_ident) * 8 == #offset);
Lukasz Anforowicz74704712021-12-22 15:30:31 +00001463 }
1464 });
Googler5ea88642021-09-29 08:05:59 +00001465 quote! {
Googler972d3582022-01-11 10:17:22 +00001466 static_assert(sizeof(class #record_ident) == #size);
1467 static_assert(alignof(class #record_ident) == #alignment);
Googler5ea88642021-09-29 08:05:59 +00001468 #( #field_assertions )*
1469 }
1470}
1471
Devin Jeanpierre58181ac2022-02-14 21:30:05 +00001472// Returns the accessor functions for no_unique_address member variables.
1473fn cc_struct_no_unique_address_impl(record: &Record, ir: &IR) -> Result<TokenStream> {
1474 let mut fields = vec![];
1475 let mut types = vec![];
1476 for field in &record.fields {
1477 if field.access != AccessSpecifier::Public || !field.is_no_unique_address {
1478 continue;
1479 }
1480 fields.push(make_rs_ident(&field.identifier.identifier));
1481 types.push(format_rs_type(&field.type_.rs_type, ir, &HashMap::new()).with_context(
1482 || format!("Failed to format type for field {:?} on record {:?}", field, record),
1483 )?)
1484 }
1485
1486 if fields.is_empty() {
1487 return Ok(quote! {});
1488 }
1489
1490 let ident = make_rs_ident(&record.identifier.identifier);
1491 Ok(quote! {
1492 impl #ident {
1493 #(
1494 pub fn #fields(&self) -> &#types {
1495 unsafe {&* (&self.#fields as *const _ as *const #types)}
1496 }
1497 )*
1498 }
1499 })
1500}
1501
Devin Jeanpierre56777022022-02-03 01:57:15 +00001502/// Returns the implementation of base class conversions, for converting a type
1503/// to its unambiguous public base classes.
1504///
1505/// TODO(b/216195042): Implement this in terms of a supporting trait which casts
1506/// raw pointers. Then, we would have blanket impls for reference, pinned mut
1507/// reference, etc. conversion. The current version is just enough to test the
1508/// logic in importer.
1509//
1510// TODO(b/216195042): Should this use, like, AsRef/AsMut (and some equivalent
1511// for Pin)?
1512fn cc_struct_upcast_impl(record: &Record, ir: &IR) -> Result<TokenStream> {
1513 let mut impls = Vec::with_capacity(record.unambiguous_public_bases.len());
1514 for base in &record.unambiguous_public_bases {
1515 let base_record: &Record = ir.find_decl(base.base_record_id)?.try_into()?;
1516 if let Some(offset) = base.offset {
1517 let offset = Literal::i64_unsuffixed(offset);
1518 // TODO(b/216195042): Correctly handle imported records, lifetimes.
1519 let base_name = make_rs_ident(&base_record.identifier.identifier);
1520 let derived_name = make_rs_ident(&record.identifier.identifier);
1521 impls.push(quote! {
1522 impl<'a> From<&'a #derived_name> for &'a #base_name {
1523 fn from(x: &'a #derived_name) -> Self {
1524 unsafe {
1525 &*((x as *const _ as *const u8).offset(#offset) as *const #base_name)
1526 }
1527 }
1528 }
1529 });
1530 } else {
1531 // TODO(b/216195042): determine offset dynamically / use a dynamic
1532 // cast. This requires a new C++ function to be
1533 // generated, so that we have something to call.
1534 }
1535 }
1536
1537 Ok(quote! {
1538 #(#impls)*
1539 })
1540}
1541
Googlera675ae02021-12-07 08:04:59 +00001542fn thunk_ident(func: &Func) -> Ident {
1543 format_ident!("__rust_thunk__{}", func.mangled_name)
Devin Jeanpierref2ec8712021-10-13 20:47:16 +00001544}
1545
Marcel Hlopko89547752021-12-10 09:39:41 +00001546fn generate_rs_api_impl(ir: &IR) -> Result<TokenStream> {
Michael Forsterbee84482021-10-13 08:35:38 +00001547 // This function uses quote! to generate C++ source code out of convenience.
1548 // This is a bold idea so we have to continously evaluate if it still makes
1549 // sense or the cost of working around differences in Rust and C++ tokens is
1550 // greather than the value added.
Marcel Hlopko3164eee2021-08-24 20:09:22 +00001551 //
Michael Forsterbee84482021-10-13 08:35:38 +00001552 // See rs_bindings_from_cc/
1553 // token_stream_printer.rs for a list of supported placeholders.
Marcel Hlopko3164eee2021-08-24 20:09:22 +00001554 let mut thunks = vec![];
Michael Forster7ef80732021-10-01 18:12:19 +00001555 for func in ir.functions() {
Lukasz Anforowiczdd9ae0f2022-02-17 15:52:53 +00001556 if can_skip_cc_thunk(func) {
Marcel Hlopko3164eee2021-08-24 20:09:22 +00001557 continue;
1558 }
1559
Googlera675ae02021-12-07 08:04:59 +00001560 let thunk_ident = thunk_ident(func);
Devin Jeanpierref2ec8712021-10-13 20:47:16 +00001561 let implementation_function = match &func.name {
Lukasz Anforowicz9c663ca2022-02-09 01:33:31 +00001562 UnqualifiedIdentifier::Operator(op) => {
1563 let name = syn::parse_str::<TokenStream>(&op.name)?;
1564 quote! { operator #name }
1565 }
Devin Jeanpierref2ec8712021-10-13 20:47:16 +00001566 UnqualifiedIdentifier::Identifier(id) => {
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +00001567 let fn_ident = format_cc_ident(&id.identifier);
Lukasz Anforowiczaab8ad22021-12-19 20:29:26 +00001568 let static_method_metadata = func
1569 .member_func_metadata
1570 .as_ref()
1571 .filter(|meta| meta.instance_method_metadata.is_none());
1572 match static_method_metadata {
1573 None => quote! {#fn_ident},
1574 Some(meta) => {
Marcel Hlopkoeaae9b72022-01-21 15:54:11 +00001575 let record_ident =
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +00001576 format_cc_ident(&meta.find_record(ir)?.identifier.identifier);
Lukasz Anforowiczaab8ad22021-12-19 20:29:26 +00001577 quote! { #record_ident :: #fn_ident }
1578 }
1579 }
Devin Jeanpierref2ec8712021-10-13 20:47:16 +00001580 }
Lukasz Anforowicz7b0042d2022-01-06 23:00:19 +00001581 // Use `destroy_at` to avoid needing to spell out the class name. Destructor identiifers
Devin Jeanpierrecc6cf092021-12-16 04:31:14 +00001582 // use the name of the type itself, without namespace qualification, template
1583 // parameters, or aliases. We do not need to use that naming scheme anywhere else in
1584 // the bindings, and it can be difficult (impossible?) to spell in the general case. By
1585 // using destroy_at, we avoid needing to determine or remember what the correct spelling
Lukasz Anforowicz7b0042d2022-01-06 23:00:19 +00001586 // is. Similar arguments apply to `construct_at`.
Lukasz Anforowicze643ec92021-12-22 15:45:15 +00001587 UnqualifiedIdentifier::Constructor => {
Lukasz Anforowicz7b0042d2022-01-06 23:00:19 +00001588 quote! { rs_api_impl_support::construct_at }
Lukasz Anforowicze643ec92021-12-22 15:45:15 +00001589 }
Devin Jeanpierref2ec8712021-10-13 20:47:16 +00001590 UnqualifiedIdentifier::Destructor => quote! {std::destroy_at},
Devin Jeanpierref2ec8712021-10-13 20:47:16 +00001591 };
Marcel Hlopkoc0956cf2021-11-29 08:31:28 +00001592 let return_type_name = format_cc_type(&func.return_type.cc_type, ir)?;
Lukasz Anforowicze643ec92021-12-22 15:45:15 +00001593 let return_stmt = if func.return_type.cc_type.is_void() {
1594 quote! {}
1595 } else {
1596 quote! { return }
1597 };
Marcel Hlopko3164eee2021-08-24 20:09:22 +00001598
1599 let param_idents =
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +00001600 func.params.iter().map(|p| format_cc_ident(&p.identifier.identifier)).collect_vec();
Marcel Hlopko3164eee2021-08-24 20:09:22 +00001601
Devin Jeanpierre09c6f452021-09-29 07:34:24 +00001602 let param_types = func
1603 .params
1604 .iter()
Marcel Hlopkoc0956cf2021-11-29 08:31:28 +00001605 .map(|p| format_cc_type(&p.type_.cc_type, ir))
Devin Jeanpierre09c6f452021-09-29 07:34:24 +00001606 .collect::<Result<Vec<_>>>()?;
Marcel Hlopko3164eee2021-08-24 20:09:22 +00001607
Lukasz Anforowiczb3d89aa2022-01-12 14:35:52 +00001608 let needs_this_deref = match &func.member_func_metadata {
1609 None => false,
1610 Some(meta) => match &func.name {
1611 UnqualifiedIdentifier::Constructor | UnqualifiedIdentifier::Destructor => false,
Marcel Hlopko14ee3c82022-02-09 09:46:23 +00001612 UnqualifiedIdentifier::Identifier(_) | UnqualifiedIdentifier::Operator(_) => {
1613 meta.instance_method_metadata.is_some()
1614 }
Lukasz Anforowiczb3d89aa2022-01-12 14:35:52 +00001615 },
1616 };
1617 let (implementation_function, arg_expressions) = if !needs_this_deref {
1618 (implementation_function, param_idents.clone())
1619 } else {
1620 let this_param = func
1621 .params
1622 .first()
1623 .ok_or_else(|| anyhow!("Instance methods must have `__this` param."))?;
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +00001624 let this_arg = format_cc_ident(&this_param.identifier.identifier);
Lukasz Anforowiczb3d89aa2022-01-12 14:35:52 +00001625 (
1626 quote! { #this_arg -> #implementation_function},
1627 param_idents.iter().skip(1).cloned().collect_vec(),
1628 )
1629 };
1630
Marcel Hlopko3164eee2021-08-24 20:09:22 +00001631 thunks.push(quote! {
1632 extern "C" #return_type_name #thunk_ident( #( #param_types #param_idents ),* ) {
Lukasz Anforowiczb3d89aa2022-01-12 14:35:52 +00001633 #return_stmt #implementation_function( #( #arg_expressions ),* );
Marcel Hlopko3164eee2021-08-24 20:09:22 +00001634 }
1635 });
1636 }
1637
Marcel Hlopkoa0f38662021-12-03 08:45:26 +00001638 let layout_assertions = ir.records().map(|record| cc_struct_layout_assertion(record, ir));
Googler5ea88642021-09-29 08:05:59 +00001639
Devin Jeanpierre231ef8d2021-10-27 10:50:44 +00001640 let mut standard_headers = <BTreeSet<Ident>>::new();
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +00001641 standard_headers.insert(format_ident!("memory")); // ubiquitous.
Devin Jeanpierre231ef8d2021-10-27 10:50:44 +00001642 if ir.records().next().is_some() {
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +00001643 standard_headers.insert(format_ident!("cstddef"));
Devin Jeanpierre231ef8d2021-10-27 10:50:44 +00001644 };
Googler5ea88642021-09-29 08:05:59 +00001645
Lukasz Anforowicz4457baf2021-12-23 17:24:04 +00001646 let mut includes =
1647 vec!["rs_bindings_from_cc/support/cxx20_backports.h"];
1648
Michael Forsterbee84482021-10-13 08:35:38 +00001649 // In order to generate C++ thunk in all the cases Clang needs to be able to
1650 // access declarations from public headers of the C++ library.
Lukasz Anforowicz4457baf2021-12-23 17:24:04 +00001651 includes.extend(ir.used_headers().map(|i| &i.name as &str));
Marcel Hlopko3164eee2021-08-24 20:09:22 +00001652
Marcel Hlopko89547752021-12-10 09:39:41 +00001653 Ok(quote! {
Googler5ea88642021-09-29 08:05:59 +00001654 #( __HASH_TOKEN__ include <#standard_headers> __NEWLINE__)*
Devin Jeanpierre7c74f842022-02-03 07:08:06 +00001655 __NEWLINE__
Michael Forsterdb8101a2021-10-08 06:56:03 +00001656 #( __HASH_TOKEN__ include #includes __NEWLINE__)* __NEWLINE__
Marcel Hlopko3164eee2021-08-24 20:09:22 +00001657
Michael Forsterdb8101a2021-10-08 06:56:03 +00001658 #( #thunks )* __NEWLINE__ __NEWLINE__
Googler5ea88642021-09-29 08:05:59 +00001659
Michael Forsterdb8101a2021-10-08 06:56:03 +00001660 #( #layout_assertions __NEWLINE__ __NEWLINE__ )*
Marcel Hlopkoc6b726c2021-10-07 06:53:09 +00001661
1662 // To satisfy http://cs/symbol:devtools.metadata.Presubmit.CheckTerminatingNewline check.
1663 __NEWLINE__
Marcel Hlopko89547752021-12-10 09:39:41 +00001664 })
Marcel Hlopko3164eee2021-08-24 20:09:22 +00001665}
1666
Marcel Hlopko42abfc82021-08-09 07:03:17 +00001667#[cfg(test)]
1668mod tests {
Devin Jeanpierre45cb1162021-10-27 10:54:28 +00001669 use super::*;
Michael Forstered642022021-10-04 09:48:25 +00001670 use anyhow::anyhow;
Lukasz Anforowicz9c663ca2022-02-09 01:33:31 +00001671 use ir_testing::{ir_from_cc, ir_from_cc_dependency, ir_func, ir_record, retrieve_func};
Marcel Hlopko89547752021-12-10 09:39:41 +00001672 use token_stream_matchers::{
1673 assert_cc_matches, assert_cc_not_matches, assert_rs_matches, assert_rs_not_matches,
Devin Jeanpierre7e9a1de2021-12-03 08:04:22 +00001674 };
Michael Forsterdb8101a2021-10-08 06:56:03 +00001675 use token_stream_printer::tokens_to_string;
Marcel Hlopko42abfc82021-08-09 07:03:17 +00001676
1677 #[test]
Marcel Hlopko3b9bf9e2021-11-29 08:25:14 +00001678 // TODO(hlopko): Move this test to a more principled place where it can access
1679 // `ir_testing`.
1680 fn test_duplicate_decl_ids_err() {
1681 let mut r1 = ir_record("R1");
Marcel Hlopko264b9ad2021-12-02 21:06:44 +00001682 r1.id = DeclId(42);
Marcel Hlopko3b9bf9e2021-11-29 08:25:14 +00001683 let mut r2 = ir_record("R2");
Marcel Hlopko264b9ad2021-12-02 21:06:44 +00001684 r2.id = DeclId(42);
Marcel Hlopko3b9bf9e2021-11-29 08:25:14 +00001685 let result = make_ir_from_items([r1.into(), r2.into()]);
1686 assert!(result.is_err());
1687 assert!(result.unwrap_err().to_string().contains("Duplicate decl_id found in"));
1688 }
1689
1690 #[test]
Marcel Hlopko45fba972021-08-23 19:52:20 +00001691 fn test_simple_function() -> Result<()> {
Marcel Hlopko89547752021-12-10 09:39:41 +00001692 let ir = ir_from_cc("int Add(int a, int b);")?;
1693 let rs_api = generate_rs_api(&ir)?;
1694 assert_rs_matches!(
1695 rs_api,
1696 quote! {
Michael Forsterdb8101a2021-10-08 06:56:03 +00001697 #[inline(always)]
Marcel Hlopko89547752021-12-10 09:39:41 +00001698 pub fn Add(a: i32, b: i32) -> i32 {
Googlera675ae02021-12-07 08:04:59 +00001699 unsafe { crate::detail::__rust_thunk___Z3Addii(a, b) }
Marcel Hlopko89547752021-12-10 09:39:41 +00001700 }
1701 }
1702 );
1703 assert_rs_matches!(
1704 rs_api,
1705 quote! {
Michael Forsterdb8101a2021-10-08 06:56:03 +00001706 mod detail {
Googler55647142022-01-11 12:37:39 +00001707 #[allow(unused_imports)]
Devin Jeanpierred4dde0e2021-10-13 20:48:25 +00001708 use super::*;
Michael Forsterdb8101a2021-10-08 06:56:03 +00001709 extern "C" {
1710 #[link_name = "_Z3Addii"]
Googlera675ae02021-12-07 08:04:59 +00001711 pub(crate) fn __rust_thunk___Z3Addii(a: i32, b: i32) -> i32;
Marcel Hlopko89547752021-12-10 09:39:41 +00001712 }
1713 }
1714 }
Marcel Hlopko42abfc82021-08-09 07:03:17 +00001715 );
Michael Forsterdb8101a2021-10-08 06:56:03 +00001716
Marcel Hlopko89547752021-12-10 09:39:41 +00001717 assert_cc_not_matches!(generate_rs_api_impl(&ir)?, quote! {__rust_thunk___Z3Addii});
Michael Forsterdb8101a2021-10-08 06:56:03 +00001718
Marcel Hlopko3164eee2021-08-24 20:09:22 +00001719 Ok(())
1720 }
1721
1722 #[test]
1723 fn test_inline_function() -> Result<()> {
Marcel Hlopko89547752021-12-10 09:39:41 +00001724 let ir = ir_from_cc("inline int Add(int a, int b);")?;
1725 let rs_api = generate_rs_api(&ir)?;
1726 assert_rs_matches!(
1727 rs_api,
1728 quote! {
Michael Forsterdb8101a2021-10-08 06:56:03 +00001729 #[inline(always)]
Marcel Hlopko89547752021-12-10 09:39:41 +00001730 pub fn Add(a: i32, b: i32) -> i32 {
Googlera675ae02021-12-07 08:04:59 +00001731 unsafe { crate::detail::__rust_thunk___Z3Addii(a, b) }
Marcel Hlopko89547752021-12-10 09:39:41 +00001732 }
1733 }
1734 );
1735 assert_rs_matches!(
1736 rs_api,
1737 quote! {
Michael Forsterdb8101a2021-10-08 06:56:03 +00001738 mod detail {
Googler55647142022-01-11 12:37:39 +00001739 #[allow(unused_imports)]
Devin Jeanpierred4dde0e2021-10-13 20:48:25 +00001740 use super::*;
Michael Forsterdb8101a2021-10-08 06:56:03 +00001741 extern "C" {
Googlera675ae02021-12-07 08:04:59 +00001742 pub(crate) fn __rust_thunk___Z3Addii(a: i32, b: i32) -> i32;
Marcel Hlopko89547752021-12-10 09:39:41 +00001743 }
1744 }
1745 }
Marcel Hlopko3164eee2021-08-24 20:09:22 +00001746 );
1747
Marcel Hlopko89547752021-12-10 09:39:41 +00001748 assert_cc_matches!(
1749 generate_rs_api_impl(&ir)?,
1750 quote! {
Googlera675ae02021-12-07 08:04:59 +00001751 extern "C" int __rust_thunk___Z3Addii(int a, int b) {
Marcel Hlopko89547752021-12-10 09:39:41 +00001752 return Add(a, b);
Marcel Hlopko3164eee2021-08-24 20:09:22 +00001753 }
Marcel Hlopko89547752021-12-10 09:39:41 +00001754 }
Marcel Hlopko3164eee2021-08-24 20:09:22 +00001755 );
Marcel Hlopko42abfc82021-08-09 07:03:17 +00001756 Ok(())
1757 }
Marcel Hlopkob4b28742021-09-15 12:45:20 +00001758
1759 #[test]
Marcel Hlopkoa0f38662021-12-03 08:45:26 +00001760 fn test_simple_function_with_types_from_other_target() -> Result<()> {
1761 let ir = ir_from_cc_dependency(
1762 "inline ReturnStruct DoSomething(ParamStruct param);",
1763 "struct ReturnStruct {}; struct ParamStruct {};",
1764 )?;
1765
Marcel Hlopko89547752021-12-10 09:39:41 +00001766 let rs_api = generate_rs_api(&ir)?;
1767 assert_rs_matches!(
1768 rs_api,
1769 quote! {
Marcel Hlopkoa0f38662021-12-03 08:45:26 +00001770 #[inline(always)]
1771 pub fn DoSomething(param: dependency::ParamStruct)
Marcel Hlopko89547752021-12-10 09:39:41 +00001772 -> dependency::ReturnStruct {
Googlera675ae02021-12-07 08:04:59 +00001773 unsafe { crate::detail::__rust_thunk___Z11DoSomething11ParamStruct(param) }
Marcel Hlopko89547752021-12-10 09:39:41 +00001774 }
1775 }
1776 );
1777 assert_rs_matches!(
1778 rs_api,
1779 quote! {
1780 mod detail {
Googler55647142022-01-11 12:37:39 +00001781 #[allow(unused_imports)]
Marcel Hlopko89547752021-12-10 09:39:41 +00001782 use super::*;
1783 extern "C" {
1784 pub(crate) fn __rust_thunk___Z11DoSomething11ParamStruct(param: dependency::ParamStruct)
1785 -> dependency::ReturnStruct;
1786 }
1787 }}
Marcel Hlopkoa0f38662021-12-03 08:45:26 +00001788 );
1789
Marcel Hlopko89547752021-12-10 09:39:41 +00001790 assert_cc_matches!(
1791 generate_rs_api_impl(&ir)?,
1792 quote! {
Googler972d3582022-01-11 10:17:22 +00001793 extern "C" class ReturnStruct __rust_thunk___Z11DoSomething11ParamStruct(class ParamStruct param) {
Marcel Hlopkoa0f38662021-12-03 08:45:26 +00001794 return DoSomething(param);
1795 }
Marcel Hlopko89547752021-12-10 09:39:41 +00001796 }
Marcel Hlopkoa0f38662021-12-03 08:45:26 +00001797 );
1798 Ok(())
1799 }
1800
1801 #[test]
Marcel Hlopkob4b28742021-09-15 12:45:20 +00001802 fn test_simple_struct() -> Result<()> {
Marcel Hlopko89547752021-12-10 09:39:41 +00001803 let ir = ir_from_cc(&tokens_to_string(quote! {
Devin Jeanpierre88343c72022-01-15 01:10:23 +00001804 struct SomeStruct final {
Marcel Hlopko89547752021-12-10 09:39:41 +00001805 int public_int;
1806 protected:
1807 int protected_int;
1808 private:
1809 int private_int;
1810 };
1811 })?)?;
Michael Forster028800b2021-10-05 12:39:59 +00001812
Marcel Hlopko89547752021-12-10 09:39:41 +00001813 let rs_api = generate_rs_api(&ir)?;
1814 assert_rs_matches!(
1815 rs_api,
1816 quote! {
Michael Forsterdb8101a2021-10-08 06:56:03 +00001817 #[derive(Clone, Copy)]
1818 #[repr(C)]
1819 pub struct SomeStruct {
1820 pub public_int: i32,
1821 protected_int: i32,
1822 private_int: i32,
Marcel Hlopko89547752021-12-10 09:39:41 +00001823 }
1824 }
1825 );
1826 assert_rs_matches!(
1827 rs_api,
1828 quote! {
Googler454f2652021-12-06 12:53:12 +00001829 const _: () = assert!(std::mem::size_of::<Option<&i32>>() == std::mem::size_of::<&i32>());
Googler209b10a2021-12-06 09:11:57 +00001830 const _: () = assert!(std::mem::size_of::<SomeStruct>() == 12usize);
1831 const _: () = assert!(std::mem::align_of::<SomeStruct>() == 4usize);
1832 const _: () = assert!(offset_of!(SomeStruct, public_int) * 8 == 0usize);
1833 const _: () = assert!(offset_of!(SomeStruct, protected_int) * 8 == 32usize);
1834 const _: () = assert!(offset_of!(SomeStruct, private_int) * 8 == 64usize);
Marcel Hlopko89547752021-12-10 09:39:41 +00001835 }
Marcel Hlopkob4b28742021-09-15 12:45:20 +00001836 );
Marcel Hlopko89547752021-12-10 09:39:41 +00001837 let rs_api_impl = generate_rs_api_impl(&ir)?;
1838 assert_cc_matches!(
1839 rs_api_impl,
1840 quote! {
Googler972d3582022-01-11 10:17:22 +00001841 extern "C" void __rust_thunk___ZN10SomeStructD1Ev(class SomeStruct * __this) {
Lukasz Anforowicze643ec92021-12-22 15:45:15 +00001842 std :: destroy_at (__this) ;
Marcel Hlopko89547752021-12-10 09:39:41 +00001843 }
1844 }
1845 );
1846 assert_cc_matches!(
1847 rs_api_impl,
1848 quote! {
Googler972d3582022-01-11 10:17:22 +00001849 static_assert(sizeof(class SomeStruct) == 12);
1850 static_assert(alignof(class SomeStruct) == 4);
1851 static_assert(offsetof(class SomeStruct, public_int) * 8 == 0);
Marcel Hlopko89547752021-12-10 09:39:41 +00001852 }
Googler5ea88642021-09-29 08:05:59 +00001853 );
Marcel Hlopkob4b28742021-09-15 12:45:20 +00001854 Ok(())
1855 }
Devin Jeanpierre7a7328e2021-09-17 07:10:08 +00001856
Devin Jeanpierre2ed14ec2021-10-06 11:32:19 +00001857 #[test]
Lukasz Anforowicz275fa922022-01-05 16:13:20 +00001858 fn test_ref_to_struct_in_thunk_impls() -> Result<()> {
Googler972d3582022-01-11 10:17:22 +00001859 let ir = ir_from_cc("struct S{}; inline void foo(class S& s) {} ")?;
Lukasz Anforowicz275fa922022-01-05 16:13:20 +00001860 let rs_api_impl = generate_rs_api_impl(&ir)?;
1861 assert_cc_matches!(
1862 rs_api_impl,
1863 quote! {
Googler972d3582022-01-11 10:17:22 +00001864 extern "C" void __rust_thunk___Z3fooR1S(class S& s) {
Lukasz Anforowicz275fa922022-01-05 16:13:20 +00001865 foo(s);
1866 }
1867 }
1868 );
1869 Ok(())
1870 }
1871
1872 #[test]
1873 fn test_const_ref_to_struct_in_thunk_impls() -> Result<()> {
Googler972d3582022-01-11 10:17:22 +00001874 let ir = ir_from_cc("struct S{}; inline void foo(const class S& s) {} ")?;
Lukasz Anforowicz275fa922022-01-05 16:13:20 +00001875 let rs_api_impl = generate_rs_api_impl(&ir)?;
1876 assert_cc_matches!(
1877 rs_api_impl,
1878 quote! {
Googler972d3582022-01-11 10:17:22 +00001879 extern "C" void __rust_thunk___Z3fooRK1S(const class S& s) {
Lukasz Anforowicz275fa922022-01-05 16:13:20 +00001880 foo(s);
1881 }
1882 }
1883 );
1884 Ok(())
1885 }
1886
1887 #[test]
Lukasz Anforowicz957cbf22022-01-05 16:14:05 +00001888 fn test_unsigned_int_in_thunk_impls() -> Result<()> {
1889 let ir = ir_from_cc("inline void foo(unsigned int i) {} ")?;
1890 let rs_api_impl = generate_rs_api_impl(&ir)?;
1891 assert_cc_matches!(
1892 rs_api_impl,
1893 quote! {
1894 extern "C" void __rust_thunk___Z3fooj(unsigned int i) {
1895 foo(i);
1896 }
1897 }
1898 );
1899 Ok(())
1900 }
1901
1902 #[test]
Marcel Hlopkodd1fcb12021-12-22 14:13:59 +00001903 fn test_record_static_methods_qualify_call_in_thunk() -> Result<()> {
1904 let ir = ir_from_cc(&tokens_to_string(quote! {
1905 struct SomeStruct {
1906 static inline int some_func() { return 42; }
1907 };
1908 })?)?;
1909
1910 assert_cc_matches!(
1911 generate_rs_api_impl(&ir)?,
1912 quote! {
1913 extern "C" int __rust_thunk___ZN10SomeStruct9some_funcEv() {
1914 return SomeStruct::some_func();
1915 }
1916 }
1917 );
1918 Ok(())
1919 }
1920
1921 #[test]
Lukasz Anforowiczb3d89aa2022-01-12 14:35:52 +00001922 fn test_record_instance_methods_deref_this_in_thunk() -> Result<()> {
1923 let ir = ir_from_cc(&tokens_to_string(quote! {
1924 struct SomeStruct {
1925 inline int some_func(int arg) const { return 42 + arg; }
1926 };
1927 })?)?;
1928
1929 assert_cc_matches!(
1930 generate_rs_api_impl(&ir)?,
1931 quote! {
1932 extern "C" int __rust_thunk___ZNK10SomeStruct9some_funcEi(
1933 const class SomeStruct* __this, int arg) {
1934 return __this->some_func(arg);
1935 }
1936 }
1937 );
1938 Ok(())
1939 }
1940
1941 #[test]
Marcel Hlopkoa0f38662021-12-03 08:45:26 +00001942 fn test_struct_from_other_target() -> Result<()> {
1943 let ir = ir_from_cc_dependency("// intentionally empty", "struct SomeStruct {};")?;
Marcel Hlopko89547752021-12-10 09:39:41 +00001944 assert_rs_not_matches!(generate_rs_api(&ir)?, quote! { SomeStruct });
1945 assert_cc_not_matches!(generate_rs_api_impl(&ir)?, quote! { SomeStruct });
Marcel Hlopkoa0f38662021-12-03 08:45:26 +00001946 Ok(())
1947 }
1948
1949 #[test]
Devin Jeanpierre2ed14ec2021-10-06 11:32:19 +00001950 fn test_copy_derives() {
Devin Jeanpierreccfefc82021-10-27 10:54:00 +00001951 let record = ir_record("S");
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00001952 assert_eq!(generate_derives(&record), &["Clone", "Copy"]);
Devin Jeanpierre2ed14ec2021-10-06 11:32:19 +00001953 }
1954
1955 #[test]
1956 fn test_copy_derives_not_is_trivial_abi() {
Devin Jeanpierreccfefc82021-10-27 10:54:00 +00001957 let mut record = ir_record("S");
Devin Jeanpierre2ed14ec2021-10-06 11:32:19 +00001958 record.is_trivial_abi = false;
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00001959 assert_eq!(generate_derives(&record), &[""; 0]);
Devin Jeanpierre2ed14ec2021-10-06 11:32:19 +00001960 }
1961
Devin Jeanpierre88343c72022-01-15 01:10:23 +00001962 /// Even if it's trivially relocatable, !Unpin C++ type cannot be
1963 /// cloned/copied or otherwise used by value, because values would allow
1964 /// assignment into the Pin.
1965 ///
1966 /// All !Unpin C++ types, not just non trivially relocatable ones, are
1967 /// unsafe to assign in the Rust sense.
Devin Jeanpierree6e16652021-12-22 15:54:46 +00001968 #[test]
Devin Jeanpierre88343c72022-01-15 01:10:23 +00001969 fn test_copy_derives_not_final() {
Devin Jeanpierree6e16652021-12-22 15:54:46 +00001970 let mut record = ir_record("S");
1971 record.is_final = false;
Devin Jeanpierre88343c72022-01-15 01:10:23 +00001972 assert_eq!(generate_derives(&record), &[""; 0]);
Devin Jeanpierree6e16652021-12-22 15:54:46 +00001973 }
1974
Devin Jeanpierre2ed14ec2021-10-06 11:32:19 +00001975 #[test]
1976 fn test_copy_derives_ctor_nonpublic() {
Devin Jeanpierreccfefc82021-10-27 10:54:00 +00001977 let mut record = ir_record("S");
Devin Jeanpierre2ed14ec2021-10-06 11:32:19 +00001978 for access in [ir::AccessSpecifier::Protected, ir::AccessSpecifier::Private] {
1979 record.copy_constructor.access = access;
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00001980 assert_eq!(generate_derives(&record), &[""; 0]);
Devin Jeanpierre2ed14ec2021-10-06 11:32:19 +00001981 }
1982 }
1983
1984 #[test]
1985 fn test_copy_derives_ctor_deleted() {
Devin Jeanpierreccfefc82021-10-27 10:54:00 +00001986 let mut record = ir_record("S");
Devin Jeanpierre2ed14ec2021-10-06 11:32:19 +00001987 record.copy_constructor.definition = ir::SpecialMemberDefinition::Deleted;
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00001988 assert_eq!(generate_derives(&record), &[""; 0]);
Devin Jeanpierre2ed14ec2021-10-06 11:32:19 +00001989 }
1990
1991 #[test]
Devin Jeanpierrebe2f33b2021-10-21 12:54:19 +00001992 fn test_copy_derives_ctor_nontrivial_members() {
Devin Jeanpierreccfefc82021-10-27 10:54:00 +00001993 let mut record = ir_record("S");
Devin Jeanpierrebe2f33b2021-10-21 12:54:19 +00001994 record.copy_constructor.definition = ir::SpecialMemberDefinition::NontrivialMembers;
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00001995 assert_eq!(generate_derives(&record), &[""; 0]);
Devin Jeanpierrebe2f33b2021-10-21 12:54:19 +00001996 }
1997
1998 #[test]
1999 fn test_copy_derives_ctor_nontrivial_self() {
Devin Jeanpierreccfefc82021-10-27 10:54:00 +00002000 let mut record = ir_record("S");
Devin Jeanpierre7b62e952021-12-08 21:43:30 +00002001 record.copy_constructor.definition = ir::SpecialMemberDefinition::NontrivialUserDefined;
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00002002 assert_eq!(generate_derives(&record), &[""; 0]);
Devin Jeanpierre2ed14ec2021-10-06 11:32:19 +00002003 }
2004
Devin Jeanpierre7a7328e2021-09-17 07:10:08 +00002005 #[test]
2006 fn test_ptr_func() -> Result<()> {
Marcel Hlopko89547752021-12-10 09:39:41 +00002007 let ir = ir_from_cc(&tokens_to_string(quote! {
2008 inline int* Deref(int*const* p);
2009 })?)?;
Devin Jeanpierred6da7002021-10-21 12:55:20 +00002010
Marcel Hlopko89547752021-12-10 09:39:41 +00002011 let rs_api = generate_rs_api(&ir)?;
2012 assert_rs_matches!(
2013 rs_api,
2014 quote! {
Michael Forsterdb8101a2021-10-08 06:56:03 +00002015 #[inline(always)]
Lukasz Anforowiczf7bdd392022-01-21 00:33:39 +00002016 pub unsafe fn Deref(p: *const *mut i32) -> *mut i32 {
2017 crate::detail::__rust_thunk___Z5DerefPKPi(p)
Marcel Hlopko89547752021-12-10 09:39:41 +00002018 }
2019 }
2020 );
2021 assert_rs_matches!(
2022 rs_api,
2023 quote! {
Michael Forsterdb8101a2021-10-08 06:56:03 +00002024 mod detail {
Googler55647142022-01-11 12:37:39 +00002025 #[allow(unused_imports)]
Devin Jeanpierred4dde0e2021-10-13 20:48:25 +00002026 use super::*;
Michael Forsterdb8101a2021-10-08 06:56:03 +00002027 extern "C" {
Googlera675ae02021-12-07 08:04:59 +00002028 pub(crate) fn __rust_thunk___Z5DerefPKPi(p: *const *mut i32) -> *mut i32;
Marcel Hlopko89547752021-12-10 09:39:41 +00002029 }
2030 }
2031 }
Devin Jeanpierre7a7328e2021-09-17 07:10:08 +00002032 );
Devin Jeanpierre184f9ac2021-09-17 13:47:03 +00002033
Marcel Hlopko89547752021-12-10 09:39:41 +00002034 assert_cc_matches!(
2035 generate_rs_api_impl(&ir)?,
2036 quote! {
Googlera675ae02021-12-07 08:04:59 +00002037 extern "C" int* __rust_thunk___Z5DerefPKPi(int* const * p) {
Devin Jeanpierre184f9ac2021-09-17 13:47:03 +00002038 return Deref(p);
2039 }
Marcel Hlopko89547752021-12-10 09:39:41 +00002040 }
Devin Jeanpierre184f9ac2021-09-17 13:47:03 +00002041 );
Devin Jeanpierre7a7328e2021-09-17 07:10:08 +00002042 Ok(())
2043 }
Michael Forstered642022021-10-04 09:48:25 +00002044
2045 #[test]
Googlerdb111532022-01-05 06:12:13 +00002046 fn test_const_char_ptr_func() -> Result<()> {
2047 // This is a regression test: We used to include the "const" in the name
2048 // of the CcType, which caused a panic in the code generator
2049 // ('"const char" is not a valid Ident').
2050 // It's therefore important that f() is inline so that we need to
2051 // generate a thunk for it (where we then process the CcType).
2052 let ir = ir_from_cc(&tokens_to_string(quote! {
2053 inline void f(const char *str);
2054 })?)?;
2055
2056 let rs_api = generate_rs_api(&ir)?;
2057 assert_rs_matches!(
2058 rs_api,
2059 quote! {
2060 #[inline(always)]
Lukasz Anforowiczf7bdd392022-01-21 00:33:39 +00002061 pub unsafe fn f(str: *const i8) {
2062 crate::detail::__rust_thunk___Z1fPKc(str)
Googlerdb111532022-01-05 06:12:13 +00002063 }
2064 }
2065 );
2066 assert_rs_matches!(
2067 rs_api,
2068 quote! {
2069 extern "C" {
2070 pub(crate) fn __rust_thunk___Z1fPKc(str: *const i8);
2071 }
2072 }
2073 );
2074
2075 assert_cc_matches!(
2076 generate_rs_api_impl(&ir)?,
2077 quote! {
2078 extern "C" void __rust_thunk___Z1fPKc(char const * str){ f(str) ; }
2079 }
2080 );
2081 Ok(())
2082 }
2083
2084 #[test]
Lukasz Anforowiczcf230fd2022-02-18 19:20:39 +00002085 fn test_func_ptr_where_params_are_primitive_types() -> Result<()> {
2086 let ir = ir_from_cc(r#" int (*get_ptr_to_func())(float, double); "#)?;
2087 let rs_api = generate_rs_api(&ir)?;
2088 let rs_api_impl = generate_rs_api_impl(&ir)?;
2089 assert_rs_matches!(
2090 rs_api,
2091 quote! {
2092 #[inline(always)]
2093 pub fn get_ptr_to_func() -> Option<extern "C" fn (f32, f64) -> i32> {
2094 unsafe { crate::detail::__rust_thunk___Z15get_ptr_to_funcv() }
2095 }
2096 }
2097 );
2098 assert_rs_matches!(
2099 rs_api,
2100 quote! {
2101 mod detail {
2102 #[allow(unused_imports)]
2103 use super::*;
2104 extern "C" {
2105 #[link_name = "_Z15get_ptr_to_funcv"]
2106 pub(crate) fn __rust_thunk___Z15get_ptr_to_funcv()
2107 -> Option<extern "C" fn(f32, f64) -> i32>;
2108 }
2109 }
2110 }
2111 );
2112 // Verify that no C++ thunk got generated.
2113 assert_cc_not_matches!(rs_api_impl, quote! { __rust_thunk___Z15get_ptr_to_funcv });
2114
2115 // TODO(b/217419782): Add another test for more exotic calling conventions /
2116 // abis.
2117
2118 // TODO(b/217419782): Add another test for pointer to a function that
2119 // takes/returns non-trivially-movable types by value. See also
2120 // <internal link>
2121
2122 Ok(())
2123 }
2124
2125 #[test]
2126 fn test_func_ptr_with_non_static_lifetime() -> Result<()> {
2127 let ir = ir_from_cc(
2128 r#"
2129 [[clang::annotate("lifetimes = -> a")]]
2130 int (*get_ptr_to_func())(float, double); "#,
2131 )?;
2132 let rs_api = generate_rs_api(&ir)?;
2133 assert_rs_matches!(
2134 rs_api,
2135 quote! {
2136 // Error while generating bindings for item 'get_ptr_to_func':
2137 // Return type is not supported: Function pointers with non-'static lifetimes are not supported: int (*)(float, double)
2138 }
2139 );
2140 Ok(())
2141 }
2142
2143 #[test]
2144 fn test_func_ptr_where_params_are_raw_ptrs() -> Result<()> {
2145 let ir = ir_from_cc(r#" const int* (*get_ptr_to_func())(const int*); "#)?;
2146 let rs_api = generate_rs_api(&ir)?;
2147 let rs_api_impl = generate_rs_api_impl(&ir)?;
2148 assert_rs_matches!(
2149 rs_api,
2150 quote! {
2151 #[inline(always)]
2152 pub fn get_ptr_to_func() -> Option<extern "C" fn (*const i32) -> *const i32> {
2153 unsafe { crate::detail::__rust_thunk___Z15get_ptr_to_funcv() }
2154 }
2155 }
2156 );
2157 assert_rs_matches!(
2158 rs_api,
2159 quote! {
2160 mod detail {
2161 #[allow(unused_imports)]
2162 use super::*;
2163 extern "C" {
2164 #[link_name = "_Z15get_ptr_to_funcv"]
2165 pub(crate) fn __rust_thunk___Z15get_ptr_to_funcv()
2166 -> Option<extern "C" fn(*const i32) -> *const i32>;
2167 }
2168 }
2169 }
2170 );
2171 // Verify that no C++ thunk got generated.
2172 assert_cc_not_matches!(rs_api_impl, quote! { __rust_thunk___Z15get_ptr_to_funcv });
2173
2174 // TODO(b/217419782): Add another test where params (and the return
2175 // type) are references with lifetimes. Something like this:
2176 // #pragma clang lifetime_elision
2177 // const int& (*get_ptr_to_func())(const int&, const int&); "#)?;
2178 // 1) Need to investigate why this fails - seeing raw pointers in Rust
2179 // seems to indicate that no lifetimes are present at the `importer.cc`
2180 // level. Maybe lifetime elision doesn't support this scenario? Unclear
2181 // how to explicitly apply [[clang::annotate("lifetimes = a, b -> a")]]
2182 // to the _inner_ function.
2183 // 2) It is important to have 2 reference parameters, so see if the problem
2184 // of passing `lifetimes` by value would have been caught - see:
2185 // cl/428079010/depot/rs_bindings_from_cc/
2186 // importer.cc?version=s6#823
2187
2188 // TODO(b/217419782): Decide what to do if the C++ pointer is *not*
2189 // annotated with a lifetime - emit `unsafe fn(...) -> ...` in that
2190 // case?
2191
2192 Ok(())
2193 }
2194
2195 #[test]
Michael Forstered642022021-10-04 09:48:25 +00002196 fn test_item_order() -> Result<()> {
Marcel Hlopko89547752021-12-10 09:39:41 +00002197 let ir = ir_from_cc(
2198 "int first_func();
2199 struct FirstStruct {};
2200 int second_func();
2201 struct SecondStruct {};",
2202 )?;
Michael Forstered642022021-10-04 09:48:25 +00002203
Marcel Hlopko89547752021-12-10 09:39:41 +00002204 let rs_api = rs_tokens_to_formatted_string(generate_rs_api(&ir)?)?;
2205
Lukasz Anforowiczdd9ae0f2022-02-17 15:52:53 +00002206 let idx = |s: &str| rs_api.find(s).ok_or_else(|| anyhow!("'{}' missing", s));
Michael Forstered642022021-10-04 09:48:25 +00002207
2208 let f1 = idx("fn first_func")?;
2209 let f2 = idx("fn second_func")?;
2210 let s1 = idx("struct FirstStruct")?;
2211 let s2 = idx("struct SecondStruct")?;
Googlera675ae02021-12-07 08:04:59 +00002212 let t1 = idx("fn __rust_thunk___Z10first_funcv")?;
2213 let t2 = idx("fn __rust_thunk___Z11second_funcv")?;
Michael Forstered642022021-10-04 09:48:25 +00002214
2215 assert!(f1 < s1);
2216 assert!(s1 < f2);
2217 assert!(f2 < s2);
2218 assert!(s2 < t1);
2219 assert!(t1 < t2);
2220
2221 Ok(())
2222 }
Michael Forster028800b2021-10-05 12:39:59 +00002223
2224 #[test]
Devin Jeanpierrec80e6242022-02-03 01:56:40 +00002225 fn test_base_class_subobject_layout() -> Result<()> {
2226 let ir = ir_from_cc(
2227 r#"
2228 // We use a class here to force `Derived::z` to live inside the tail padding of `Base`.
2229 // On the Itanium ABI, this would not happen if `Base` were a POD type.
Devin Jeanpierre56777022022-02-03 01:57:15 +00002230 class Base {__INT64_TYPE__ x; char y;};
2231 struct Derived final : Base {__INT16_TYPE__ z;};
Devin Jeanpierrec80e6242022-02-03 01:56:40 +00002232 "#,
2233 )?;
2234 let rs_api = generate_rs_api(&ir)?;
2235 assert_rs_matches!(
2236 rs_api,
2237 quote! {
2238 #[repr(C, align(8))]
2239 pub struct Derived {
2240 __base_class_subobjects: [std::mem::MaybeUninit<u8>; 10],
2241 pub z: i16,
2242 }
2243 }
2244 );
2245 Ok(())
2246 }
2247
2248 /// The same as test_base_class_subobject_layout, but with multiple
2249 /// inheritance.
2250 #[test]
2251 fn test_base_class_multiple_inheritance_subobject_layout() -> Result<()> {
2252 let ir = ir_from_cc(
2253 r#"
Devin Jeanpierre56777022022-02-03 01:57:15 +00002254 class Base1 {__INT64_TYPE__ x;};
Devin Jeanpierrec80e6242022-02-03 01:56:40 +00002255 class Base2 {char y;};
Devin Jeanpierre56777022022-02-03 01:57:15 +00002256 struct Derived final : Base1, Base2 {__INT16_TYPE__ z;};
Devin Jeanpierrec80e6242022-02-03 01:56:40 +00002257 "#,
2258 )?;
2259 let rs_api = generate_rs_api(&ir)?;
2260 assert_rs_matches!(
2261 rs_api,
2262 quote! {
2263 #[repr(C, align(8))]
2264 pub struct Derived {
2265 __base_class_subobjects: [std::mem::MaybeUninit<u8>; 10],
2266 pub z: i16,
2267 }
2268 }
2269 );
2270 Ok(())
2271 }
2272
2273 /// The same as test_base_class_subobject_layout, but with a chain of
2274 /// inheritance.
2275 #[test]
2276 fn test_base_class_deep_inheritance_subobject_layout() -> Result<()> {
2277 let ir = ir_from_cc(
2278 r#"
Devin Jeanpierre56777022022-02-03 01:57:15 +00002279 class Base1 {__INT64_TYPE__ x;};
Devin Jeanpierrec80e6242022-02-03 01:56:40 +00002280 class Base2 : Base1 {char y;};
Devin Jeanpierre56777022022-02-03 01:57:15 +00002281 struct Derived final : Base2 {__INT16_TYPE__ z;};
Devin Jeanpierrec80e6242022-02-03 01:56:40 +00002282 "#,
2283 )?;
2284 let rs_api = generate_rs_api(&ir)?;
2285 assert_rs_matches!(
2286 rs_api,
2287 quote! {
2288 #[repr(C, align(8))]
2289 pub struct Derived {
2290 __base_class_subobjects: [std::mem::MaybeUninit<u8>; 10],
2291 pub z: i16,
2292 }
2293 }
2294 );
2295 Ok(())
2296 }
2297
2298 /// For derived classes with no data members, we can't use the offset of the
2299 /// first member to determine the size of the base class subobjects.
2300 #[test]
2301 fn test_base_class_subobject_fieldless_layout() -> Result<()> {
2302 let ir = ir_from_cc(
2303 r#"
Devin Jeanpierre56777022022-02-03 01:57:15 +00002304 class Base {__INT64_TYPE__ x; char y;};
Devin Jeanpierrec80e6242022-02-03 01:56:40 +00002305 struct Derived final : Base {};
2306 "#,
2307 )?;
2308 let rs_api = generate_rs_api(&ir)?;
2309 assert_rs_matches!(
2310 rs_api,
2311 quote! {
2312 #[repr(C, align(8))]
2313 pub struct Derived {
2314 __base_class_subobjects: [std::mem::MaybeUninit<u8>; 9],
2315 }
2316 }
2317 );
2318 Ok(())
2319 }
2320
2321 #[test]
2322 fn test_base_class_subobject_empty_fieldless() -> Result<()> {
2323 let ir = ir_from_cc(
2324 r#"
2325 class Base {};
2326 struct Derived final : Base {};
2327 "#,
2328 )?;
2329 let rs_api = generate_rs_api(&ir)?;
2330 assert_rs_matches!(
2331 rs_api,
2332 quote! {
2333 #[repr(C)]
2334 pub struct Derived {
2335 __base_class_subobjects: [std::mem::MaybeUninit<u8>; 0],
2336 /// Prevent empty C++ struct being zero-size in Rust.
2337 placeholder: std::mem::MaybeUninit<u8>,
2338 }
2339 }
2340 );
2341 Ok(())
2342 }
2343
2344 #[test]
2345 fn test_base_class_subobject_empty() -> Result<()> {
2346 let ir = ir_from_cc(
2347 r#"
2348 class Base {};
2349 struct Derived final : Base {};
2350 "#,
2351 )?;
2352 let rs_api = generate_rs_api(&ir)?;
2353 assert_rs_matches!(
2354 rs_api,
2355 quote! {
2356 #[repr(C)]
2357 pub struct Derived {
2358 __base_class_subobjects: [std::mem::MaybeUninit<u8>; 0],
2359 /// Prevent empty C++ struct being zero-size in Rust.
2360 placeholder: std::mem::MaybeUninit<u8>,
2361 }
2362 }
2363 );
2364 Ok(())
2365 }
2366
Devin Jeanpierreb69bcae2022-02-03 09:45:50 +00002367 /// When a field is [[no_unique_address]], it occupies the space up to the
2368 /// next field.
2369 #[test]
2370 fn test_no_unique_address() -> Result<()> {
2371 let ir = ir_from_cc(
2372 r#"
2373 class Field1 {__INT64_TYPE__ x;};
2374 class Field2 {char y;};
2375 struct Struct final {
2376 [[no_unique_address]] Field1 field1;
2377 [[no_unique_address]] Field2 field2;
2378 __INT16_TYPE__ z;
2379 };
2380 "#,
2381 )?;
2382 let rs_api = generate_rs_api(&ir)?;
2383 assert_rs_matches!(
2384 rs_api,
2385 quote! {
2386 #[derive(Clone, Copy)]
2387 #[repr(C, align(8))]
2388 pub struct Struct {
2389 field1: [std::mem::MaybeUninit<u8>; 8],
2390 field2: [std::mem::MaybeUninit<u8>; 2],
2391 pub z: i16,
2392 }
Devin Jeanpierre58181ac2022-02-14 21:30:05 +00002393
2394 impl Struct {
2395 pub fn field1(&self) -> &Field1 {
2396 unsafe {&* (&self.field1 as *const _ as *const Field1)}
2397 }
2398 pub fn field2(&self) -> &Field2 {
2399 unsafe {&* (&self.field2 as *const _ as *const Field2)}
2400 }
2401 }
Devin Jeanpierreb69bcae2022-02-03 09:45:50 +00002402 }
2403 );
2404 Ok(())
2405 }
2406
2407 /// When a [[no_unique_address]] field is the last one, it occupies the rest
2408 /// of the object.
2409 #[test]
2410 fn test_no_unique_address_last_field() -> Result<()> {
2411 let ir = ir_from_cc(
2412 r#"
2413 class Field1 {__INT64_TYPE__ x;};
2414 class Field2 {char y;};
2415 struct Struct final {
2416 [[no_unique_address]] Field1 field1;
2417 [[no_unique_address]] Field2 field2;
2418 };
2419 "#,
2420 )?;
2421 let rs_api = generate_rs_api(&ir)?;
2422 assert_rs_matches!(
2423 rs_api,
2424 quote! {
2425 #[derive(Clone, Copy)]
2426 #[repr(C, align(8))]
2427 pub struct Struct {
2428 field1: [std::mem::MaybeUninit<u8>; 8],
2429 field2: [std::mem::MaybeUninit<u8>; 8],
2430 }
2431 }
2432 );
2433 Ok(())
2434 }
2435
2436 #[test]
2437 fn test_no_unique_address_empty() -> Result<()> {
2438 let ir = ir_from_cc(
2439 r#"
2440 class Field {};
2441 struct Struct final {
2442 [[no_unique_address]] Field field;
2443 int x;
2444 };
2445 "#,
2446 )?;
2447 let rs_api = generate_rs_api(&ir)?;
2448 assert_rs_matches!(
2449 rs_api,
2450 quote! {
2451 #[repr(C, align(4))]
2452 pub struct Struct {
2453 field: [std::mem::MaybeUninit<u8>; 0],
2454 pub x: i32,
2455 }
2456 }
2457 );
2458 Ok(())
2459 }
2460
2461 #[test]
2462 fn test_base_class_subobject_empty_last_field() -> Result<()> {
2463 let ir = ir_from_cc(
2464 r#"
2465 class Field {};
2466 struct Struct final {
2467 [[no_unique_address]] Field field;
2468 };
2469 "#,
2470 )?;
2471 let rs_api = generate_rs_api(&ir)?;
2472 assert_rs_matches!(
2473 rs_api,
2474 quote! {
2475 #[repr(C)]
2476 pub struct Struct {
2477 field: [std::mem::MaybeUninit<u8>; 1],
2478 }
2479 }
2480 );
2481 Ok(())
2482 }
2483
Devin Jeanpierrec80e6242022-02-03 01:56:40 +00002484 #[test]
Michael Forster409d9412021-10-07 08:35:29 +00002485 fn test_doc_comment_func() -> Result<()> {
Marcel Hlopko89547752021-12-10 09:39:41 +00002486 let ir = ir_from_cc(
2487 "
2488 // Doc Comment
2489 // with two lines
2490 int func();",
2491 )?;
Michael Forster409d9412021-10-07 08:35:29 +00002492
Marcel Hlopko89547752021-12-10 09:39:41 +00002493 assert_rs_matches!(
2494 generate_rs_api(&ir)?,
2495 // leading space is intentional so there is a space between /// and the text of the
2496 // comment
2497 quote! {
2498 #[doc = " Doc Comment\n with two lines"]
2499 #[inline(always)]
2500 pub fn func
2501 }
Michael Forster409d9412021-10-07 08:35:29 +00002502 );
2503
2504 Ok(())
2505 }
2506
2507 #[test]
2508 fn test_doc_comment_record() -> Result<()> {
Marcel Hlopko89547752021-12-10 09:39:41 +00002509 let ir = ir_from_cc(
2510 "// Doc Comment\n\
2511 //\n\
2512 // * with bullet\n\
Devin Jeanpierre88343c72022-01-15 01:10:23 +00002513 struct SomeStruct final {\n\
Marcel Hlopko89547752021-12-10 09:39:41 +00002514 // Field doc\n\
2515 int field;\
2516 };",
2517 )?;
Michael Forster028800b2021-10-05 12:39:59 +00002518
Marcel Hlopko89547752021-12-10 09:39:41 +00002519 assert_rs_matches!(
2520 generate_rs_api(&ir)?,
2521 quote! {
2522 #[doc = " Doc Comment\n \n * with bullet"]
2523 #[derive(Clone, Copy)]
2524 #[repr(C)]
2525 pub struct SomeStruct {
2526 # [doc = " Field doc"]
2527 pub field: i32,
2528 }
2529 }
Michael Forstercc5941a2021-10-07 07:12:24 +00002530 );
Michael Forster028800b2021-10-05 12:39:59 +00002531 Ok(())
2532 }
Devin Jeanpierre91de7012021-10-21 12:53:51 +00002533
Devin Jeanpierre96839c12021-12-14 00:27:38 +00002534 #[test]
Devin Jeanpierre56777022022-02-03 01:57:15 +00002535 fn test_unambiguous_public_bases() -> Result<()> {
2536 let ir = ir_from_cc_dependency(
2537 "
2538 struct VirtualBase {};
2539 struct PrivateBase {};
2540 struct ProtectedBase {};
2541 struct UnambiguousPublicBase {};
2542 struct AmbiguousPublicBase {};
2543 struct MultipleInheritance : UnambiguousPublicBase, AmbiguousPublicBase {};
2544 struct Derived : private PrivateBase, protected ProtectedBase, MultipleInheritance, AmbiguousPublicBase, virtual VirtualBase {};
2545 ",
2546 "",
2547 )?;
2548 let rs_api = generate_rs_api(&ir)?;
2549 // TODO(b/216195042): virtual bases.
2550 assert_rs_not_matches!(rs_api, quote! { From<&'a Derived> for &'a VirtualBase });
2551 assert_rs_matches!(rs_api, quote! { From<&'a Derived> for &'a UnambiguousPublicBase });
2552 assert_rs_matches!(rs_api, quote! { From<&'a Derived> for &'a MultipleInheritance });
2553 assert_rs_not_matches!(rs_api, quote! {From<&'a Derived> for &'a PrivateBase});
2554 assert_rs_not_matches!(rs_api, quote! {From<&'a Derived> for &'a ProtectedBase});
2555 assert_rs_not_matches!(rs_api, quote! {From<&'a Derived> for &'a AmbiguousPublicBase});
2556 Ok(())
2557 }
2558
2559 /// Contrary to intuitions: a base class conversion is ambiguous even if the
2560 /// ambiguity is from a private base class cast that you can't even
2561 /// perform.
2562 ///
2563 /// Explanation (courtesy James Dennett):
2564 ///
2565 /// > Once upon a time, there was a rule in C++ that changing all access
2566 /// > specifiers to "public" would not change the meaning of code.
2567 /// > That's no longer true, but some of its effects can still be seen.
2568 ///
2569 /// So, we need to be sure to not allow casting to privately-ambiguous
2570 /// bases.
2571 #[test]
2572 fn test_unambiguous_public_bases_private_ambiguity() -> Result<()> {
2573 let ir = ir_from_cc_dependency(
2574 "
2575 struct Base {};
2576 struct Intermediate : public Base {};
2577 struct Derived : Base, private Intermediate {};
2578 ",
2579 "",
2580 )?;
2581 let rs_api = generate_rs_api(&ir)?;
2582 assert_rs_not_matches!(rs_api, quote! { From<&'a Derived> for &'a Base });
2583 Ok(())
2584 }
2585
2586 #[test]
Devin Jeanpierre96839c12021-12-14 00:27:38 +00002587 fn test_virtual_thunk() -> Result<()> {
2588 let ir = ir_from_cc("struct Polymorphic { virtual void Foo(); };")?;
2589
2590 assert_cc_matches!(
2591 generate_rs_api_impl(&ir)?,
2592 quote! {
Googler972d3582022-01-11 10:17:22 +00002593 extern "C" void __rust_thunk___ZN11Polymorphic3FooEv(class Polymorphic * __this)
Devin Jeanpierre96839c12021-12-14 00:27:38 +00002594 }
2595 );
2596 Ok(())
2597 }
2598
Devin Jeanpierree6e16652021-12-22 15:54:46 +00002599 /// A trivially relocatable final struct is safe to use in Rust as normal,
2600 /// and is Unpin.
2601 #[test]
2602 fn test_no_negative_impl_unpin() -> Result<()> {
2603 let ir = ir_from_cc("struct Trivial final {};")?;
2604 let rs_api = generate_rs_api(&ir)?;
2605 assert_rs_not_matches!(rs_api, quote! {impl !Unpin});
2606 Ok(())
2607 }
2608
2609 /// A non-final struct, even if it's trivial, is not usable by mut
2610 /// reference, and so is !Unpin.
2611 #[test]
2612 fn test_negative_impl_unpin_nonfinal() -> Result<()> {
2613 let ir = ir_from_cc("struct Nonfinal {};")?;
2614 let rs_api = generate_rs_api(&ir)?;
2615 assert_rs_matches!(rs_api, quote! {impl !Unpin for Nonfinal {}});
2616 Ok(())
2617 }
2618
Devin Jeanpierre91de7012021-10-21 12:53:51 +00002619 /// At the least, a trivial type should have no drop impl if or until we add
2620 /// empty drop impls.
2621 #[test]
2622 fn test_no_impl_drop() -> Result<()> {
Googler7cced422021-12-06 11:58:39 +00002623 let ir = ir_from_cc("struct Trivial {};")?;
Marcel Hlopko89547752021-12-10 09:39:41 +00002624 let rs_api = rs_tokens_to_formatted_string(generate_rs_api(&ir)?)?;
Devin Jeanpierre91de7012021-10-21 12:53:51 +00002625 assert!(!rs_api.contains("impl Drop"));
2626 Ok(())
2627 }
2628
Devin Jeanpierre7e9a1de2021-12-03 08:04:22 +00002629 /// User-defined destructors *must* become Drop impls with ManuallyDrop
2630 /// fields
Devin Jeanpierre91de7012021-10-21 12:53:51 +00002631 #[test]
Devin Jeanpierre7e9a1de2021-12-03 08:04:22 +00002632 fn test_impl_drop_user_defined_destructor() -> Result<()> {
Googler7cced422021-12-06 11:58:39 +00002633 let ir = ir_from_cc(
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00002634 r#" struct NontrivialStruct { ~NontrivialStruct(); };
2635 struct UserDefinedDestructor {
Devin Jeanpierre91de7012021-10-21 12:53:51 +00002636 ~UserDefinedDestructor();
Devin Jeanpierre7e9a1de2021-12-03 08:04:22 +00002637 int x;
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00002638 NontrivialStruct nts;
Devin Jeanpierre91de7012021-10-21 12:53:51 +00002639 };"#,
2640 )?;
2641 let rs_api = generate_rs_api(&ir)?;
Lukasz Anforowicz6d553632022-01-06 21:36:14 +00002642 assert_rs_matches!(
2643 rs_api,
2644 quote! {
2645 impl Drop for UserDefinedDestructor {
2646 #[inline(always)]
2647 fn drop(&mut self) {
2648 unsafe { crate::detail::__rust_thunk___ZN21UserDefinedDestructorD1Ev(self) }
2649 }
2650 }
2651 }
2652 );
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00002653 assert_rs_matches!(rs_api, quote! {pub x: i32,});
2654 assert_rs_matches!(rs_api, quote! {pub nts: std::mem::ManuallyDrop<NontrivialStruct>,});
Devin Jeanpierre7e9a1de2021-12-03 08:04:22 +00002655 Ok(())
2656 }
2657
Lukasz Anforowicz6d553632022-01-06 21:36:14 +00002658 /// nontrivial types without user-defined destructors should invoke
2659 /// the C++ destructor to preserve the order of field destructions.
Devin Jeanpierre7e9a1de2021-12-03 08:04:22 +00002660 #[test]
2661 fn test_impl_drop_nontrivial_member_destructor() -> Result<()> {
2662 // TODO(jeanpierreda): This would be cleaner if the UserDefinedDestructor code were
2663 // omitted. For example, we simulate it so that UserDefinedDestructor
2664 // comes from another library.
Googler7cced422021-12-06 11:58:39 +00002665 let ir = ir_from_cc(
Devin Jeanpierre88343c72022-01-15 01:10:23 +00002666 r#"struct UserDefinedDestructor final {
Devin Jeanpierre7e9a1de2021-12-03 08:04:22 +00002667 ~UserDefinedDestructor();
2668 };
Devin Jeanpierre88343c72022-01-15 01:10:23 +00002669 struct TrivialStruct final { int i; };
2670 struct NontrivialMembers final {
Devin Jeanpierre7e9a1de2021-12-03 08:04:22 +00002671 UserDefinedDestructor udd;
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00002672 TrivialStruct ts;
Devin Jeanpierre7e9a1de2021-12-03 08:04:22 +00002673 int x;
2674 };"#,
2675 )?;
2676 let rs_api = generate_rs_api(&ir)?;
Lukasz Anforowicz6d553632022-01-06 21:36:14 +00002677 assert_rs_matches!(
2678 rs_api,
2679 quote! {
2680 impl Drop for NontrivialMembers {
2681 #[inline(always)]
2682 fn drop(&mut self) {
2683 unsafe { crate::detail::__rust_thunk___ZN17NontrivialMembersD1Ev(self) }
2684 }
2685 }
2686 }
2687 );
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00002688 assert_rs_matches!(rs_api, quote! {pub x: i32,});
2689 assert_rs_matches!(rs_api, quote! {pub ts: TrivialStruct,});
Lukasz Anforowicz6d553632022-01-06 21:36:14 +00002690 assert_rs_matches!(
2691 rs_api,
2692 quote! {pub udd: std::mem::ManuallyDrop<UserDefinedDestructor>,}
2693 );
Devin Jeanpierre7e9a1de2021-12-03 08:04:22 +00002694 Ok(())
2695 }
2696
2697 /// Trivial types (at least those that are mapped to Copy rust types) do not
2698 /// get a Drop impl.
2699 #[test]
2700 fn test_impl_drop_trivial() -> Result<()> {
Googler7cced422021-12-06 11:58:39 +00002701 let ir = ir_from_cc(
Devin Jeanpierre88343c72022-01-15 01:10:23 +00002702 r#"struct Trivial final {
Devin Jeanpierre7e9a1de2021-12-03 08:04:22 +00002703 ~Trivial() = default;
2704 int x;
2705 };"#,
2706 )?;
2707 let rs_api = generate_rs_api(&ir)?;
Marcel Hlopko89547752021-12-10 09:39:41 +00002708 assert_rs_not_matches!(rs_api, quote! {impl Drop});
2709 assert_rs_matches!(rs_api, quote! {pub x: i32});
Lukasz Anforowicz2f074162022-01-06 22:50:51 +00002710 let rs_api_impl = generate_rs_api_impl(&ir)?;
2711 // TODO(b/213326125): Avoid generating thunk impls that are never called.
2712 // (The test assertion below should be reversed once this bug is fixed.)
2713 assert_cc_matches!(rs_api_impl, quote! { std::destroy_at });
Devin Jeanpierre91de7012021-10-21 12:53:51 +00002714 Ok(())
2715 }
Devin Jeanpierre45cb1162021-10-27 10:54:28 +00002716
2717 #[test]
Lukasz Anforowicze643ec92021-12-22 15:45:15 +00002718 fn test_impl_default_explicitly_defaulted_constructor() -> Result<()> {
2719 let ir = ir_from_cc(
Lukasz Anforowicz95551272022-01-20 00:02:24 +00002720 r#"#pragma clang lifetime_elision
2721 struct DefaultedConstructor final {
Lukasz Anforowicze643ec92021-12-22 15:45:15 +00002722 DefaultedConstructor() = default;
2723 };"#,
2724 )?;
2725 let rs_api = generate_rs_api(&ir)?;
2726 assert_rs_matches!(
2727 rs_api,
2728 quote! {
2729 impl Default for DefaultedConstructor {
2730 #[inline(always)]
2731 fn default() -> Self {
Lukasz Anforowiczbedbdee2022-01-05 01:14:52 +00002732 let mut tmp = std::mem::MaybeUninit::<Self>::zeroed();
Lukasz Anforowicze643ec92021-12-22 15:45:15 +00002733 unsafe {
Lukasz Anforowicz40c2eb82022-01-11 18:22:31 +00002734 crate::detail::__rust_thunk___ZN20DefaultedConstructorC1Ev(&mut tmp);
Lukasz Anforowicze643ec92021-12-22 15:45:15 +00002735 tmp.assume_init()
2736 }
2737 }
2738 }
2739 }
2740 );
2741 let rs_api_impl = generate_rs_api_impl(&ir)?;
2742 assert_cc_matches!(
2743 rs_api_impl,
2744 quote! {
2745 extern "C" void __rust_thunk___ZN20DefaultedConstructorC1Ev(
Googler972d3582022-01-11 10:17:22 +00002746 class DefaultedConstructor* __this) {
Lukasz Anforowicz4457baf2021-12-23 17:24:04 +00002747 rs_api_impl_support::construct_at (__this) ;
Lukasz Anforowicze643ec92021-12-22 15:45:15 +00002748 }
2749 }
2750 );
2751 Ok(())
2752 }
2753
2754 #[test]
Lukasz Anforowicz326c4e42022-01-27 14:43:00 +00002755 fn test_impl_clone_that_propagates_lifetime() -> Result<()> {
2756 // This test covers the case where a single lifetime applies to 1)
2757 // the `__this` parameter and 2) other constructor parameters. For
2758 // example, maybe the newly constructed object needs to have the
2759 // same lifetime as the constructor's parameter. (This might require
2760 // annotating the whole C++ struct with a lifetime, so maybe the
2761 // example below is not fully realistic/accurate...).
2762 let mut ir = ir_from_cc(
2763 r#"#pragma clang lifetime_elision
2764 struct Foo final {
2765 [[clang::annotate("lifetimes = a: a")]]
2766 Foo(const int& i);
2767 };"#,
2768 )?;
2769 let ctor: &mut Func = ir
2770 .items_mut()
2771 .filter_map(|item| match item {
2772 Item::Func(func) => Some(func),
2773 _ => None,
2774 })
2775 .find(|f| {
2776 matches!(&f.name, UnqualifiedIdentifier::Constructor)
2777 && f.params.get(1).map(|p| p.identifier.identifier == "i").unwrap_or_default()
2778 })
2779 .unwrap();
2780 {
2781 // Double-check that the test scenario set up above uses the same lifetime
2782 // for both of the constructor's parameters: `__this` and `i`.
2783 assert_eq!(ctor.params.len(), 2);
2784 let this_lifetime: LifetimeId =
2785 *ctor.params[0].type_.rs_type.lifetime_args.first().unwrap();
2786 let i_lifetime: LifetimeId =
2787 *ctor.params[1].type_.rs_type.lifetime_args.first_mut().unwrap();
2788 assert_eq!(i_lifetime, this_lifetime);
2789 }
2790
2791 // Before cl/423346348 the generated Rust code would incorrectly look
2792 // like this (note the mismatched 'a and 'b lifetimes):
2793 // fn from<'b>(i: &'a i32) -> Self
2794 // After this CL, this scenario will result in an explicit error.
2795 let err = generate_rs_api(&ir).unwrap_err();
2796 let msg = format!("{}", err);
2797 assert!(
2798 msg.contains("The lifetime of `__this` is unexpectedly also used by another parameter")
2799 );
2800 Ok(())
2801 }
2802
2803 #[test]
Lukasz Anforowicz9bab8352021-12-22 17:35:31 +00002804 fn test_impl_default_non_trivial_struct() -> Result<()> {
2805 let ir = ir_from_cc(
Lukasz Anforowicz71716b72022-01-26 17:05:05 +00002806 r#"#pragma clang lifetime_elision
2807 struct NonTrivialStructWithConstructors final {
Lukasz Anforowicz9bab8352021-12-22 17:35:31 +00002808 NonTrivialStructWithConstructors();
2809 ~NonTrivialStructWithConstructors(); // Non-trivial
2810 };"#,
2811 )?;
2812 let rs_api = generate_rs_api(&ir)?;
2813 assert_rs_not_matches!(rs_api, quote! {impl Default});
2814 Ok(())
2815 }
2816
2817 #[test]
Lukasz Anforowicz71716b72022-01-26 17:05:05 +00002818 fn test_impl_from_for_explicit_conversion_constructor() -> Result<()> {
2819 let ir = ir_from_cc(
2820 r#"#pragma clang lifetime_elision
2821 struct SomeStruct final {
2822 explicit SomeStruct(int i);
2823 };"#,
2824 )?;
2825 let rs_api = generate_rs_api(&ir)?;
2826 // As discussed in b/214020567 for now we only generate `From::from` bindings
2827 // for *implicit* C++ conversion constructors.
2828 assert_rs_not_matches!(rs_api, quote! {impl From});
2829 Ok(())
2830 }
2831
2832 #[test]
2833 fn test_impl_from_for_implicit_conversion_constructor() -> Result<()> {
2834 let ir = ir_from_cc(
2835 r#"#pragma clang lifetime_elision
2836 struct SomeStruct final {
2837 SomeStruct(int i); // implicit - no `explicit` keyword
2838 };"#,
2839 )?;
2840 let rs_api = generate_rs_api(&ir)?;
2841 // As discussed in b/214020567 we generate `From::from` bindings for
2842 // *implicit* C++ conversion constructors.
2843 assert_rs_matches!(
2844 rs_api,
2845 quote! {
2846 impl From<i32> for SomeStruct {
2847 #[inline(always)]
2848 fn from(i: i32) -> Self {
2849 let mut tmp = std::mem::MaybeUninit::<Self>::zeroed();
2850 unsafe {
2851 crate::detail::__rust_thunk___ZN10SomeStructC1Ei(&mut tmp, i);
2852 tmp.assume_init()
2853 }
2854 }
2855 }
2856 }
2857 );
2858 Ok(())
2859 }
2860
2861 #[test]
Lukasz Anforowicz732ca642022-02-03 20:58:38 +00002862 fn test_impl_eq_for_member_function() -> Result<()> {
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +00002863 let ir = ir_from_cc(
2864 r#"#pragma clang lifetime_elision
2865 struct SomeStruct final {
2866 inline bool operator==(const SomeStruct& other) const {
2867 return i == other.i;
2868 }
2869 int i;
2870 };"#,
2871 )?;
2872 let rs_api = generate_rs_api(&ir)?;
2873 assert_rs_matches!(
2874 rs_api,
2875 quote! {
2876 impl PartialEq<SomeStruct> for SomeStruct {
2877 #[inline(always)]
2878 fn eq<'a, 'b>(&'a self, other: &'b SomeStruct) -> bool {
2879 unsafe { crate::detail::__rust_thunk___ZNK10SomeStructeqERKS_(self, other) }
2880 }
2881 }
2882 }
2883 );
2884 let rs_api_impl = generate_rs_api_impl(&ir)?;
2885 assert_cc_matches!(
2886 rs_api_impl,
2887 quote! {
2888 extern "C" bool __rust_thunk___ZNK10SomeStructeqERKS_(
2889 const class SomeStruct* __this, const class SomeStruct& other) {
2890 return __this->operator==(other);
2891 }
2892 }
2893 );
2894 Ok(())
2895 }
2896
2897 #[test]
Lukasz Anforowicz732ca642022-02-03 20:58:38 +00002898 fn test_impl_eq_for_free_function() -> Result<()> {
2899 let ir = ir_from_cc(
2900 r#"#pragma clang lifetime_elision
2901 struct SomeStruct final { int i; };
2902 bool operator==(const SomeStruct& lhs, const SomeStruct& rhs) {
2903 return lhs.i == rhs.i;
2904 }"#,
2905 )?;
2906 let rs_api = generate_rs_api(&ir)?;
2907 assert_rs_matches!(
2908 rs_api,
2909 quote! {
2910 impl PartialEq<SomeStruct> for SomeStruct {
2911 #[inline(always)]
2912 fn eq<'a, 'b>(&'a self, rhs: &'b SomeStruct) -> bool {
2913 unsafe { crate::detail::__rust_thunk___ZeqRK10SomeStructS1_(self, rhs) }
2914 }
2915 }
2916 }
2917 );
2918 Ok(())
2919 }
2920
2921 #[test]
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +00002922 fn test_impl_eq_non_const_member_function() -> Result<()> {
2923 let ir = ir_from_cc(
2924 r#"#pragma clang lifetime_elision
2925 struct SomeStruct final {
2926 bool operator==(const SomeStruct& other) /* no `const` here */;
2927 };"#,
2928 )?;
2929 let rs_api = generate_rs_api(&ir)?;
2930 assert_rs_not_matches!(rs_api, quote! {impl PartialEq});
2931 Ok(())
2932 }
2933
2934 #[test]
2935 fn test_impl_eq_rhs_by_value() -> Result<()> {
2936 let ir = ir_from_cc(
2937 r#"#pragma clang lifetime_elision
2938 struct SomeStruct final {
2939 bool operator==(SomeStruct other) const;
2940 };"#,
2941 )?;
2942 let rs_api = generate_rs_api(&ir)?;
2943 assert_rs_not_matches!(rs_api, quote! {impl PartialEq});
2944 Ok(())
2945 }
2946
2947 #[test]
Devin Jeanpierre45cb1162021-10-27 10:54:28 +00002948 fn test_thunk_ident_function() {
2949 let func = ir_func("foo");
Marcel Hlopkoeaae9b72022-01-21 15:54:11 +00002950 assert_eq!(thunk_ident(&func), make_rs_ident("__rust_thunk___Z3foov"));
Devin Jeanpierre45cb1162021-10-27 10:54:28 +00002951 }
2952
2953 #[test]
2954 fn test_thunk_ident_special_names() {
Marcel Hlopko4b13b962021-12-06 12:40:56 +00002955 let ir = ir_from_cc("struct Class {};").unwrap();
Devin Jeanpierre45cb1162021-10-27 10:54:28 +00002956
Googler45ad2752021-12-06 12:12:35 +00002957 let destructor =
2958 ir.functions().find(|f| f.name == UnqualifiedIdentifier::Destructor).unwrap();
Lukasz Anforowiczdd9ae0f2022-02-17 15:52:53 +00002959 assert_eq!(thunk_ident(destructor), make_rs_ident("__rust_thunk___ZN5ClassD1Ev"));
Devin Jeanpierre45cb1162021-10-27 10:54:28 +00002960
Lukasz Anforowicz49b5bbc2022-02-04 23:40:10 +00002961 let default_constructor = ir
2962 .functions()
2963 .find(|f| f.name == UnqualifiedIdentifier::Constructor && f.params.len() == 1)
2964 .unwrap();
Lukasz Anforowiczdd9ae0f2022-02-17 15:52:53 +00002965 assert_eq!(thunk_ident(default_constructor), make_rs_ident("__rust_thunk___ZN5ClassC1Ev"));
Devin Jeanpierre45cb1162021-10-27 10:54:28 +00002966 }
Googler7cced422021-12-06 11:58:39 +00002967
2968 #[test]
Marcel Hlopko89547752021-12-10 09:39:41 +00002969 fn test_elided_lifetimes() -> Result<()> {
Googler7cced422021-12-06 11:58:39 +00002970 let ir = ir_from_cc(
2971 r#"#pragma clang lifetime_elision
Devin Jeanpierre88343c72022-01-15 01:10:23 +00002972 struct S final {
Googler7cced422021-12-06 11:58:39 +00002973 int& f(int& i);
2974 };"#,
Marcel Hlopko89547752021-12-10 09:39:41 +00002975 )?;
2976 let rs_api = generate_rs_api(&ir)?;
2977 assert_rs_matches!(
2978 rs_api,
2979 quote! {
Lukasz Anforowicz231a3bb2022-01-12 14:05:59 +00002980 pub fn f<'a, 'b>(&'a mut self, i: &'b mut i32) -> &'a mut i32 { ... }
Marcel Hlopko89547752021-12-10 09:39:41 +00002981 }
Googler7cced422021-12-06 11:58:39 +00002982 );
Marcel Hlopko89547752021-12-10 09:39:41 +00002983 assert_rs_matches!(
2984 rs_api,
2985 quote! {
Googler6804a012022-01-05 07:04:36 +00002986 pub(crate) fn __rust_thunk___ZN1S1fERi<'a, 'b>(__this: &'a mut S, i: &'b mut i32)
2987 -> &'a mut i32;
Marcel Hlopko89547752021-12-10 09:39:41 +00002988 }
Googler7cced422021-12-06 11:58:39 +00002989 );
Marcel Hlopko89547752021-12-10 09:39:41 +00002990 Ok(())
Googler7cced422021-12-06 11:58:39 +00002991 }
Lukasz Anforowiczdaff0402021-12-23 00:37:50 +00002992
2993 #[test]
2994 fn test_format_generic_params() -> Result<()> {
2995 assert_rs_matches!(format_generic_params(std::iter::empty::<syn::Ident>()), quote! {});
2996
Marcel Hlopkoeaae9b72022-01-21 15:54:11 +00002997 let idents = ["T1", "T2"].iter().map(|s| make_rs_ident(s));
Lukasz Anforowiczdaff0402021-12-23 00:37:50 +00002998 assert_rs_matches!(format_generic_params(idents), quote! { < T1, T2 > });
2999
3000 let lifetimes = ["a", "b"]
3001 .iter()
3002 .map(|s| syn::Lifetime::new(&format!("'{}", s), proc_macro2::Span::call_site()));
3003 assert_rs_matches!(format_generic_params(lifetimes), quote! { < 'a, 'b > });
3004
3005 Ok(())
3006 }
Googlerd03d05b2022-01-07 10:10:57 +00003007
3008 #[test]
3009 fn test_overloaded_functions() -> Result<()> {
3010 // TODO(b/213280424): We don't support creating bindings for overloaded
3011 // functions yet, except in the case of overloaded constructors with a
3012 // single parameter.
3013 let ir = ir_from_cc(
Lukasz Anforowicz55673c92022-01-27 19:37:26 +00003014 r#" #pragma clang lifetime_elision
3015 void f();
Googlerd03d05b2022-01-07 10:10:57 +00003016 void f(int i);
Devin Jeanpierre88343c72022-01-15 01:10:23 +00003017 struct S1 final {
Googlerd03d05b2022-01-07 10:10:57 +00003018 void f();
3019 void f(int i);
3020 };
Devin Jeanpierre88343c72022-01-15 01:10:23 +00003021 struct S2 final {
Googlerd03d05b2022-01-07 10:10:57 +00003022 void f();
3023 };
Devin Jeanpierre88343c72022-01-15 01:10:23 +00003024 struct S3 final {
Googlerd03d05b2022-01-07 10:10:57 +00003025 S3(int i);
3026 S3(double d);
3027 };
3028 "#,
3029 )?;
3030 let rs_api = generate_rs_api(&ir)?;
3031 let rs_api_str = tokens_to_string(rs_api.clone())?;
3032
3033 // Cannot overload free functions.
3034 assert!(rs_api_str.contains("Error while generating bindings for item 'f'"));
3035 assert_rs_not_matches!(rs_api, quote! {pub fn f()});
3036 assert_rs_not_matches!(rs_api, quote! {pub fn f(i: i32)});
3037
3038 // Cannot overload member functions.
3039 assert!(rs_api_str.contains("Error while generating bindings for item 'S1::f'"));
3040 assert_rs_not_matches!(rs_api, quote! {pub fn f(... S1 ...)});
3041
3042 // But we can import member functions that have the same name as a free
3043 // function.
Lukasz Anforowicz55673c92022-01-27 19:37:26 +00003044 assert_rs_matches!(rs_api, quote! {pub fn f<'a>(&'a mut self)});
Googlerd03d05b2022-01-07 10:10:57 +00003045
3046 // We can also import overloaded single-parameter constructors.
3047 assert_rs_matches!(rs_api, quote! {impl From<i32> for S3});
3048 assert_rs_matches!(rs_api, quote! {impl From<f64> for S3});
3049 Ok(())
3050 }
Googlerdcca7f72022-01-10 12:30:43 +00003051
3052 #[test]
3053 fn test_type_alias() -> Result<()> {
3054 let ir = ir_from_cc(
3055 r#"
3056 typedef int MyTypedefDecl;
3057 using MyTypeAliasDecl = int;
Googler6a0a5252022-01-11 14:08:09 +00003058 using MyTypeAliasDecl_Alias = MyTypeAliasDecl;
3059
Devin Jeanpierre88343c72022-01-15 01:10:23 +00003060 struct S final {};
Googler6a0a5252022-01-11 14:08:09 +00003061 using S_Alias = S;
3062 using S_Alias_Alias = S_Alias;
3063
3064 inline void f(MyTypedefDecl t) {}
Googlerdcca7f72022-01-10 12:30:43 +00003065 "#,
3066 )?;
3067 let rs_api = generate_rs_api(&ir)?;
Googler6a0a5252022-01-11 14:08:09 +00003068 assert_rs_matches!(rs_api, quote! { pub type MyTypedefDecl = i32; });
3069 assert_rs_matches!(rs_api, quote! { pub type MyTypeAliasDecl = i32; });
3070 assert_rs_matches!(rs_api, quote! { pub type MyTypeAliasDecl_Alias = MyTypeAliasDecl; });
3071 assert_rs_matches!(rs_api, quote! { pub type S_Alias = S; });
3072 assert_rs_matches!(rs_api, quote! { pub type S_Alias_Alias = S_Alias; });
3073 assert_rs_matches!(rs_api, quote! { pub fn f(t: MyTypedefDecl) });
3074 assert_cc_matches!(
3075 generate_rs_api_impl(&ir)?,
3076 quote! {
3077 extern "C" void __rust_thunk___Z1fi(MyTypedefDecl t){ f (t) ; }
3078 }
3079 );
Googlerdcca7f72022-01-10 12:30:43 +00003080 Ok(())
3081 }
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00003082
3083 #[test]
3084 fn test_rs_type_kind_implements_copy() -> Result<()> {
Lukasz Anforowicz20651e32022-02-10 14:52:15 +00003085 let template = r#" LIFETIMES
Devin Jeanpierre88343c72022-01-15 01:10:23 +00003086 struct [[clang::trivial_abi]] TrivialStruct final { int i; };
3087 struct [[clang::trivial_abi]] UserDefinedCopyConstructor final {
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00003088 UserDefinedCopyConstructor(const UserDefinedCopyConstructor&);
3089 };
3090 using IntAlias = int;
3091 using TrivialAlias = TrivialStruct;
3092 using NonTrivialAlias = UserDefinedCopyConstructor;
3093 void func(PARAM_TYPE some_param);
3094 "#;
3095 assert_impl_all!(i32: Copy);
3096 assert_impl_all!(&i32: Copy);
3097 assert_not_impl_all!(&mut i32: Copy);
Lukasz Anforowicz20651e32022-02-10 14:52:15 +00003098 assert_impl_all!(Option<&i32>: Copy);
3099 assert_not_impl_all!(Option<&mut i32>: Copy);
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00003100 assert_impl_all!(*const i32: Copy);
3101 assert_impl_all!(*mut i32: Copy);
Lukasz Anforowicz20651e32022-02-10 14:52:15 +00003102 struct Test {
3103 // Test inputs:
3104 cc: &'static str,
3105 lifetimes: bool,
3106 // Expected test outputs:
3107 rs: &'static str,
3108 is_copy: bool,
3109 }
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00003110 let tests = vec![
3111 // Validity of the next few tests is verified via
3112 // `assert_[not_]impl_all!` static assertions above.
Lukasz Anforowicz20651e32022-02-10 14:52:15 +00003113 Test { cc: "int", lifetimes: true, rs: "i32", is_copy: true },
3114 Test { cc: "const int&", lifetimes: true, rs: "&'a i32", is_copy: true },
3115 Test { cc: "int&", lifetimes: true, rs: "&'a mut i32", is_copy: false },
3116 Test { cc: "const int*", lifetimes: true, rs: "Option<&'a i32>", is_copy: true },
3117 Test { cc: "int*", lifetimes: true, rs: "Option<&'a mut i32>", is_copy: false },
3118 Test { cc: "const int*", lifetimes: false, rs: "*const i32", is_copy: true },
3119 Test { cc: "int*", lifetimes: false, rs: "*mut i32", is_copy: true },
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00003120 // Tests below have been thought-through and verified "manually".
Lukasz Anforowicz20651e32022-02-10 14:52:15 +00003121 // TrivialStruct is expected to derive Copy.
3122 Test { cc: "TrivialStruct", lifetimes: true, rs: "TrivialStruct", is_copy: true },
3123 Test {
3124 cc: "UserDefinedCopyConstructor",
3125 lifetimes: true,
3126 rs: "UserDefinedCopyConstructor",
3127 is_copy: false,
3128 },
3129 Test { cc: "IntAlias", lifetimes: true, rs: "IntAlias", is_copy: true },
3130 Test { cc: "TrivialAlias", lifetimes: true, rs: "TrivialAlias", is_copy: true },
3131 Test { cc: "NonTrivialAlias", lifetimes: true, rs: "NonTrivialAlias", is_copy: false },
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00003132 ];
Lukasz Anforowicz20651e32022-02-10 14:52:15 +00003133 for test in tests.iter() {
3134 let test_name = format!("cc='{}', lifetimes={}", test.cc, test.lifetimes);
3135 let cc_input = template.replace("PARAM_TYPE", test.cc).replace(
3136 "LIFETIMES",
3137 if test.lifetimes { "#pragma clang lifetime_elision" } else { "" },
3138 );
3139 let ir = ir_from_cc(&cc_input)?;
Lukasz Anforowicz9c663ca2022-02-09 01:33:31 +00003140 let f = retrieve_func(&ir, "func");
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00003141 let t = RsTypeKind::new(&f.params[0].type_.rs_type, &ir)?;
Lukasz Anforowicz20651e32022-02-10 14:52:15 +00003142
Lukasz Anforowicz90bdb962022-02-14 21:07:45 +00003143 let lifetime_to_name: HashMap::<LifetimeId, String> = t.lifetimes().map(
3144 |lifetime_id| (lifetime_id, "a".to_string())).collect();
3145
Lukasz Anforowicz20651e32022-02-10 14:52:15 +00003146 let fmt = tokens_to_string(t.format(&ir, &lifetime_to_name)?)?;
3147 assert_eq!(test.rs, fmt, "Testing: {}", test_name);
3148
3149 assert_eq!(test.is_copy, t.implements_copy(), "Testing: {}", test_name);
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00003150 }
3151 Ok(())
3152 }
Lukasz Anforowicza94ab702022-01-14 22:40:25 +00003153
3154 #[test]
3155 fn test_rs_type_kind_is_shared_ref_to_with_lifetimes() -> Result<()> {
3156 let ir = ir_from_cc(
3157 "#pragma clang lifetime_elision
3158 struct SomeStruct {};
3159 void foo(const SomeStruct& foo_param);
3160 void bar(SomeStruct& bar_param);",
3161 )?;
3162 let record = ir.records().next().unwrap();
Lukasz Anforowicz9c663ca2022-02-09 01:33:31 +00003163 let foo_func = retrieve_func(&ir, "foo");
3164 let bar_func = retrieve_func(&ir, "bar");
Lukasz Anforowicza94ab702022-01-14 22:40:25 +00003165
3166 // const-ref + lifetimes in C++ ===> shared-ref in Rust
3167 assert_eq!(foo_func.params.len(), 1);
3168 let foo_param = &foo_func.params[0];
3169 assert_eq!(&foo_param.identifier.identifier, "foo_param");
3170 let foo_type = RsTypeKind::new(&foo_param.type_.rs_type, &ir)?;
3171 assert!(foo_type.is_shared_ref_to(record));
3172 assert!(matches!(foo_type, RsTypeKind::Reference { mutability: Mutability::Const, .. }));
3173
3174 // non-const-ref + lifetimes in C++ ===> mutable-ref in Rust
3175 assert_eq!(bar_func.params.len(), 1);
3176 let bar_param = &bar_func.params[0];
3177 assert_eq!(&bar_param.identifier.identifier, "bar_param");
3178 let bar_type = RsTypeKind::new(&bar_param.type_.rs_type, &ir)?;
3179 assert!(!bar_type.is_shared_ref_to(record));
3180 assert!(matches!(bar_type, RsTypeKind::Reference { mutability: Mutability::Mut, .. }));
3181
3182 Ok(())
3183 }
3184
3185 #[test]
3186 fn test_rs_type_kind_is_shared_ref_to_without_lifetimes() -> Result<()> {
3187 let ir = ir_from_cc(
3188 "struct SomeStruct {};
3189 void foo(const SomeStruct& foo_param);",
3190 )?;
3191 let record = ir.records().next().unwrap();
Lukasz Anforowicz9c663ca2022-02-09 01:33:31 +00003192 let foo_func = retrieve_func(&ir, "foo");
Lukasz Anforowicza94ab702022-01-14 22:40:25 +00003193
3194 // const-ref + *no* lifetimes in C++ ===> const-pointer in Rust
3195 assert_eq!(foo_func.params.len(), 1);
3196 let foo_param = &foo_func.params[0];
3197 assert_eq!(&foo_param.identifier.identifier, "foo_param");
3198 let foo_type = RsTypeKind::new(&foo_param.type_.rs_type, &ir)?;
3199 assert!(!foo_type.is_shared_ref_to(record));
3200 assert!(matches!(foo_type, RsTypeKind::Pointer { mutability: Mutability::Const, .. }));
3201
3202 Ok(())
3203 }
Marcel Hlopkoeaae9b72022-01-21 15:54:11 +00003204
3205 #[test]
Lukasz Anforowicz90bdb962022-02-14 21:07:45 +00003206 fn test_rs_type_kind_dfs_iter_ordering() {
3207 // Set up a test input representing: A<B<C>, D<E>>.
3208 let a = {
3209 let b = {
3210 let c = RsTypeKind::Other { name: "C", type_args: vec![] };
3211 RsTypeKind::Other { name: "B", type_args: vec![c] }
3212 };
3213 let d = {
3214 let e = RsTypeKind::Other { name: "E", type_args: vec![] };
3215 RsTypeKind::Other { name: "D", type_args: vec![e] }
3216 };
3217 RsTypeKind::Other { name: "A", type_args: vec![b, d] }
3218 };
3219 let dfs_names = a
3220 .dfs_iter()
3221 .map(|t| match t {
3222 RsTypeKind::Other { name, .. } => *name,
3223 _ => unreachable!("Only 'other' types are used in this test"),
3224 })
3225 .collect_vec();
3226 assert_eq!(vec!["A", "B", "C", "D", "E"], dfs_names);
3227 }
3228
3229 #[test]
Lukasz Anforowiczcf230fd2022-02-18 19:20:39 +00003230 fn test_rs_type_kind_dfs_iter_ordering_for_func_ptr() {
3231 // Set up a test input representing: fn(A, B) -> C
3232 let f = {
3233 let a = RsTypeKind::Other { name: "A", type_args: vec![] };
3234 let b = RsTypeKind::Other { name: "B", type_args: vec![] };
3235 let c = RsTypeKind::Other { name: "C", type_args: vec![] };
3236 RsTypeKind::FuncPtr { abi: "blah", param_types: vec![a, b], return_type: Box::new(c) }
3237 };
3238 let dfs_names = f
3239 .dfs_iter()
3240 .map(|t| match t {
3241 RsTypeKind::FuncPtr { .. } => "fn",
3242 RsTypeKind::Other { name, .. } => *name,
3243 _ => unreachable!("Only FuncPtr and Other kinds are used in this test"),
3244 })
3245 .collect_vec();
3246 assert_eq!(vec!["fn", "A", "B", "C"], dfs_names);
3247 }
3248
3249 #[test]
Lukasz Anforowicz90bdb962022-02-14 21:07:45 +00003250 fn test_rs_type_kind_lifetimes() -> Result<()> {
3251 let ir = ir_from_cc(
3252 r#"
3253 #pragma clang lifetime_elision
3254 using TypeAlias = int&;
3255 struct SomeStruct {};
3256 void foo(int a, int& b, int* c, int** d, TypeAlias e, SomeStruct f); "#,
3257 )?;
3258 let f = retrieve_func(&ir, "foo");
3259 let ret = RsTypeKind::new(&f.return_type.rs_type, &ir)?;
3260 let a = RsTypeKind::new(&f.params[0].type_.rs_type, &ir)?;
3261 let b = RsTypeKind::new(&f.params[1].type_.rs_type, &ir)?;
3262 let c = RsTypeKind::new(&f.params[2].type_.rs_type, &ir)?;
3263 let d = RsTypeKind::new(&f.params[3].type_.rs_type, &ir)?;
3264 let e = RsTypeKind::new(&f.params[4].type_.rs_type, &ir)?;
3265 let f = RsTypeKind::new(&f.params[5].type_.rs_type, &ir)?;
3266
3267 assert_eq!(0, ret.lifetimes().count()); // No lifetimes on `void`.
3268 assert_eq!(0, a.lifetimes().count()); // No lifetimes on `int`.
3269 assert_eq!(1, b.lifetimes().count()); // `&'a i32` has a single lifetime.
3270 assert_eq!(1, c.lifetimes().count()); // `Option<&'b i32>` has a single lifetime.
3271 assert_eq!(2, d.lifetimes().count()); // `&'c Option<&'d i32>` has two lifetimes.
3272 assert_eq!(1, e.lifetimes().count()); // Lifetime of underlying type should show through.
3273 assert_eq!(0, f.lifetimes().count()); // No lifetimes on structs (yet).
3274 Ok(())
3275 }
3276
3277 #[test]
3278 fn test_rs_type_kind_lifetimes_raw_ptr() -> Result<()> {
3279 let ir = ir_from_cc("void foo(int* a);")?;
3280 let f = retrieve_func(&ir, "foo");
3281 let a = RsTypeKind::new(&f.params[0].type_.rs_type, &ir)?;
3282 assert_eq!(0, a.lifetimes().count()); // No lifetimes on `int*`.
3283 Ok(())
3284 }
3285
3286 #[test]
Marcel Hlopkoeaae9b72022-01-21 15:54:11 +00003287 fn test_rust_keywords_are_escaped_in_rs_api_file() -> Result<()> {
3288 let ir = ir_from_cc("struct type { int dyn; };")?;
3289 let rs_api = generate_rs_api(&ir)?;
3290 assert_rs_matches!(rs_api, quote! { struct r#type { ... r#dyn: i32 ... } });
3291 Ok(())
3292 }
3293
3294 #[test]
3295 fn test_rust_keywords_are_not_escaped_in_rs_api_impl_file() -> Result<()> {
3296 let ir = ir_from_cc("struct type { int dyn; };")?;
3297 let rs_api_impl = generate_rs_api_impl(&ir)?;
3298 assert_cc_matches!(rs_api_impl, quote! { static_assert(offsetof(class type, dyn) ... ) });
3299 Ok(())
3300 }
Marcel Hlopko14ee3c82022-02-09 09:46:23 +00003301
3302 #[test]
3303 fn test_no_aligned_attr() {
3304 let ir = ir_from_cc("struct SomeStruct {};").unwrap();
3305 let rs_api = generate_rs_api(&ir).unwrap();
3306
3307 assert_rs_matches! {rs_api, quote! {
3308 #[repr(C)]
3309 pub struct SomeStruct { ... }
3310 }};
3311 }
3312
3313 #[test]
3314 fn test_aligned_attr() {
3315 let ir = ir_from_cc("struct SomeStruct {} __attribute__((aligned(64)));").unwrap();
3316 let rs_api = generate_rs_api(&ir).unwrap();
3317
3318 assert_rs_matches! {rs_api, quote! {
3319 #[repr(C, align(64))]
3320 pub struct SomeStruct { ... }
3321 }
3322 };
3323 }
Marcel Hlopko42abfc82021-08-09 07:03:17 +00003324}