blob: 06ed344c17130a54c86dada7cb760ba23490d8b7 [file] [log] [blame]
Marcel Hlopko42abfc82021-08-09 07:03:17 +00001// Part of the Crubit project, under the Apache License v2.0 with LLVM
2// Exceptions. See /LICENSE for license information.
3// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
4
Lukasz Anforowicz40c2eb82022-01-11 18:22:31 +00005use anyhow::{anyhow, bail, ensure, Context, Result};
Marcel Hlopko884ae7f2021-08-18 13:58:22 +00006use ffi_types::*;
Marcel Hlopko42abfc82021-08-09 07:03:17 +00007use ir::*;
8use itertools::Itertools;
Googler5ea88642021-09-29 08:05:59 +00009use proc_macro2::{Ident, Literal, TokenStream};
Devin Jeanpierre92ca2612022-04-06 11:35:13 -070010use quote::{format_ident, quote, ToTokens};
Devin Jeanpierre9886fb42022-04-01 04:31:20 -070011use std::collections::{BTreeSet, HashSet};
Marcel Hlopko42abfc82021-08-09 07:03:17 +000012use std::iter::Iterator;
13use std::panic::catch_unwind;
14use std::process;
Marcel Hlopko65d05f02021-12-09 12:29:24 +000015use token_stream_printer::{rs_tokens_to_formatted_string, tokens_to_string};
Marcel Hlopko42abfc82021-08-09 07:03:17 +000016
Marcel Hlopko45fba972021-08-23 19:52:20 +000017/// FFI equivalent of `Bindings`.
18#[repr(C)]
19pub struct FfiBindings {
20 rs_api: FfiU8SliceBox,
21 rs_api_impl: FfiU8SliceBox,
22}
23
24/// Deserializes IR from `json` and generates bindings source code.
Marcel Hlopko42abfc82021-08-09 07:03:17 +000025///
26/// This function panics on error.
27///
Lukasz Anforowiczdd9ae0f2022-02-17 15:52:53 +000028/// # Safety
29///
30/// Expectations:
31/// * function expects that param `json` is a FfiU8Slice for a valid array of
32/// bytes with the given size.
33/// * function expects that param `json` doesn't change during the call.
34///
Marcel Hlopko42abfc82021-08-09 07:03:17 +000035/// Ownership:
Michael Forsterbee84482021-10-13 08:35:38 +000036/// * function doesn't take ownership of (in other words it borrows) the
37/// param `json`
Marcel Hlopko42abfc82021-08-09 07:03:17 +000038/// * function passes ownership of the returned value to the caller
Marcel Hlopko42abfc82021-08-09 07:03:17 +000039#[no_mangle]
Marcel Hlopko45fba972021-08-23 19:52:20 +000040pub unsafe extern "C" fn GenerateBindingsImpl(json: FfiU8Slice) -> FfiBindings {
Marcel Hlopko42abfc82021-08-09 07:03:17 +000041 catch_unwind(|| {
Marcel Hlopko45fba972021-08-23 19:52:20 +000042 // It is ok to abort here.
43 let Bindings { rs_api, rs_api_impl } = generate_bindings(json.as_slice()).unwrap();
44
45 FfiBindings {
46 rs_api: FfiU8SliceBox::from_boxed_slice(rs_api.into_bytes().into_boxed_slice()),
47 rs_api_impl: FfiU8SliceBox::from_boxed_slice(
48 rs_api_impl.into_bytes().into_boxed_slice(),
49 ),
50 }
Marcel Hlopko42abfc82021-08-09 07:03:17 +000051 })
52 .unwrap_or_else(|_| process::abort())
53}
54
Marcel Hlopko45fba972021-08-23 19:52:20 +000055/// Source code for generated bindings.
56struct Bindings {
57 // Rust source code.
58 rs_api: String,
59 // C++ source code.
60 rs_api_impl: String,
Marcel Hlopko42abfc82021-08-09 07:03:17 +000061}
62
Marcel Hlopko45fba972021-08-23 19:52:20 +000063fn generate_bindings(json: &[u8]) -> Result<Bindings> {
64 let ir = deserialize_ir(json)?;
Marcel Hlopkoca84ff42021-12-09 14:15:14 +000065
66 // The code is formatted with a non-default rustfmt configuration. Prevent
Lukasz Anforowicz5b3f5302022-02-07 01:04:47 +000067 // downstream workflows from reformatting with a different configuration by
68 // marking the output with `@generated`. See also
69 // https://rust-lang.github.io/rustfmt/?version=v1.4.38&search=#format_generated_files
70 //
71 // TODO(lukasza): It would be nice to include "by $argv[0]"" in the
72 // @generated comment below. OTOH, `std::env::current_exe()` in our
73 // current build environment returns a guid-like path... :-/
Lukasz Anforowicz72c4d222022-02-18 19:07:28 +000074 //
75 // TODO(lukasza): Try to remove `#![rustfmt:skip]` - in theory it shouldn't
76 // be needed when `@generated` comment/keyword is present...
Lukasz Anforowicz5b3f5302022-02-07 01:04:47 +000077 let rs_api = format!(
Lukasz Anforowicz72c4d222022-02-18 19:07:28 +000078 "// Automatically @generated Rust bindings for C++ target\n\
79 // {target}\n\
80 #![rustfmt::skip]\n\
81 {code}",
Lukasz Anforowicz5b3f5302022-02-07 01:04:47 +000082 target = ir.current_target().0,
83 code = rs_tokens_to_formatted_string(generate_rs_api(&ir)?)?
84 );
Marcel Hlopko89547752021-12-10 09:39:41 +000085 let rs_api_impl = tokens_to_string(generate_rs_api_impl(&ir)?)?;
Marcel Hlopkoca84ff42021-12-09 14:15:14 +000086
Marcel Hlopko45fba972021-08-23 19:52:20 +000087 Ok(Bindings { rs_api, rs_api_impl })
88}
89
Devin Jeanpierre6d5e7cc2021-10-21 12:56:07 +000090/// Rust source code with attached information about how to modify the parent
91/// crate.
Devin Jeanpierre273eeae2021-10-06 13:29:35 +000092///
Michael Forsterbee84482021-10-13 08:35:38 +000093/// For example, the snippet `vec![].into_raw_parts()` is not valid unless the
94/// `vec_into_raw_parts` feature is enabled. So such a snippet should be
95/// represented as:
Devin Jeanpierre273eeae2021-10-06 13:29:35 +000096///
97/// ```
98/// RsSnippet {
Marcel Hlopkoeaae9b72022-01-21 15:54:11 +000099/// features: btree_set![make_rs_ident("vec_into_raw_parts")],
Devin Jeanpierre273eeae2021-10-06 13:29:35 +0000100/// tokens: quote!{vec![].into_raw_parts()},
101/// }
102/// ```
Lukasz Anforowiczeb19ac62022-02-05 00:10:16 +0000103#[derive(Clone, Debug)]
Devin Jeanpierre273eeae2021-10-06 13:29:35 +0000104struct RsSnippet {
105 /// Rust feature flags used by this snippet.
106 features: BTreeSet<Ident>,
107 /// The snippet itself, as a token stream.
108 tokens: TokenStream,
109}
110
111impl From<TokenStream> for RsSnippet {
112 fn from(tokens: TokenStream) -> Self {
113 RsSnippet { features: BTreeSet::new(), tokens }
114 }
115}
116
Michael Forsterbee84482021-10-13 08:35:38 +0000117/// If we know the original C++ function is codegenned and already compatible
118/// with `extern "C"` calling convention we skip creating/calling the C++ thunk
119/// since we can call the original C++ directly.
Marcel Hlopko3164eee2021-08-24 20:09:22 +0000120fn can_skip_cc_thunk(func: &Func) -> bool {
Devin Jeanpierre96839c12021-12-14 00:27:38 +0000121 // ## Inline functions
122 //
Michael Forsterbee84482021-10-13 08:35:38 +0000123 // Inline functions may not be codegenned in the C++ library since Clang doesn't
124 // know if Rust calls the function or not. Therefore in order to make inline
125 // functions callable from Rust we need to generate a C++ file that defines
126 // a thunk that delegates to the original inline function. When compiled,
127 // Clang will emit code for this thunk and Rust code will call the
Marcel Hlopko3164eee2021-08-24 20:09:22 +0000128 // thunk when the user wants to call the original inline function.
129 //
Michael Forsterbee84482021-10-13 08:35:38 +0000130 // This is not great runtime-performance-wise in regular builds (inline function
131 // will not be inlined, there will always be a function call), but it is
132 // correct. ThinLTO builds will be able to see through the thunk and inline
133 // code across the language boundary. For non-ThinLTO builds we plan to
134 // implement <internal link> which removes the runtime performance overhead.
Devin Jeanpierre96839c12021-12-14 00:27:38 +0000135 if func.is_inline {
136 return false;
137 }
138 // ## Virtual functions
139 //
140 // When calling virtual `A::Method()`, it's not necessarily the case that we'll
141 // specifically call the concrete `A::Method` impl. For example, if this is
142 // called on something whose dynamic type is some subclass `B` with an
143 // overridden `B::Method`, then we'll call that.
144 //
145 // We must reuse the C++ dynamic dispatching system. In this case, the easiest
146 // way to do it is by resorting to a C++ thunk, whose implementation will do
147 // the lookup.
148 //
149 // In terms of runtime performance, since this only occurs for virtual function
150 // calls, which are already slow, it may not be such a big deal. We can
151 // benchmark it later. :)
152 if let Some(meta) = &func.member_func_metadata {
153 if let Some(inst_meta) = &meta.instance_method_metadata {
154 if inst_meta.is_virtual {
155 return false;
156 }
157 }
158 }
Lukasz Anforowicz0b6a6ac2022-03-22 22:32:23 +0000159 // ## Custom calling convention requires a thunk.
160 //
161 // The thunk has the "C" calling convention, and internally can call the
162 // C++ function using any of the calling conventions supported by the C++
163 // compiler (which might not always match the set supported by Rust - e.g.,
164 // abi.rs doesn't contain "swiftcall" from
165 // clang::FunctionType::getNameForCallConv)
166 if !func.has_c_calling_convention {
167 return false;
168 }
Devin Jeanpierre96839c12021-12-14 00:27:38 +0000169
170 true
Marcel Hlopko3164eee2021-08-24 20:09:22 +0000171}
172
Googlerd03d05b2022-01-07 10:10:57 +0000173/// Uniquely identifies a generated Rust function.
Lukasz Anforowiczeb19ac62022-02-05 00:10:16 +0000174#[derive(Clone, Debug, PartialEq, Eq, Hash)]
Googlerd03d05b2022-01-07 10:10:57 +0000175struct FunctionId {
176 // If the function is on a trait impl, contains the name of the Self type for
177 // which the trait is being implemented.
178 self_type: Option<syn::Path>,
179 // Fully qualified path of the function. For functions in impl blocks, this
180 // includes the name of the type or trait on which the function is being
181 // implemented, e.g. `Default::default`.
182 function_path: syn::Path,
183}
184
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +0000185/// Returns the name of `func` in C++ syntax.
Googlerd03d05b2022-01-07 10:10:57 +0000186fn cxx_function_name(func: &Func, ir: &IR) -> Result<String> {
187 let record: Option<&str> = func
188 .member_func_metadata
189 .as_ref()
190 .map(|meta| meta.find_record(ir))
191 .transpose()?
Lukasz Anforowicz4c3a2cc2022-03-11 00:24:49 +0000192 .map(|r| &*r.cc_name);
Googlerd03d05b2022-01-07 10:10:57 +0000193
194 let func_name = match &func.name {
195 UnqualifiedIdentifier::Identifier(id) => id.identifier.clone(),
Lukasz Anforowicz9c663ca2022-02-09 01:33:31 +0000196 UnqualifiedIdentifier::Operator(op) => op.cc_name(),
Googlerd03d05b2022-01-07 10:10:57 +0000197 UnqualifiedIdentifier::Destructor => {
198 format!("~{}", record.expect("destructor must be associated with a record"))
199 }
200 UnqualifiedIdentifier::Constructor => {
Lukasz Anforowiczeb19ac62022-02-05 00:10:16 +0000201 record.expect("constructor must be associated with a record").to_string()
Googlerd03d05b2022-01-07 10:10:57 +0000202 }
203 };
204
205 if let Some(record_name) = record {
206 Ok(format!("{}::{}", record_name, func_name))
207 } else {
208 Ok(func_name)
209 }
210}
211
Lukasz Anforowiczeb19ac62022-02-05 00:10:16 +0000212fn make_unsupported_fn(func: &Func, ir: &IR, message: impl ToString) -> Result<UnsupportedItem> {
213 Ok(UnsupportedItem {
214 name: cxx_function_name(func, ir)?,
215 message: message.to_string(),
216 source_loc: func.source_loc.clone(),
Rosica Dejanovskad638cf52022-03-23 15:45:01 +0000217 id: func.id,
Lukasz Anforowiczeb19ac62022-02-05 00:10:16 +0000218 })
219}
220
Devin Jeanpierre9c34da62022-03-31 04:57:16 -0700221/// The name of a trait, with extra entries for specially-understood traits and
222/// families of traits.
Devin Jeanpierre103cbb52022-04-06 12:55:16 -0700223enum TraitName<'ir> {
Devin Jeanpierread125742022-04-11 13:50:28 -0700224 /// The constructor trait for !Unpin types, with a list of parameter types.
225 /// For example, `CtorNew(vec![])` is the default constructor.
226 CtorNew(Vec<RsTypeKind<'ir>>),
Devin Jeanpierre9c34da62022-03-31 04:57:16 -0700227 /// An Unpin constructor trait, e.g. From or Clone.
228 UnpinConstructor(TokenStream),
229 /// Any other trait, e.g. Eq.
230 Other(TokenStream),
231}
Devin Jeanpierre103cbb52022-04-06 12:55:16 -0700232impl<'ir> ToTokens for TraitName<'ir> {
Devin Jeanpierre9c34da62022-03-31 04:57:16 -0700233 fn to_tokens(&self, tokens: &mut TokenStream) {
234 match self {
235 Self::UnpinConstructor(t) | Self::Other(t) => t.to_tokens(tokens),
Devin Jeanpierread125742022-04-11 13:50:28 -0700236 Self::CtorNew(arg_types) => {
237 let arg_types = format_tuple_except_singleton(arg_types);
238 quote! { ctor::CtorNew < #arg_types > }.to_tokens(tokens)
239 }
Devin Jeanpierre9c34da62022-03-31 04:57:16 -0700240 }
241 }
242}
243
244/// The kind of the `impl` block the function needs to be generated in.
Devin Jeanpierre103cbb52022-04-06 12:55:16 -0700245enum ImplKind<'ir> {
Devin Jeanpierre9c34da62022-03-31 04:57:16 -0700246 /// Used for free functions for which we don't want the `impl` block.
247 None { is_unsafe: bool },
248 /// Used for inherent methods for which we need an `impl SomeStruct { ... }`
249 /// block.
250 Struct {
251 /// For example, `SomeStruct`. Retrieved from
252 /// `func.member_func_metadata`.
253 record_name: Ident,
254 is_unsafe: bool,
255 /// Whether to format the first parameter as "self" (e.g. `__this:
256 /// &mut T` -> `&mut self`)
257 format_first_param_as_self: bool,
258 },
259 /// Used for trait methods for which we need an `impl TraitName for
260 /// SomeStruct { ... }` block.
261 Trait {
262 /// For example, `SomeStruct`.
263 /// Note that `record_name` might *not* be from
264 /// `func.member_func_metadata`.
265 record_name: Ident,
266 /// For example, `quote!{ From<i32> }`.
Devin Jeanpierre103cbb52022-04-06 12:55:16 -0700267 trait_name: TraitName<'ir>,
Devin Jeanpierre9c34da62022-03-31 04:57:16 -0700268
269 /// Where to declare lifetimes: `impl<'b>` VS `fn foo<'b>`.
270 declare_lifetimes: bool,
271 /// The generic params of trait `impl` (e.g. `<'b>`). These start
272 /// empty and only later are mutated into the correct value.
273 trait_generic_params: TokenStream,
274 /// Whether to format the first parameter as "self" (e.g. `__this:
275 /// &mut T` -> `&mut self`)
276 format_first_param_as_self: bool,
277 },
278}
Devin Jeanpierre103cbb52022-04-06 12:55:16 -0700279impl<'ir> ImplKind<'ir> {
Devin Jeanpierre9c34da62022-03-31 04:57:16 -0700280 fn new_trait(
Devin Jeanpierre103cbb52022-04-06 12:55:16 -0700281 trait_name: TraitName<'ir>,
Devin Jeanpierre9c34da62022-03-31 04:57:16 -0700282 record_name: Ident,
283 format_first_param_as_self: bool,
284 ) -> Self {
285 ImplKind::Trait {
286 trait_name,
287 record_name,
288 declare_lifetimes: false,
289 trait_generic_params: quote! {},
290 format_first_param_as_self,
291 }
292 }
293 fn new_generic_trait(
Devin Jeanpierre103cbb52022-04-06 12:55:16 -0700294 trait_name: TraitName<'ir>,
Devin Jeanpierre9c34da62022-03-31 04:57:16 -0700295 record_name: Ident,
296 format_first_param_as_self: bool,
297 ) -> Self {
298 ImplKind::Trait {
299 trait_name,
300 record_name,
301 declare_lifetimes: true,
302 trait_generic_params: quote! {},
303 format_first_param_as_self,
304 }
305 }
306 fn format_first_param_as_self(&self) -> bool {
307 matches!(
308 self,
309 Self::Trait { format_first_param_as_self: true, .. }
310 | Self::Struct { format_first_param_as_self: true, .. }
311 )
312 }
313 /// Returns whether the function is defined as `unsafe fn ...`.
314 fn is_unsafe(&self) -> bool {
315 matches!(self, Self::None { is_unsafe: true, .. } | Self::Struct { is_unsafe: true, .. })
316 }
317}
318
319/// Returns the shape of the generated Rust API for a given function definition.
Devin Jeanpierred7b48102022-03-31 04:15:03 -0700320///
Devin Jeanpierre9c34da62022-03-31 04:57:16 -0700321/// Returns:
Devin Jeanpierred7b48102022-03-31 04:15:03 -0700322///
323/// * `Err(_)`: something went wrong importing this function.
324/// * `Ok(None)`: the function imported as "nothing". (For example, a defaulted
325/// destructor might be mapped to no `Drop` impl at all.)
Devin Jeanpierre9c34da62022-03-31 04:57:16 -0700326/// * `Ok((func_name, impl_kind))`: The function name and ImplKind.
Devin Jeanpierre103cbb52022-04-06 12:55:16 -0700327fn api_func_shape<'ir>(
Devin Jeanpierre9c34da62022-03-31 04:57:16 -0700328 func: &Func,
329 ir: &IR,
Devin Jeanpierre22f91492022-04-06 13:01:23 -0700330 param_types: &[RsTypeKind<'ir>],
Devin Jeanpierre103cbb52022-04-06 12:55:16 -0700331) -> Result<Option<(Ident, ImplKind<'ir>)>> {
Lukasz Anforowiczab65e292022-01-14 23:04:21 +0000332 let maybe_record: Option<&Record> =
Lukasz Anforowicz13cf7492021-12-22 15:29:52 +0000333 func.member_func_metadata.as_ref().map(|meta| meta.find_record(ir)).transpose()?;
Devin Jeanpierre22f91492022-04-06 13:01:23 -0700334 let has_pointer_params = param_types.iter().any(|p| matches!(p, RsTypeKind::Pointer { .. }));
Devin Jeanpierre9c34da62022-03-31 04:57:16 -0700335 let impl_kind: ImplKind;
Lukasz Anforowiczab65e292022-01-14 23:04:21 +0000336 let func_name: syn::Ident;
Googlerd03d05b2022-01-07 10:10:57 +0000337 match &func.name {
Lukasz Anforowicz9c663ca2022-02-09 01:33:31 +0000338 UnqualifiedIdentifier::Operator(op) if op.name == "==" => {
Devin Jeanpierre22f91492022-04-06 13:01:23 -0700339 if param_types.len() != 2 {
Lukasz Anforowiczeb19ac62022-02-05 00:10:16 +0000340 bail!("Unexpected number of parameters in operator==: {:?}", func);
341 }
Devin Jeanpierre22f91492022-04-06 13:01:23 -0700342 match (&param_types[0], &param_types[1]) {
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +0000343 (
Lukasz Anforowiczeb19ac62022-02-05 00:10:16 +0000344 RsTypeKind::Reference { referent: lhs, mutability: Mutability::Const, .. },
345 RsTypeKind::Reference { referent: rhs, mutability: Mutability::Const, .. },
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +0000346 ) => match **lhs {
Devin Jeanpierrefec5bfe2022-04-05 10:59:10 -0700347 RsTypeKind::Record { record: lhs_record, .. } => {
Lukasz Anforowicz4c3a2cc2022-03-11 00:24:49 +0000348 let lhs: Ident = make_rs_ident(&lhs_record.rs_name);
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +0000349 func_name = make_rs_ident("eq");
Lukasz Anforowicz5e623782022-03-14 16:52:23 +0000350 // Not using `ImplKind::new_generic_trait`, because #rhs
351 // should be stripped of references + because `&'a self`
352 // needs to have its lifetime declared next to `fn`, not
353 // next to `impl`.
Devin Jeanpierred9a6e6c2022-03-29 02:55:41 -0700354 impl_kind = ImplKind::new_trait(
355 TraitName::Other(quote! {PartialEq<#rhs>}),
356 lhs,
357 /* format_first_param_as_self= */ true,
358 );
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +0000359 }
Marcel Hlopko14ee3c82022-02-09 09:46:23 +0000360 _ => {
Devin Jeanpierred7b48102022-03-31 04:15:03 -0700361 bail!("operator== where lhs doesn't refer to a record",);
Marcel Hlopko14ee3c82022-02-09 09:46:23 +0000362 }
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +0000363 },
Marcel Hlopko14ee3c82022-02-09 09:46:23 +0000364 _ => {
Devin Jeanpierred7b48102022-03-31 04:15:03 -0700365 bail!("operator== where operands are not const references",);
Marcel Hlopko14ee3c82022-02-09 09:46:23 +0000366 }
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +0000367 };
368 }
Lukasz Anforowicz9c663ca2022-02-09 01:33:31 +0000369 UnqualifiedIdentifier::Operator(_) => {
Devin Jeanpierred7b48102022-03-31 04:15:03 -0700370 bail!("Bindings for this kind of operator are not supported");
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +0000371 }
Devin Jeanpierref2ec8712021-10-13 20:47:16 +0000372 UnqualifiedIdentifier::Identifier(id) => {
Marcel Hlopkoeaae9b72022-01-21 15:54:11 +0000373 func_name = make_rs_ident(&id.identifier);
Lukasz Anforowiczf9564622022-01-28 14:31:04 +0000374 match maybe_record {
375 None => {
Devin Jeanpierre7c3d8ed2022-03-29 03:02:04 -0700376 impl_kind = ImplKind::None { is_unsafe: has_pointer_params };
Lukasz Anforowiczf9564622022-01-28 14:31:04 +0000377 }
378 Some(record) => {
Devin Jeanpierred9a6e6c2022-03-29 02:55:41 -0700379 let format_first_param_as_self = if func.is_instance_method() {
Devin Jeanpierre22f91492022-04-06 13:01:23 -0700380 let first_param = param_types.first().ok_or_else(|| {
Lukasz Anforowiczf9564622022-01-28 14:31:04 +0000381 anyhow!("Missing `__this` parameter in an instance method: {:?}", func)
382 })?;
Devin Jeanpierred9a6e6c2022-03-29 02:55:41 -0700383 first_param.is_ref_to(record)
Lukasz Anforowiczf9564622022-01-28 14:31:04 +0000384 } else {
Devin Jeanpierred9a6e6c2022-03-29 02:55:41 -0700385 false
386 };
Devin Jeanpierre6784e5e2022-03-29 02:59:01 -0700387 impl_kind = ImplKind::Struct {
388 record_name: make_rs_ident(&record.rs_name),
389 format_first_param_as_self,
Devin Jeanpierre7c3d8ed2022-03-29 03:02:04 -0700390 is_unsafe: has_pointer_params,
Devin Jeanpierre6784e5e2022-03-29 02:59:01 -0700391 };
Lukasz Anforowiczf9564622022-01-28 14:31:04 +0000392 }
393 };
Michael Forstered642022021-10-04 09:48:25 +0000394 }
Devin Jeanpierre91de7012021-10-21 12:53:51 +0000395 UnqualifiedIdentifier::Destructor => {
Lukasz Anforowiczab65e292022-01-14 23:04:21 +0000396 // Note: to avoid double-destruction of the fields, they are all wrapped in
397 // ManuallyDrop in this case. See `generate_record`.
398 let record =
399 maybe_record.ok_or_else(|| anyhow!("Destructors must be member functions."))?;
Lukasz Anforowicze57215c2022-01-12 14:54:16 +0000400 if !should_implement_drop(record) {
Devin Jeanpierred7b48102022-03-31 04:15:03 -0700401 return Ok(None);
Devin Jeanpierre91de7012021-10-21 12:53:51 +0000402 }
Devin Jeanpierred9a6e6c2022-03-29 02:55:41 -0700403 impl_kind = ImplKind::new_trait(
404 TraitName::Other(quote! {Drop}),
Devin Jeanpierre6784e5e2022-03-29 02:59:01 -0700405 make_rs_ident(&record.rs_name),
Devin Jeanpierred9a6e6c2022-03-29 02:55:41 -0700406 /* format_first_param_as_self= */ true,
407 );
Marcel Hlopkoeaae9b72022-01-21 15:54:11 +0000408 func_name = make_rs_ident("drop");
Devin Jeanpierre91de7012021-10-21 12:53:51 +0000409 }
Lukasz Anforowicz13cf7492021-12-22 15:29:52 +0000410 UnqualifiedIdentifier::Constructor => {
Lukasz Anforowicz71716b72022-01-26 17:05:05 +0000411 let member_func_metadata = func
412 .member_func_metadata
413 .as_ref()
414 .ok_or_else(|| anyhow!("Constructors must be member functions."))?;
415 let record = maybe_record
416 .ok_or_else(|| anyhow!("Constructors must be associated with a record."))?;
417 let instance_method_metadata =
418 member_func_metadata
419 .instance_method_metadata
420 .as_ref()
421 .ok_or_else(|| anyhow!("Constructors must be instance methods."))?;
Devin Jeanpierre7c3d8ed2022-03-29 03:02:04 -0700422 if has_pointer_params {
Lukasz Anforowicz55673c92022-01-27 19:37:26 +0000423 // TODO(b/216648347): Allow this outside of traits (e.g. after supporting
424 // translating C++ constructors into static methods in Rust).
Devin Jeanpierred7b48102022-03-31 04:15:03 -0700425 bail!(
Lukasz Anforowiczeb19ac62022-02-05 00:10:16 +0000426 "Unsafe constructors (e.g. with no elided or explicit lifetimes) \
427 are intentionally not supported",
428 );
Lukasz Anforowicz55673c92022-01-27 19:37:26 +0000429 }
430
Devin Jeanpierre6784e5e2022-03-29 02:59:01 -0700431 let record_name = make_rs_ident(&record.rs_name);
Devin Jeanpierre6f607372022-03-22 21:34:38 +0000432 if !record.is_unpin() {
433 func_name = make_rs_ident("ctor_new");
Devin Jeanpierre6f607372022-03-22 21:34:38 +0000434
Devin Jeanpierread125742022-04-11 13:50:28 -0700435 match param_types {
436 [] => bail!("Missing `__this` parameter in a constructor: {:?}", func),
437 [_this, params @ ..] => {
Lukasz Anforowicz5e623782022-03-14 16:52:23 +0000438 impl_kind = ImplKind::new_generic_trait(
Devin Jeanpierread125742022-04-11 13:50:28 -0700439 TraitName::CtorNew(params.iter().cloned().collect()),
Devin Jeanpierre1b8f4a12022-03-22 21:29:42 +0000440 record_name,
Devin Jeanpierred9a6e6c2022-03-29 02:55:41 -0700441 /* format_first_param_as_self= */ false,
Devin Jeanpierre1b8f4a12022-03-22 21:29:42 +0000442 );
Devin Jeanpierre6f607372022-03-22 21:34:38 +0000443 }
Lukasz Anforowicz73326af2022-01-05 01:13:10 +0000444 }
Devin Jeanpierre6f607372022-03-22 21:34:38 +0000445 } else {
446 match func.params.len() {
447 0 => bail!("Missing `__this` parameter in a constructor: {:?}", func),
448 1 => {
449 impl_kind = ImplKind::new_trait(
450 TraitName::UnpinConstructor(quote! {Default}),
451 record_name,
Devin Jeanpierred9a6e6c2022-03-29 02:55:41 -0700452 /* format_first_param_as_self= */ false,
Devin Jeanpierre6f607372022-03-22 21:34:38 +0000453 );
454 func_name = make_rs_ident("default");
Devin Jeanpierre6f607372022-03-22 21:34:38 +0000455 }
456 2 => {
Devin Jeanpierre22f91492022-04-06 13:01:23 -0700457 if param_types[1].is_shared_ref_to(record) {
Devin Jeanpierre6f607372022-03-22 21:34:38 +0000458 // Copy constructor
459 if should_derive_clone(record) {
Devin Jeanpierred7b48102022-03-31 04:15:03 -0700460 return Ok(None);
Devin Jeanpierre6f607372022-03-22 21:34:38 +0000461 } else {
462 impl_kind = ImplKind::new_trait(
463 TraitName::UnpinConstructor(quote! {Clone}),
464 record_name,
Devin Jeanpierred9a6e6c2022-03-29 02:55:41 -0700465 /* format_first_param_as_self= */ true,
Devin Jeanpierre6f607372022-03-22 21:34:38 +0000466 );
467 func_name = make_rs_ident("clone");
Devin Jeanpierre6f607372022-03-22 21:34:38 +0000468 }
469 } else if !instance_method_metadata.is_explicit_ctor {
Devin Jeanpierre22f91492022-04-06 13:01:23 -0700470 let param_type = &param_types[1];
Devin Jeanpierre6f607372022-03-22 21:34:38 +0000471 impl_kind = ImplKind::new_generic_trait(
472 TraitName::UnpinConstructor(quote! {From< #param_type >}),
473 record_name,
Devin Jeanpierred9a6e6c2022-03-29 02:55:41 -0700474 /* format_first_param_as_self= */ false,
Devin Jeanpierre6f607372022-03-22 21:34:38 +0000475 );
476 func_name = make_rs_ident("from");
Devin Jeanpierre6f607372022-03-22 21:34:38 +0000477 } else {
Devin Jeanpierred7b48102022-03-31 04:15:03 -0700478 bail!("Not yet supported type of constructor parameter",);
Devin Jeanpierre6f607372022-03-22 21:34:38 +0000479 }
480 }
481 _ => {
482 // TODO(b/216648347): Support bindings for other constructors.
Devin Jeanpierred7b48102022-03-31 04:15:03 -0700483 bail!("More than 1 constructor parameter is not supported yet",);
Devin Jeanpierre6f607372022-03-22 21:34:38 +0000484 }
Lukasz Anforowicz73326af2022-01-05 01:13:10 +0000485 }
Lukasz Anforowiczab65e292022-01-14 23:04:21 +0000486 }
487 }
488 }
Devin Jeanpierre9c34da62022-03-31 04:57:16 -0700489 Ok(Some((func_name, impl_kind)))
490}
491
492/// Generates Rust source code for a given `Func`.
493///
494/// Returns:
495///
496/// * `Err(_)`: couldn't import the function, emit an `UnsupportedItem`.
497/// * `Ok(None)`: the function imported as "nothing". (For example, a defaulted
498/// destructor might be mapped to no `Drop` impl at all.)
499/// * `Ok((rs_api, rs_thunk, function_id))`: The Rust function definition,
500/// thunk FFI definition, and function ID.
501fn generate_func(func: &Func, ir: &IR) -> Result<Option<(RsSnippet, RsSnippet, FunctionId)>> {
Devin Jeanpierre22f91492022-04-06 13:01:23 -0700502 let param_types = func
Devin Jeanpierre9c34da62022-03-31 04:57:16 -0700503 .params
504 .iter()
505 .map(|p| {
506 RsTypeKind::new(&p.type_.rs_type, ir).with_context(|| {
507 format!("Failed to process type of parameter {:?} on {:?}", p, func)
508 })
509 })
510 .collect::<Result<Vec<_>>>()?;
511
Devin Jeanpierread125742022-04-11 13:50:28 -0700512 let (func_name, mut impl_kind) = if let Some(values) = api_func_shape(func, ir, &param_types)? {
513 values
514 } else {
515 return Ok(None);
516 };
Lukasz Anforowiczab65e292022-01-14 23:04:21 +0000517
Devin Jeanpierred9cecff2022-03-29 02:53:58 -0700518 let return_type_fragment = RsTypeKind::new(&func.return_type.rs_type, ir)
Devin Jeanpierre8c6ff6d2022-04-06 12:57:14 -0700519 .with_context(|| format!("Failed to format return type for {:?}", func))?
520 .format_as_return_type_fragment();
Devin Jeanpierred9cecff2022-03-29 02:53:58 -0700521 let param_idents =
522 func.params.iter().map(|p| make_rs_ident(&p.identifier.identifier)).collect_vec();
Devin Jeanpierre3eb5bbd2022-04-06 12:56:19 -0700523
Devin Jeanpierre22f91492022-04-06 13:01:23 -0700524 let thunk = generate_func_thunk(func, &param_idents, &param_types, &return_type_fragment)?;
Devin Jeanpierred9cecff2022-03-29 02:53:58 -0700525
Lukasz Anforowiczab65e292022-01-14 23:04:21 +0000526 let api_func_def = {
Devin Jeanpierre3eb5bbd2022-04-06 12:56:19 -0700527 let mut return_type_fragment = return_type_fragment;
Lukasz Anforowiczab65e292022-01-14 23:04:21 +0000528 let mut thunk_args = param_idents.iter().map(|id| quote! { #id}).collect_vec();
529 let mut api_params = param_idents
530 .iter()
531 .zip(param_types.iter())
532 .map(|(ident, type_)| quote! { #ident : #type_ })
533 .collect_vec();
Lukasz Anforowicz95551272022-01-20 00:02:24 +0000534 let mut lifetimes = func.lifetime_params.iter().collect_vec();
Devin Jeanpierre22f91492022-04-06 13:01:23 -0700535 let mut maybe_first_api_param = param_types.get(0);
Lukasz Anforowiczab65e292022-01-14 23:04:21 +0000536
Devin Jeanpierre6f607372022-03-22 21:34:38 +0000537 if let ImplKind::Trait {
Devin Jeanpierread125742022-04-11 13:50:28 -0700538 trait_name: trait_name @ (TraitName::UnpinConstructor(..) | TraitName::CtorNew(..)),
Devin Jeanpierre6f607372022-03-22 21:34:38 +0000539 ..
Devin Jeanpierread125742022-04-11 13:50:28 -0700540 } = &impl_kind
Devin Jeanpierre6f607372022-03-22 21:34:38 +0000541 {
Lukasz Anforowiczab65e292022-01-14 23:04:21 +0000542 return_type_fragment = quote! { -> Self };
543
Lukasz Anforowiczf9564622022-01-28 14:31:04 +0000544 // Drop `__this` parameter from the public Rust API. Presence of
545 // element #0 is indirectly verified by a `Constructor`-related
546 // `match` branch a little bit above.
547 api_params.remove(0);
548 thunk_args.remove(0);
Lukasz Anforowicz95551272022-01-20 00:02:24 +0000549
Lukasz Anforowicz326c4e42022-01-27 14:43:00 +0000550 // Remove the lifetime associated with `__this`.
Devin Jeanpierre1b8f4a12022-03-22 21:29:42 +0000551 ensure!(
552 func.return_type.rs_type.is_unit_type(),
553 "Unexpectedly non-void return type of a constructor: {:?}",
554 func
555 );
Lukasz Anforowiczf7bdd392022-01-21 00:33:39 +0000556 let maybe_first_lifetime = func.params[0].type_.rs_type.lifetime_args.first();
Lukasz Anforowicz55673c92022-01-27 19:37:26 +0000557 let no_longer_needed_lifetime_id = maybe_first_lifetime
558 .ok_or_else(|| anyhow!("Missing lifetime on `__this` parameter: {:?}", func))?;
559 lifetimes.retain(|l| l.id != *no_longer_needed_lifetime_id);
Devin Jeanpierre22f91492022-04-06 13:01:23 -0700560 if let Some(type_still_dependent_on_removed_lifetime) = param_types
Lukasz Anforowicz55673c92022-01-27 19:37:26 +0000561 .iter()
562 .skip(1) // Skipping `__this`
Lukasz Anforowicz90bdb962022-02-14 21:07:45 +0000563 .flat_map(|t| t.lifetimes())
564 .find(|lifetime_id| *lifetime_id == *no_longer_needed_lifetime_id)
Lukasz Anforowicz55673c92022-01-27 19:37:26 +0000565 {
566 bail!(
567 "The lifetime of `__this` is unexpectedly also used by another \
568 parameter {:?} in function {:?}",
569 type_still_dependent_on_removed_lifetime,
570 func.name
571 );
Lukasz Anforowicz95551272022-01-20 00:02:24 +0000572 }
573
574 // Rebind `maybe_first_api_param` to the next param after `__this`.
Devin Jeanpierre22f91492022-04-06 13:01:23 -0700575 maybe_first_api_param = param_types.get(1);
Lukasz Anforowiczab65e292022-01-14 23:04:21 +0000576
Devin Jeanpierread125742022-04-11 13:50:28 -0700577 if let TraitName::CtorNew(args_type) = trait_name {
578 // CtorNew has no self param, so this should never be used -- and we should fail
579 // if it is.
580 maybe_first_api_param = None;
581
582 return_type_fragment = quote! { -> Self::CtorType };
583 let args_type = format_tuple_except_singleton(args_type);
584 api_params = vec![quote! {args: #args_type}];
585 }
Devin Jeanpierre6f607372022-03-22 21:34:38 +0000586 }
587
Lukasz Anforowiczab65e292022-01-14 23:04:21 +0000588 // Change `__this: &'a SomeStruct` into `&'a self` if needed.
Devin Jeanpierred9a6e6c2022-03-29 02:55:41 -0700589 if impl_kind.format_first_param_as_self() {
Lukasz Anforowiczab65e292022-01-14 23:04:21 +0000590 let first_api_param = maybe_first_api_param
591 .ok_or_else(|| anyhow!("No parameter to format as 'self': {:?}", func))?;
Devin Jeanpierrefec5bfe2022-04-05 10:59:10 -0700592 let self_decl = first_api_param.format_as_self_param(func, ir).with_context(|| {
593 format!("Failed to format as `self` param: {:?}", first_api_param)
594 })?;
Lukasz Anforowiczf9564622022-01-28 14:31:04 +0000595 // Presence of element #0 is verified by `ok_or_else` on
596 // `maybe_first_api_param` above.
597 api_params[0] = self_decl;
598 thunk_args[0] = quote! { self };
Lukasz Anforowiczab65e292022-01-14 23:04:21 +0000599 }
600
Devin Jeanpierre6f607372022-03-22 21:34:38 +0000601 // TODO(b/200067242): the Pin-wrapping code doesn't know to wrap &mut
602 // MaybeUninit<T> in Pin if T is !Unpin. It should understand
603 // 'structural pinning', so that we do not need into_inner_unchecked()
604 // here.
Devin Jeanpierre3eb5bbd2022-04-06 12:56:19 -0700605 let thunk_ident = thunk_ident(func);
Devin Jeanpierre7bddfdb2022-03-14 11:04:40 +0000606 let func_body = match &impl_kind {
Devin Jeanpierre6f607372022-03-22 21:34:38 +0000607 ImplKind::Trait { trait_name: TraitName::CtorNew(..), .. } => {
Devin Jeanpierread125742022-04-11 13:50:28 -0700608 let thunk_vars = format_tuple_except_singleton(&thunk_args);
Devin Jeanpierredeea7892022-03-29 02:13:31 -0700609 // TODO(b/226447239): check for copy here and instead use copies in that case?
Devin Jeanpierre6f607372022-03-22 21:34:38 +0000610 quote! {
Devin Jeanpierread125742022-04-11 13:50:28 -0700611 let #thunk_vars = args;
Rosica Dejanovskaaccf00b2022-04-01 13:28:04 -0700612 ctor::FnCtor::new(move |dest: rust_std::pin::Pin<&mut rust_std::mem::MaybeUninit<Self>>| {
Devin Jeanpierre6f607372022-03-22 21:34:38 +0000613 unsafe {
Rosica Dejanovskadd9a9032022-04-12 07:34:41 -0700614 detail::#thunk_ident(rust_std::pin::Pin::into_inner_unchecked(dest) #( , #thunk_args )*);
Devin Jeanpierre6f607372022-03-22 21:34:38 +0000615 }
616 })
617 }
618 }
Devin Jeanpierre1b8f4a12022-03-22 21:29:42 +0000619 ImplKind::Trait { trait_name: TraitName::UnpinConstructor(..), .. } => {
Lukasz Anforowiczab65e292022-01-14 23:04:21 +0000620 // SAFETY: A user-defined constructor is not guaranteed to
621 // initialize all the fields. To make the `assume_init()` call
622 // below safe, the memory is zero-initialized first. This is a
623 // bit safer, because zero-initialized memory represents a valid
624 // value for the currently supported field types (this may
625 // change once the bindings generator starts supporting
626 // reference fields). TODO(b/213243309): Double-check if
627 // zero-initialization is desirable here.
628 quote! {
Rosica Dejanovskaaccf00b2022-04-01 13:28:04 -0700629 let mut tmp = rust_std::mem::MaybeUninit::<Self>::zeroed();
Lukasz Anforowiczab65e292022-01-14 23:04:21 +0000630 unsafe {
Rosica Dejanovskadd9a9032022-04-12 07:34:41 -0700631 detail::#thunk_ident( &mut tmp #( , #thunk_args )* );
Lukasz Anforowiczab65e292022-01-14 23:04:21 +0000632 tmp.assume_init()
Lukasz Anforowicz13cf7492021-12-22 15:29:52 +0000633 }
Lukasz Anforowicz13cf7492021-12-22 15:29:52 +0000634 }
Lukasz Anforowiczab65e292022-01-14 23:04:21 +0000635 }
Devin Jeanpierre7bddfdb2022-03-14 11:04:40 +0000636 _ => {
Rosica Dejanovskadd9a9032022-04-12 07:34:41 -0700637 let mut body = quote! { detail::#thunk_ident( #( #thunk_args ),* ) };
Devin Jeanpierre7bddfdb2022-03-14 11:04:40 +0000638 // Only need to wrap everything in an `unsafe { ... }` block if
639 // the *whole* api function is safe.
Devin Jeanpierre7c3d8ed2022-03-29 03:02:04 -0700640 if !impl_kind.is_unsafe() {
Devin Jeanpierre7bddfdb2022-03-14 11:04:40 +0000641 body = quote! { unsafe { #body } };
642 }
643 body
644 }
Lukasz Anforowiczab65e292022-01-14 23:04:21 +0000645 };
646
Devin Jeanpierre7c3d8ed2022-03-29 03:02:04 -0700647 let pub_ = match impl_kind {
648 ImplKind::None { .. } | ImplKind::Struct { .. } => quote! { pub },
649 ImplKind::Trait { .. } => quote! {},
650 };
651 let unsafe_ = if impl_kind.is_unsafe() {
652 quote! { unsafe }
653 } else {
654 quote! {}
Lukasz Anforowiczab65e292022-01-14 23:04:21 +0000655 };
656
Lukasz Anforowicz95551272022-01-20 00:02:24 +0000657 let lifetimes = lifetimes.into_iter().map(|l| format_lifetime_name(&l.name));
Lukasz Anforowicz5e623782022-03-14 16:52:23 +0000658 let fn_generic_params: TokenStream;
Devin Jeanpierre1b8f4a12022-03-22 21:29:42 +0000659 if let ImplKind::Trait { declare_lifetimes: true, trait_generic_params, .. } =
660 &mut impl_kind
661 {
Lukasz Anforowicz5e623782022-03-14 16:52:23 +0000662 *trait_generic_params = format_generic_params(lifetimes);
Devin Jeanpierre1b8f4a12022-03-22 21:29:42 +0000663 fn_generic_params = quote! {}
Lukasz Anforowicz5e623782022-03-14 16:52:23 +0000664 } else {
665 fn_generic_params = format_generic_params(lifetimes);
666 }
Lukasz Anforowicz95551272022-01-20 00:02:24 +0000667
Lukasz Anforowiczab65e292022-01-14 23:04:21 +0000668 quote! {
669 #[inline(always)]
Lukasz Anforowicz5e623782022-03-14 16:52:23 +0000670 #pub_ #unsafe_ fn #func_name #fn_generic_params(
671 #( #api_params ),* ) #return_type_fragment {
Lukasz Anforowiczab65e292022-01-14 23:04:21 +0000672 #func_body
673 }
Lukasz Anforowicz13cf7492021-12-22 15:29:52 +0000674 }
Michael Forstered642022021-10-04 09:48:25 +0000675 };
676
Devin Jeanpierred9cecff2022-03-29 02:53:58 -0700677 let doc_comment = generate_doc_comment(&func.doc_comment);
678 let mut features = BTreeSet::new();
Lukasz Anforowiczab65e292022-01-14 23:04:21 +0000679 let api_func: TokenStream;
680 let function_id: FunctionId;
Lukasz Anforowiczab65e292022-01-14 23:04:21 +0000681 match impl_kind {
Devin Jeanpierre7c3d8ed2022-03-29 03:02:04 -0700682 ImplKind::None { .. } => {
Lukasz Anforowiczab65e292022-01-14 23:04:21 +0000683 api_func = quote! { #doc_comment #api_func_def };
684 function_id = FunctionId { self_type: None, function_path: func_name.into() };
685 }
Devin Jeanpierre6784e5e2022-03-29 02:59:01 -0700686 ImplKind::Struct { record_name, .. } => {
Lukasz Anforowiczab65e292022-01-14 23:04:21 +0000687 api_func = quote! { impl #record_name { #doc_comment #api_func_def } };
688 function_id = FunctionId {
689 self_type: None,
690 function_path: syn::parse2(quote! { #record_name :: #func_name })?,
691 };
692 }
Lukasz Anforowicz5e623782022-03-14 16:52:23 +0000693 ImplKind::Trait { trait_name, record_name, trait_generic_params, .. } => {
Devin Jeanpierre6f607372022-03-22 21:34:38 +0000694 let extra_items;
695 match trait_name {
696 TraitName::CtorNew(..) => {
697 // This feature seems destined for stabilization, and makes the code
698 // simpler.
699 features.insert(make_rs_ident("type_alias_impl_trait"));
700 extra_items = quote! {type CtorType = impl ctor::Ctor<Output = Self>;};
701 }
702 _ => {
703 extra_items = quote! {};
704 }
705 };
Lukasz Anforowicz5e623782022-03-14 16:52:23 +0000706 api_func = quote! {
707 #doc_comment
708 impl #trait_generic_params #trait_name for #record_name {
Devin Jeanpierre6f607372022-03-22 21:34:38 +0000709 #extra_items
Lukasz Anforowicz5e623782022-03-14 16:52:23 +0000710 #api_func_def
711 }
712 };
Lukasz Anforowiczab65e292022-01-14 23:04:21 +0000713 function_id = FunctionId {
714 self_type: Some(record_name.into()),
715 function_path: syn::parse2(quote! { #trait_name :: #func_name })?,
716 };
717 }
718 }
719
Devin Jeanpierre3eb5bbd2022-04-06 12:56:19 -0700720 Ok(Some((RsSnippet { features, tokens: api_func }, thunk.into(), function_id)))
721}
Devin Jeanpierre7e9a1de2021-12-03 08:04:22 +0000722
Devin Jeanpierre3eb5bbd2022-04-06 12:56:19 -0700723fn generate_func_thunk(
724 func: &Func,
725 param_idents: &[Ident],
Devin Jeanpierre22f91492022-04-06 13:01:23 -0700726 param_types: &[RsTypeKind],
Devin Jeanpierre3eb5bbd2022-04-06 12:56:19 -0700727 return_type_fragment: &TokenStream,
728) -> Result<TokenStream> {
729 let thunk_attr = if can_skip_cc_thunk(func) {
730 let mangled_name = &func.mangled_name;
731 quote! {#[link_name = #mangled_name]}
732 } else {
733 quote! {}
Michael Forstered642022021-10-04 09:48:25 +0000734 };
735
Devin Jeanpierre3eb5bbd2022-04-06 12:56:19 -0700736 // For constructors, inject MaybeUninit into the type of `__this_` parameter.
Devin Jeanpierre22f91492022-04-06 13:01:23 -0700737 let mut param_types = param_types.into_iter();
Devin Jeanpierre3eb5bbd2022-04-06 12:56:19 -0700738 let mut self_param = None;
739 if func.name == UnqualifiedIdentifier::Constructor {
Devin Jeanpierre22f91492022-04-06 13:01:23 -0700740 let first_param = param_types
Devin Jeanpierre3eb5bbd2022-04-06 12:56:19 -0700741 .next()
742 .ok_or_else(|| anyhow!("Constructors should have at least one parameter (__this)"))?;
743 self_param = Some(first_param.format_mut_ref_as_uninitialized().with_context(|| {
744 format!(
745 "Failed to format `__this` param for a constructor thunk: {:?}",
746 func.params.get(0)
747 )
748 })?);
749 } else if func.name == UnqualifiedIdentifier::Destructor {
Devin Jeanpierre22f91492022-04-06 13:01:23 -0700750 let first_param = param_types
Devin Jeanpierre3eb5bbd2022-04-06 12:56:19 -0700751 .next()
752 .ok_or_else(|| anyhow!("Constructors should have at least one parameter (__this)"))?;
753 self_param = Some(first_param.format_ref_as_raw_ptr().with_context(|| {
754 format!(
755 "Failed to format `__this` param for a destructor thunk: {:?}",
756 func.params.get(0)
757 )
758 })?);
759 }
760
761 let thunk_ident = thunk_ident(func);
762 let lifetimes = func.lifetime_params.iter().map(|l| format_lifetime_name(&l.name));
763 let generic_params = format_generic_params(lifetimes);
Devin Jeanpierre22f91492022-04-06 13:01:23 -0700764 let param_types = self_param.into_iter().chain(param_types.map(|t| quote! {#t}));
Devin Jeanpierre3eb5bbd2022-04-06 12:56:19 -0700765
766 Ok(quote! {
767 #thunk_attr
768 pub(crate) fn #thunk_ident #generic_params( #( #param_idents: #param_types ),*
769 ) #return_type_fragment ;
770 })
Michael Forstered642022021-10-04 09:48:25 +0000771}
772
Michael Forstercc5941a2021-10-07 07:12:24 +0000773fn generate_doc_comment(comment: &Option<String>) -> TokenStream {
774 match comment {
Michael Forster028800b2021-10-05 12:39:59 +0000775 Some(text) => {
Marcel Hlopko89547752021-12-10 09:39:41 +0000776 // token_stream_printer (and rustfmt) don't put a space between /// and the doc
777 // comment, let's add it here so our comments are pretty.
Lukasz Anforowiczdd9ae0f2022-02-17 15:52:53 +0000778 let doc = format!(" {}", text.replace('\n', "\n "));
Michael Forster028800b2021-10-05 12:39:59 +0000779 quote! {#[doc=#doc]}
780 }
781 None => quote! {},
Michael Forstercc5941a2021-10-07 07:12:24 +0000782 }
783}
Lukasz Anforowiczdaff0402021-12-23 00:37:50 +0000784
Devin Jeanpierre92ca2612022-04-06 11:35:13 -0700785fn format_generic_params<T: ToTokens>(params: impl IntoIterator<Item = T>) -> TokenStream {
Lukasz Anforowiczdaff0402021-12-23 00:37:50 +0000786 let mut params = params.into_iter().peekable();
787 if params.peek().is_none() {
788 quote! {}
789 } else {
790 quote! { < #( #params ),* > }
791 }
792}
793
Devin Jeanpierread125742022-04-11 13:50:28 -0700794/// Formats singletons as themselves, and collections of n!=1 items as a tuple.
795///
796/// In other words, this formats a collection of things as if via `#(#items),*`,
797/// but without lint warnings.
798///
799/// For example:
800///
801/// * [] => ()
802/// * [x] => x // equivalent to (x), but lint-free.
803/// * [x, y] => (x, y)
804fn format_tuple_except_singleton<T: ToTokens>(items: &[T]) -> TokenStream {
805 match items {
806 [singleton] => quote! {#singleton},
807 items => quote! {(#(#items),*)},
808 }
809}
810
Lukasz Anforowicze57215c2022-01-12 14:54:16 +0000811fn should_implement_drop(record: &Record) -> bool {
812 match record.destructor.definition {
813 // TODO(b/202258760): Only omit destructor if `Copy` is specified.
814 SpecialMemberDefinition::Trivial => false,
815
816 // TODO(b/212690698): Avoid calling into the C++ destructor (e.g. let
817 // Rust drive `drop`-ing) to avoid (somewhat unergonomic) ManuallyDrop
818 // if we can ask Rust to preserve C++ field destruction order in
819 // NontrivialMembers case.
820 SpecialMemberDefinition::NontrivialMembers => true,
821
822 // The `impl Drop` for NontrivialUserDefined needs to call into the
823 // user-defined destructor on C++ side.
824 SpecialMemberDefinition::NontrivialUserDefined => true,
825
826 // TODO(b/213516512): Today the IR doesn't contain Func entries for
827 // deleted functions/destructors/etc. But, maybe we should generate
828 // `impl Drop` in this case? With `unreachable!`? With
829 // `std::mem::forget`?
830 SpecialMemberDefinition::Deleted => false,
831 }
832}
833
834/// Returns whether fields of type `ty` need to be wrapped in `ManuallyDrop<T>`
835/// to prevent the fields from being destructed twice (once by the C++
836/// destructor calkled from the `impl Drop` of the struct and once by `drop` on
837/// the Rust side).
838///
839/// A type is safe to destroy twice if it implements `Copy`. Fields of such
840/// don't need to be wrapped in `ManuallyDrop<T>` even if the struct
841/// containing the fields provides an `impl Drop` that calles into a C++
842/// destructor (in addition to dropping the fields on the Rust side).
843///
844/// Note that it is not enough to just be `!needs_drop<T>()`: Rust only
845/// guarantees that it is safe to use-after-destroy for `Copy` types. See
846/// e.g. the documentation for
847/// [`drop_in_place`](https://doc.rust-lang.org/std/ptr/fn.drop_in_place.html):
848///
849/// > if `T` is not `Copy`, using the pointed-to value after calling
850/// > `drop_in_place` can cause undefined behavior
Teddy Katzd2cd1422022-04-04 09:41:33 -0700851///
Devin Jeanpierrefec5bfe2022-04-05 10:59:10 -0700852/// For non-Copy union fields, failing to use `ManuallyDrop<T>` would
853/// additionally cause a compile-time error until https://github.com/rust-lang/rust/issues/55149 is stabilized.
Lukasz Anforowicze57215c2022-01-12 14:54:16 +0000854fn needs_manually_drop(ty: &ir::RsType, ir: &IR) -> Result<bool> {
855 let ty_implements_copy = RsTypeKind::new(ty, ir)?.implements_copy();
856 Ok(!ty_implements_copy)
857}
858
Devin Jeanpierrec0543eb2022-04-20 16:00:34 -0700859/// Generates Rust source code for a given incomplete record declaration.
860fn generate_incomplete_record(incomplete_record: &IncompleteRecord) -> Result<TokenStream> {
861 let ident = make_rs_ident(&incomplete_record.cc_name);
862 let name = &incomplete_record.cc_name;
863 Ok(quote! {
864 forward_declare::forward_declare!(
865 pub #ident __SPACE__ = __SPACE__ forward_declare::symbol!(#name)
866 );
867 })
868}
869
Michael Forsterbee84482021-10-13 08:35:38 +0000870/// Generates Rust source code for a given `Record` and associated assertions as
871/// a tuple.
Rosica Dejanovskab2bd59e2022-04-11 09:02:03 -0700872fn generate_record(
873 record: &Record,
874 ir: &IR,
875 overloaded_funcs: &HashSet<FunctionId>,
876) -> Result<GeneratedItem> {
Lukasz Anforowicz4c3a2cc2022-03-11 00:24:49 +0000877 let ident = make_rs_ident(&record.rs_name);
Michael Forstercc5941a2021-10-07 07:12:24 +0000878 let doc_comment = generate_doc_comment(&record.doc_comment);
Marcel Hlopkob4b28742021-09-15 12:45:20 +0000879 let field_idents =
Marcel Hlopkoeaae9b72022-01-21 15:54:11 +0000880 record.fields.iter().map(|f| make_rs_ident(&f.identifier.identifier)).collect_vec();
Michael Forstercc5941a2021-10-07 07:12:24 +0000881 let field_doc_coments =
882 record.fields.iter().map(|f| generate_doc_comment(&f.doc_comment)).collect_vec();
Lukasz Anforowiczfed64e62022-03-22 22:39:04 +0000883
884 let mut field_copy_trait_assertions: Vec<TokenStream> = vec![];
Devin Jeanpierre09c6f452021-09-29 07:34:24 +0000885 let field_types = record
886 .fields
887 .iter()
Devin Jeanpierreb69bcae2022-02-03 09:45:50 +0000888 .enumerate()
889 .map(|(i, f)| {
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +0000890 // [[no_unique_address]] fields are replaced by an unaligned block of memory
891 // which fills space up to the next field.
Devin Jeanpierreb69bcae2022-02-03 09:45:50 +0000892 // See: docs/struct_layout
893 if f.is_no_unique_address {
894 let next_offset = if let Some(next) = record.fields.get(i + 1) {
895 next.offset
896 } else {
897 record.size * 8
898 };
899 let width = Literal::usize_unsuffixed((next_offset - f.offset) / 8);
Rosica Dejanovskaaccf00b2022-04-01 13:28:04 -0700900 return Ok(quote! {[rust_std::mem::MaybeUninit<u8>; #width]});
Devin Jeanpierreb69bcae2022-02-03 09:45:50 +0000901 }
Devin Jeanpierrefec5bfe2022-04-05 10:59:10 -0700902 let mut formatted = format_rs_type(&f.type_.rs_type, ir).with_context(|| {
903 format!("Failed to format type for field {:?} on record {:?}", f, record)
904 })?;
Lukasz Anforowicze57215c2022-01-12 14:54:16 +0000905 // TODO(b/212696226): Verify cases where ManuallyDrop<T> is skipped
906 // via static asserts in the generated code.
Teddy Katzd2cd1422022-04-04 09:41:33 -0700907 if should_implement_drop(record) || record.is_union {
Lukasz Anforowiczfed64e62022-03-22 22:39:04 +0000908 if needs_manually_drop(&f.type_.rs_type, ir)? {
909 // TODO(b/212690698): Avoid (somewhat unergonomic) ManuallyDrop
910 // if we can ask Rust to preserve field destruction order if the
911 // destructor is the SpecialMemberDefinition::NontrivialMembers
912 // case.
Rosica Dejanovskaaccf00b2022-04-01 13:28:04 -0700913 formatted = quote! { rust_std::mem::ManuallyDrop<#formatted> }
Lukasz Anforowiczfed64e62022-03-22 22:39:04 +0000914 } else {
915 field_copy_trait_assertions.push(quote! {
Lukasz Anforowicz768bba32022-04-11 14:06:13 -0700916 const _: () = { static_assertions::assert_impl_all!(#formatted: Copy); };
Lukasz Anforowiczfed64e62022-03-22 22:39:04 +0000917 });
918 }
Lukasz Anforowicz6d553632022-01-06 21:36:14 +0000919 };
Devin Jeanpierre7e9a1de2021-12-03 08:04:22 +0000920 Ok(formatted)
921 })
Devin Jeanpierre09c6f452021-09-29 07:34:24 +0000922 .collect::<Result<Vec<_>>>()?;
Lukasz Anforowiczfed64e62022-03-22 22:39:04 +0000923
Googlerec589eb2021-09-17 07:45:39 +0000924 let field_accesses = record
925 .fields
926 .iter()
927 .map(|f| {
Devin Jeanpierreb69bcae2022-02-03 09:45:50 +0000928 if f.access == AccessSpecifier::Public && !f.is_no_unique_address {
Googlerec589eb2021-09-17 07:45:39 +0000929 quote! { pub }
930 } else {
931 quote! {}
932 }
933 })
934 .collect_vec();
Googlerec648ff2021-09-23 07:19:53 +0000935 let size = record.size;
936 let alignment = record.alignment;
Lukasz Anforowiczfed64e62022-03-22 22:39:04 +0000937 let field_offset_assertions =
Googleraaa0a532021-10-01 09:11:27 +0000938 record.fields.iter().zip(field_idents.iter()).map(|(field, field_ident)| {
939 let offset = field.offset;
940 quote! {
941 // The IR contains the offset in bits, while offset_of!()
942 // returns the offset in bytes, so we need to convert.
Googler209b10a2021-12-06 09:11:57 +0000943 const _: () = assert!(offset_of!(#ident, #field_ident) * 8 == #offset);
Googleraaa0a532021-10-01 09:11:27 +0000944 }
945 });
Lukasz Anforowiczfed64e62022-03-22 22:39:04 +0000946 // TODO(b/212696226): Generate `assert_impl_all!` or `assert_not_impl_all!`
947 // assertions about the `Copy` trait - this trait should be implemented
948 // iff `should_implement_drop(record)` is false.
Michael Forsterdb8101a2021-10-08 06:56:03 +0000949 let mut record_features = BTreeSet::new();
950 let mut assertion_features = BTreeSet::new();
Devin Jeanpierre273eeae2021-10-06 13:29:35 +0000951
952 // TODO(mboehme): For the time being, we're using unstable features to
953 // be able to use offset_of!() in static assertions. This is fine for a
954 // prototype, but longer-term we want to either get those features
955 // stabilized or find an alternative. For more details, see
956 // b/200120034#comment15
Marcel Hlopkoeaae9b72022-01-21 15:54:11 +0000957 assertion_features.insert(make_rs_ident("const_ptr_offset_from"));
Devin Jeanpierre2ed14ec2021-10-06 11:32:19 +0000958
Lukasz Anforowicze57215c2022-01-12 14:54:16 +0000959 let derives = generate_derives(record);
Devin Jeanpierre9227d2c2021-10-06 12:26:05 +0000960 let derives = if derives.is_empty() {
961 quote! {}
962 } else {
963 quote! {#[derive( #(#derives),* )]}
964 };
Teddy Katzd2cd1422022-04-04 09:41:33 -0700965 let record_kind = if record.is_union {
966 quote! { union }
967 } else {
968 quote! { struct }
969 };
Lukasz Anforowiczdd9ae0f2022-02-17 15:52:53 +0000970 let unpin_impl = if record.is_unpin() {
971 quote! {}
Devin Jeanpierreea700d32021-10-06 11:33:56 +0000972 } else {
Michael Forsterbee84482021-10-13 08:35:38 +0000973 // negative_impls are necessary for universal initialization due to Rust's
974 // coherence rules: PhantomPinned isn't enough to prove to Rust that a
975 // blanket impl that requires Unpin doesn't apply. See http://<internal link>=h.f6jp8ifzgt3n
Marcel Hlopkoeaae9b72022-01-21 15:54:11 +0000976 record_features.insert(make_rs_ident("negative_impls"));
Lukasz Anforowiczdd9ae0f2022-02-17 15:52:53 +0000977 quote! {
Michael Forsterdb8101a2021-10-08 06:56:03 +0000978 __NEWLINE__ __NEWLINE__
979 impl !Unpin for #ident {}
Lukasz Anforowiczdd9ae0f2022-02-17 15:52:53 +0000980 }
981 };
Devin Jeanpierre273eeae2021-10-06 13:29:35 +0000982
Devin Jeanpierrec80e6242022-02-03 01:56:40 +0000983 let mut repr_attributes = vec![quote! {C}];
984 if record.override_alignment && record.alignment > 1 {
985 let alignment = Literal::usize_unsuffixed(record.alignment);
986 repr_attributes.push(quote! {align(#alignment)});
987 }
988
989 // Adjust the struct to also include base class subobjects. We use an opaque
990 // field because subobjects can live in the alignment of base class
991 // subobjects.
992 let base_subobjects_field = if let Some(base_size) = record.base_size {
993 let n = proc_macro2::Literal::usize_unsuffixed(base_size);
994 quote! {
Rosica Dejanovskaaccf00b2022-04-01 13:28:04 -0700995 __base_class_subobjects: [rust_std::mem::MaybeUninit<u8>; #n],
Devin Jeanpierrec80e6242022-02-03 01:56:40 +0000996 }
997 } else {
998 quote! {}
999 };
1000
Devin Jeanpierre27450132022-04-11 13:52:01 -07001001 // TODO(b/227442773): After namespace support is added, use the fully-namespaced
1002 // name.
1003 let incomplete_symbol = &record.cc_name;
1004 let incomplete_definition = quote! {
1005 forward_declare::unsafe_define!(forward_declare::symbol!(#incomplete_symbol), #ident);
1006 };
1007
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +00001008 let empty_struct_placeholder_field =
1009 if record.fields.is_empty() && record.base_size.unwrap_or(0) == 0 {
1010 quote! {
1011 /// Prevent empty C++ struct being zero-size in Rust.
Rosica Dejanovskaaccf00b2022-04-01 13:28:04 -07001012 placeholder: rust_std::mem::MaybeUninit<u8>,
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +00001013 }
1014 } else {
1015 quote! {}
1016 };
Googlerf4792062021-10-20 07:21:21 +00001017
Devin Jeanpierre58181ac2022-02-14 21:30:05 +00001018 let no_unique_address_accessors = cc_struct_no_unique_address_impl(record, ir)?;
Devin Jeanpierre56777022022-02-03 01:57:15 +00001019 let base_class_into = cc_struct_upcast_impl(record, ir)?;
1020
Rosica Dejanovskab2bd59e2022-04-11 09:02:03 -07001021 let record_generated_items = record
1022 .child_item_ids
1023 .iter()
1024 .map(|id| {
1025 let item = ir.find_decl(*id)?;
1026 generate_item(item, ir, overloaded_funcs)
1027 })
1028 .collect::<Result<Vec<_>>>()?;
1029
1030 let mut items = vec![];
1031 let mut thunks_from_record_items = vec![];
1032 let mut assertions_from_record_items = vec![];
1033
1034 for generated in record_generated_items.iter() {
1035 items.push(&generated.item);
1036 if !generated.thunks.is_empty() {
1037 thunks_from_record_items.push(&generated.thunks);
1038 }
1039 if !generated.assertions.is_empty() {
1040 assertions_from_record_items.push(&generated.assertions);
1041 }
1042 record_features.extend(generated.features.clone());
1043 }
1044
Michael Forsterdb8101a2021-10-08 06:56:03 +00001045 let record_tokens = quote! {
Michael Forster028800b2021-10-05 12:39:59 +00001046 #doc_comment
Devin Jeanpierre9227d2c2021-10-06 12:26:05 +00001047 #derives
Devin Jeanpierrec80e6242022-02-03 01:56:40 +00001048 #[repr(#( #repr_attributes ),*)]
Teddy Katzd2cd1422022-04-04 09:41:33 -07001049 pub #record_kind #ident {
Devin Jeanpierrec80e6242022-02-03 01:56:40 +00001050 #base_subobjects_field
Michael Forstercc5941a2021-10-07 07:12:24 +00001051 #( #field_doc_coments #field_accesses #field_idents: #field_types, )*
Googlerf4792062021-10-20 07:21:21 +00001052 #empty_struct_placeholder_field
Marcel Hlopkob4b28742021-09-15 12:45:20 +00001053 }
Googlerec648ff2021-09-23 07:19:53 +00001054
Devin Jeanpierre27450132022-04-11 13:52:01 -07001055 #incomplete_definition
1056
Devin Jeanpierre58181ac2022-02-14 21:30:05 +00001057 #no_unique_address_accessors
1058
Devin Jeanpierre56777022022-02-03 01:57:15 +00001059 #base_class_into
1060
Devin Jeanpierreea700d32021-10-06 11:33:56 +00001061 #unpin_impl
Rosica Dejanovskab2bd59e2022-04-11 09:02:03 -07001062
1063 __NEWLINE__ __NEWLINE__
1064 #( #items __NEWLINE__ __NEWLINE__)*
Devin Jeanpierre273eeae2021-10-06 13:29:35 +00001065 };
1066
Lukasz Anforowicz95938f82022-03-24 13:51:54 +00001067 let record_trait_assertions = {
Devin Jeanpierre92ca2612022-04-06 11:35:13 -07001068 let record_type_name = RsTypeKind::new_record(record, ir).to_token_stream();
Lukasz Anforowicz95938f82022-03-24 13:51:54 +00001069 let mut assertions: Vec<TokenStream> = vec![];
1070 let mut add_assertion = |assert_impl_macro: TokenStream, trait_name: TokenStream| {
1071 assertions.push(quote! {
Lukasz Anforowicz768bba32022-04-11 14:06:13 -07001072 const _: () = { static_assertions::#assert_impl_macro (#record_type_name: #trait_name); };
Lukasz Anforowicz95938f82022-03-24 13:51:54 +00001073 });
1074 };
1075 if should_derive_clone(record) {
1076 add_assertion(quote! { assert_impl_all! }, quote! { Clone });
1077 } else {
1078 // Can't `assert_not_impl_all!` here, because `Clone` may be
1079 // implemented rather than derived.
1080 }
1081 let mut add_conditional_assertion = |should_impl_trait: bool, trait_name: TokenStream| {
1082 let assert_impl_macro = if should_impl_trait {
1083 quote! { assert_impl_all! }
1084 } else {
1085 quote! { assert_not_impl_all! }
1086 };
1087 add_assertion(assert_impl_macro, trait_name);
1088 };
1089 add_conditional_assertion(should_derive_copy(record), quote! { Copy });
1090 add_conditional_assertion(should_implement_drop(record), quote! { Drop });
1091 assertions
1092 };
Michael Forsterdb8101a2021-10-08 06:56:03 +00001093 let assertion_tokens = quote! {
Rosica Dejanovskaaccf00b2022-04-01 13:28:04 -07001094 const _: () = assert!(rust_std::mem::size_of::<#ident>() == #size);
1095 const _: () = assert!(rust_std::mem::align_of::<#ident>() == #alignment);
Lukasz Anforowicz95938f82022-03-24 13:51:54 +00001096 #( #record_trait_assertions )*
Lukasz Anforowiczfed64e62022-03-22 22:39:04 +00001097 #( #field_offset_assertions )*
1098 #( #field_copy_trait_assertions )*
Rosica Dejanovskab2bd59e2022-04-11 09:02:03 -07001099 #( #assertions_from_record_items )*
Michael Forsterdb8101a2021-10-08 06:56:03 +00001100 };
1101
Rosica Dejanovskab2bd59e2022-04-11 09:02:03 -07001102 let thunk_tokens = quote! {
1103 #( #thunks_from_record_items )*
1104 };
1105
1106 Ok(GeneratedItem {
1107 item: record_tokens,
1108 features: record_features.union(&assertion_features).cloned().collect(),
1109 assertions: assertion_tokens,
1110 thunks: thunk_tokens,
1111 has_record: true,
1112 })
Marcel Hlopkob4b28742021-09-15 12:45:20 +00001113}
1114
Lukasz Anforowicz2e41bb62022-01-11 18:23:07 +00001115fn should_derive_clone(record: &Record) -> bool {
Devin Jeanpierre88343c72022-01-15 01:10:23 +00001116 record.is_unpin()
Devin Jeanpierre2ed14ec2021-10-06 11:32:19 +00001117 && record.copy_constructor.access == ir::AccessSpecifier::Public
1118 && record.copy_constructor.definition == SpecialMemberDefinition::Trivial
Lukasz Anforowicz2e41bb62022-01-11 18:23:07 +00001119}
1120
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00001121fn should_derive_copy(record: &Record) -> bool {
1122 // TODO(b/202258760): Make `Copy` inclusion configurable.
1123 should_derive_clone(record)
1124}
1125
1126fn generate_derives(record: &Record) -> Vec<Ident> {
1127 let mut derives = vec![];
Lukasz Anforowicz2e41bb62022-01-11 18:23:07 +00001128 if should_derive_clone(record) {
Marcel Hlopkoeaae9b72022-01-21 15:54:11 +00001129 derives.push(make_rs_ident("Clone"));
Devin Jeanpierre2ed14ec2021-10-06 11:32:19 +00001130 }
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00001131 if should_derive_copy(record) {
Marcel Hlopkoeaae9b72022-01-21 15:54:11 +00001132 derives.push(make_rs_ident("Copy"));
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00001133 }
1134 derives
Devin Jeanpierre2ed14ec2021-10-06 11:32:19 +00001135}
1136
Teddy Katz76fa42b2022-02-23 01:22:56 +00001137fn generate_enum(enum_: &Enum, ir: &IR) -> Result<TokenStream> {
1138 let name = make_rs_ident(&enum_.identifier.identifier);
Devin Jeanpierre9886fb42022-04-01 04:31:20 -07001139 let underlying_type = format_rs_type(&enum_.underlying_type.rs_type, ir)?;
Teddy Katz76fa42b2022-02-23 01:22:56 +00001140 let enumerator_names =
1141 enum_.enumerators.iter().map(|enumerator| make_rs_ident(&enumerator.identifier.identifier));
1142 let enumerator_values = enum_.enumerators.iter().map(|enumerator| enumerator.value);
1143 Ok(quote! {
1144 #[repr(transparent)]
1145 #[derive(Debug, PartialEq, Eq, Copy, Clone, Hash, PartialOrd, Ord)]
1146 pub struct #name(#underlying_type);
1147 impl #name {
1148 #(pub const #enumerator_names: #name = #name(#enumerator_values);)*
1149 }
1150 impl From<#underlying_type> for #name {
1151 fn from(value: #underlying_type) -> #name {
1152 #name(v)
1153 }
1154 }
1155 impl From<#name> for #underlying_type {
1156 fn from(value: #name) -> #underlying_type {
1157 v.0
1158 }
1159 }
1160 })
1161}
1162
Googler6a0a5252022-01-11 14:08:09 +00001163fn generate_type_alias(type_alias: &TypeAlias, ir: &IR) -> Result<TokenStream> {
Marcel Hlopkoeaae9b72022-01-21 15:54:11 +00001164 let ident = make_rs_ident(&type_alias.identifier.identifier);
Lukasz Anforowiczc503af42022-03-04 23:18:15 +00001165 let doc_comment = generate_doc_comment(&type_alias.doc_comment);
Devin Jeanpierre9886fb42022-04-01 04:31:20 -07001166 let underlying_type = format_rs_type(&type_alias.underlying_type.rs_type, ir)
Googler6a0a5252022-01-11 14:08:09 +00001167 .with_context(|| format!("Failed to format underlying type for {:?}", type_alias))?;
Lukasz Anforowiczc503af42022-03-04 23:18:15 +00001168 Ok(quote! {
1169 #doc_comment
1170 pub type #ident = #underlying_type;
1171 })
Googler6a0a5252022-01-11 14:08:09 +00001172}
1173
Michael Forster523dbd42021-10-12 11:05:44 +00001174/// Generates Rust source code for a given `UnsupportedItem`.
1175fn generate_unsupported(item: &UnsupportedItem) -> Result<TokenStream> {
Googler48a74dd2021-10-25 07:31:53 +00001176 let location = if item.source_loc.filename.is_empty() {
1177 "<unknown location>".to_string()
1178 } else {
1179 // TODO(forster): The "google3" prefix should probably come from a command line
1180 // argument.
1181 // TODO(forster): Consider linking to the symbol instead of to the line number
1182 // to avoid wrong links while generated files have not caught up.
1183 format!("google3/{};l={}", &item.source_loc.filename, &item.source_loc.line)
1184 };
Michael Forster6a184ad2021-10-12 13:04:05 +00001185 let message = format!(
Googler48a74dd2021-10-25 07:31:53 +00001186 "{}\nError while generating bindings for item '{}':\n{}",
1187 &location, &item.name, &item.message
Michael Forster6a184ad2021-10-12 13:04:05 +00001188 );
Michael Forster523dbd42021-10-12 11:05:44 +00001189 Ok(quote! { __COMMENT__ #message })
1190}
1191
Michael Forsterf1dce422021-10-13 09:50:16 +00001192/// Generates Rust source code for a given `Comment`.
1193fn generate_comment(comment: &Comment) -> Result<TokenStream> {
1194 let text = &comment.text;
1195 Ok(quote! { __COMMENT__ #text })
1196}
1197
Rosica Dejanovskadd9a9032022-04-12 07:34:41 -07001198fn generate_namespace(
1199 namespace: &Namespace,
1200 ir: &IR,
1201 overloaded_funcs: &HashSet<FunctionId>,
1202) -> Result<GeneratedItem> {
1203 let mut items = vec![];
1204 let mut thunks = vec![];
1205 let mut assertions = vec![];
1206 let mut has_record = false;
1207 let mut features = BTreeSet::new();
1208
1209 for item_id in namespace.child_item_ids.iter() {
1210 let item = ir.find_decl(*item_id)?;
1211 let generated = generate_item(item, ir, &overloaded_funcs)?;
1212 items.push(generated.item);
1213 if !generated.thunks.is_empty() {
1214 thunks.push(generated.thunks);
1215 }
1216 if !generated.assertions.is_empty() {
1217 assertions.push(generated.assertions);
1218 }
1219 features.extend(generated.features);
1220 has_record = has_record || generated.has_record;
1221 }
1222
1223 let name = make_rs_ident(&namespace.name.identifier);
1224
1225 // TODO(rosica): Instead of generating thunks and assertions within
1226 // the namespace, we could put them at the end of the file.
1227 // For this we need to have fully qualified identifiers.
1228 let mod_detail = if thunks.is_empty() {
1229 quote! {}
1230 } else {
1231 quote! {
1232 mod detail {
1233 #[allow(unused_imports)]
1234 use super::*;
1235 extern "C" {
1236 #( #thunks )*
1237 }
1238 }
1239 }
1240 };
1241
1242 let namespace_tokens = quote! {
1243 pub mod #name {
1244
1245 #( #items __NEWLINE__ __NEWLINE__ )*
1246
1247 __NEWLINE__ __NEWLINE__
1248 #mod_detail __NEWLINE__ __NEWLINE__
1249
1250 #( #assertions __NEWLINE__ __NEWLINE__ )*
1251
1252 }
1253 };
1254
1255 Ok(GeneratedItem {
1256 item: namespace_tokens,
1257 features: features,
1258 has_record: has_record,
1259 ..Default::default()
1260 })
1261}
1262
Rosica Dejanovska8da8f792022-03-29 02:02:22 -07001263#[derive(Clone, Debug, Default)]
1264struct GeneratedItem {
1265 item: TokenStream,
1266 thunks: TokenStream,
1267 assertions: TokenStream,
1268 features: BTreeSet<Ident>,
1269 has_record: bool,
1270}
1271
1272fn generate_item(
1273 item: &Item,
1274 ir: &IR,
1275 overloaded_funcs: &HashSet<FunctionId>,
1276) -> Result<GeneratedItem> {
1277 let generated_item = match item {
Devin Jeanpierred7b48102022-03-31 04:15:03 -07001278 Item::Func(func) => match generate_func(func, ir) {
1279 Err(e) => GeneratedItem {
1280 item: generate_unsupported(&make_unsupported_fn(func, ir, format!("{e}"))?)?,
1281 ..Default::default()
1282 },
1283 Ok(None) => GeneratedItem::default(),
1284 Ok(Some((api_func, thunk, function_id))) => {
Rosica Dejanovska8da8f792022-03-29 02:02:22 -07001285 if overloaded_funcs.contains(&function_id) {
1286 GeneratedItem {
1287 item: generate_unsupported(&make_unsupported_fn(
1288 func,
1289 ir,
1290 "Cannot generate bindings for overloaded function",
1291 )?)?,
1292 ..Default::default()
1293 }
1294 } else {
1295 GeneratedItem {
1296 item: api_func.tokens,
1297 thunks: thunk.tokens,
1298 features: api_func.features.union(&thunk.features).cloned().collect(),
1299 ..Default::default()
1300 }
1301 }
1302 }
1303 },
Devin Jeanpierrec0543eb2022-04-20 16:00:34 -07001304 Item::IncompleteRecord(incomplete_record) => {
1305 if !ir.is_current_target(&incomplete_record.owning_target)
1306 && !ir.is_stdlib_target(&incomplete_record.owning_target)
1307 {
1308 GeneratedItem { ..Default::default() }
1309 } else {
1310 GeneratedItem {
1311 item: generate_incomplete_record(incomplete_record)?,
1312 ..Default::default()
1313 }
1314 }
1315 }
Rosica Dejanovska8da8f792022-03-29 02:02:22 -07001316 Item::Record(record) => {
1317 if !ir.is_current_target(&record.owning_target)
1318 && !ir.is_stdlib_target(&record.owning_target)
1319 {
1320 GeneratedItem { ..Default::default() }
1321 } else {
Rosica Dejanovskab2bd59e2022-04-11 09:02:03 -07001322 generate_record(record, ir, overloaded_funcs)?
Rosica Dejanovska8da8f792022-03-29 02:02:22 -07001323 }
1324 }
1325 Item::Enum(enum_) => {
1326 if !ir.is_current_target(&enum_.owning_target)
1327 && !ir.is_stdlib_target(&enum_.owning_target)
1328 {
1329 GeneratedItem { ..Default::default() }
1330 } else {
1331 GeneratedItem { item: generate_enum(enum_, ir)?, ..Default::default() }
1332 }
1333 }
1334 Item::TypeAlias(type_alias) => {
1335 if !ir.is_current_target(&type_alias.owning_target)
1336 && !ir.is_stdlib_target(&type_alias.owning_target)
1337 {
1338 GeneratedItem { ..Default::default() }
1339 } else {
1340 GeneratedItem { item: generate_type_alias(type_alias, ir)?, ..Default::default() }
1341 }
1342 }
1343 Item::UnsupportedItem(unsupported) => {
1344 GeneratedItem { item: generate_unsupported(unsupported)?, ..Default::default() }
1345 }
1346 Item::Comment(comment) => {
1347 GeneratedItem { item: generate_comment(comment)?, ..Default::default() }
1348 }
Devin Jeanpierrec0543eb2022-04-20 16:00:34 -07001349 Item::Namespace(namespace) => generate_namespace(namespace, ir, overloaded_funcs)?,
Rosica Dejanovska8da8f792022-03-29 02:02:22 -07001350 };
1351
1352 Ok(generated_item)
1353}
1354
Marcel Hlopko89547752021-12-10 09:39:41 +00001355fn generate_rs_api(ir: &IR) -> Result<TokenStream> {
Michael Forstered642022021-10-04 09:48:25 +00001356 let mut items = vec![];
Marcel Hlopko42abfc82021-08-09 07:03:17 +00001357 let mut thunks = vec![];
Michael Forsterdb8101a2021-10-08 06:56:03 +00001358 let mut assertions = vec![];
Marcel Hlopko42abfc82021-08-09 07:03:17 +00001359
Googler454f2652021-12-06 12:53:12 +00001360 // We import nullable pointers as an Option<&T> and assume that at the ABI
1361 // level, None is represented as a zero pointer value whereas Some is
1362 // represented as as non-zero pointer value. This seems like a pretty safe
1363 // assumption to make, but to provide some safeguard, assert that
1364 // `Option<&i32>` and `&i32` have the same size.
1365 assertions.push(quote! {
Rosica Dejanovskaaccf00b2022-04-01 13:28:04 -07001366 const _: () = assert!(rust_std::mem::size_of::<Option<&i32>>() == rust_std::mem::size_of::<&i32>());
Googler454f2652021-12-06 12:53:12 +00001367 });
1368
Michael Forsterbee84482021-10-13 08:35:38 +00001369 // TODO(jeanpierreda): Delete has_record, either in favor of using RsSnippet, or not
1370 // having uses. See https://chat.google.com/room/AAAAnQmj8Qs/6QbkSvWcfhA
Devin Jeanpierreea700d32021-10-06 11:33:56 +00001371 let mut has_record = false;
Devin Jeanpierre273eeae2021-10-06 13:29:35 +00001372 let mut features = BTreeSet::new();
Marcel Hlopko42abfc82021-08-09 07:03:17 +00001373
Lukasz Anforowicz72c4d222022-02-18 19:07:28 +00001374 // For #![rustfmt::skip].
1375 features.insert(make_rs_ident("custom_inner_attributes"));
1376
Googlerd03d05b2022-01-07 10:10:57 +00001377 // Identify all functions having overloads that we can't import (yet).
1378 // TODO(b/213280424): Implement support for overloaded functions.
1379 let mut seen_funcs = HashSet::new();
1380 let mut overloaded_funcs = HashSet::new();
1381 for func in ir.functions() {
Devin Jeanpierred7b48102022-03-31 04:15:03 -07001382 if let Ok(Some((.., function_id))) = generate_func(func, ir) {
Googlerd03d05b2022-01-07 10:10:57 +00001383 if !seen_funcs.insert(function_id.clone()) {
1384 overloaded_funcs.insert(function_id);
1385 }
1386 }
1387 }
1388
Rosica Dejanovskab2bd59e2022-04-11 09:02:03 -07001389 for top_level_item_id in ir.top_level_item_ids() {
1390 let item = ir.find_decl(*top_level_item_id)?;
Rosica Dejanovska8da8f792022-03-29 02:02:22 -07001391 let generated = generate_item(item, ir, &overloaded_funcs)?;
1392 items.push(generated.item);
Devin Jeanpierreb5ba1402022-03-29 07:29:24 -07001393 if !generated.thunks.is_empty() {
Rosica Dejanovska8da8f792022-03-29 02:02:22 -07001394 thunks.push(generated.thunks);
Michael Forstered642022021-10-04 09:48:25 +00001395 }
Devin Jeanpierreb5ba1402022-03-29 07:29:24 -07001396 if !generated.assertions.is_empty() {
Rosica Dejanovska8da8f792022-03-29 02:02:22 -07001397 assertions.push(generated.assertions);
1398 }
1399 features.extend(generated.features);
1400 has_record = has_record || generated.has_record;
Marcel Hlopko42abfc82021-08-09 07:03:17 +00001401 }
1402
Marcel Hlopkob4b28742021-09-15 12:45:20 +00001403 let mod_detail = if thunks.is_empty() {
1404 quote! {}
1405 } else {
1406 quote! {
1407 mod detail {
Googler55647142022-01-11 12:37:39 +00001408 #[allow(unused_imports)]
Devin Jeanpierred4dde0e2021-10-13 20:48:25 +00001409 use super::*;
Marcel Hlopkob4b28742021-09-15 12:45:20 +00001410 extern "C" {
1411 #( #thunks )*
1412 }
Marcel Hlopko42abfc82021-08-09 07:03:17 +00001413 }
1414 }
1415 };
1416
Rosica Dejanovskaaccf00b2022-04-01 13:28:04 -07001417 // TODO(b/227790881): Replace usage of rust_std with ::std once the issue
1418 // is fixed.
Devin Jeanpierreea700d32021-10-06 11:33:56 +00001419 let imports = if has_record {
Googlerec648ff2021-09-23 07:19:53 +00001420 quote! {
Rosica Dejanovskabbed2012022-04-05 04:06:30 -07001421 use ::std as rust_std;
Googleraaa0a532021-10-01 09:11:27 +00001422 use memoffset_unstable_const::offset_of;
Googlerec648ff2021-09-23 07:19:53 +00001423 }
Michael Forstered642022021-10-04 09:48:25 +00001424 } else {
Rosica Dejanovskaaccf00b2022-04-01 13:28:04 -07001425 quote! {
Rosica Dejanovskabbed2012022-04-05 04:06:30 -07001426 use ::std as rust_std;
Rosica Dejanovskaaccf00b2022-04-01 13:28:04 -07001427 }
Googlerec648ff2021-09-23 07:19:53 +00001428 };
1429
Devin Jeanpierre273eeae2021-10-06 13:29:35 +00001430 let features = if features.is_empty() {
1431 quote! {}
1432 } else {
1433 quote! {
1434 #![feature( #(#features),* )]
1435 }
1436 };
1437
Marcel Hlopko89547752021-12-10 09:39:41 +00001438 Ok(quote! {
Googler55647142022-01-11 12:37:39 +00001439 #features __NEWLINE__
1440 #![allow(non_camel_case_types)] __NEWLINE__
1441 #![allow(non_snake_case)] __NEWLINE__ __NEWLINE__
1442
Michael Forsterdb8101a2021-10-08 06:56:03 +00001443 #imports __NEWLINE__ __NEWLINE__
Googlerec648ff2021-09-23 07:19:53 +00001444
Michael Forsterdb8101a2021-10-08 06:56:03 +00001445 #( #items __NEWLINE__ __NEWLINE__ )*
Marcel Hlopkob4b28742021-09-15 12:45:20 +00001446
Michael Forsterdb8101a2021-10-08 06:56:03 +00001447 #mod_detail __NEWLINE__ __NEWLINE__
1448
1449 #( #assertions __NEWLINE__ __NEWLINE__ )*
Marcel Hlopko89547752021-12-10 09:39:41 +00001450 })
Marcel Hlopko42abfc82021-08-09 07:03:17 +00001451}
1452
Marcel Hlopkoeaae9b72022-01-21 15:54:11 +00001453/// Makes an 'Ident' to be used in the Rust source code. Escapes Rust keywords.
1454fn make_rs_ident(ident: &str) -> Ident {
1455 // TODO(https://github.com/dtolnay/syn/pull/1098): Remove the hardcoded list once syn recognizes
1456 // 2018 and 2021 keywords.
1457 if ["async", "await", "try", "dyn"].contains(&ident) {
1458 return format_ident!("r#{}", ident);
1459 }
1460 match syn::parse_str::<syn::Ident>(ident) {
1461 Ok(_) => format_ident!("{}", ident),
1462 Err(_) => format_ident!("r#{}", ident),
1463 }
1464}
1465
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +00001466/// Formats a C++ identifier. Does not escape C++ keywords.
1467fn format_cc_ident(ident: &str) -> TokenStream {
1468 ident.parse().unwrap()
Marcel Hlopko42abfc82021-08-09 07:03:17 +00001469}
1470
Devin Jeanpierrefec5bfe2022-04-05 10:59:10 -07001471/// Returns Some(crate_ident) if this is an imported crate.
1472fn rs_imported_crate_name(owning_target: &BazelLabel, ir: &IR) -> Option<Ident> {
Googler6a0a5252022-01-11 14:08:09 +00001473 if ir.is_current_target(owning_target) || ir.is_stdlib_target(owning_target) {
Devin Jeanpierrefec5bfe2022-04-05 10:59:10 -07001474 None
Googler6a0a5252022-01-11 14:08:09 +00001475 } else {
Devin Jeanpierre084ebbd2022-04-01 04:32:46 -07001476 let owning_crate_name = owning_target.target_name();
Marcel Hlopkod906b892022-01-27 08:52:36 +00001477 // TODO(b/216587072): Remove this hacky escaping and use the import! macro once
1478 // available
Lukasz Anforowiczdd9ae0f2022-02-17 15:52:53 +00001479 let escaped_owning_crate_name = owning_crate_name.replace('-', "_");
Marcel Hlopkod906b892022-01-27 08:52:36 +00001480 let owning_crate = make_rs_ident(&escaped_owning_crate_name);
Devin Jeanpierrefec5bfe2022-04-05 10:59:10 -07001481 Some(owning_crate)
Googler6a0a5252022-01-11 14:08:09 +00001482 }
1483}
1484
Devin Jeanpierre103cbb52022-04-06 12:55:16 -07001485#[derive(Copy, Clone, Debug, Eq, PartialEq)]
Lukasz Anforowicz40c2eb82022-01-11 18:22:31 +00001486enum Mutability {
1487 Const,
1488 Mut,
1489}
1490
1491impl Mutability {
1492 fn format_for_pointer(&self) -> TokenStream {
1493 match self {
1494 Mutability::Mut => quote! {mut},
1495 Mutability::Const => quote! {const},
1496 }
1497 }
1498
1499 fn format_for_reference(&self) -> TokenStream {
1500 match self {
1501 Mutability::Mut => quote! {mut},
1502 Mutability::Const => quote! {},
1503 }
1504 }
1505}
1506
1507// TODO(b/213947473): Instead of having a separate RsTypeKind here, consider
1508// changing ir::RsType into a similar `enum`, with fields that contain
Rosica Dejanovskad638cf52022-03-23 15:45:01 +00001509// references (e.g. &'ir Record`) instead of ItemIds.
Devin Jeanpierre103cbb52022-04-06 12:55:16 -07001510#[derive(Clone, Debug)]
Lukasz Anforowicz40c2eb82022-01-11 18:22:31 +00001511enum RsTypeKind<'ir> {
Devin Jeanpierrefec5bfe2022-04-05 10:59:10 -07001512 Pointer {
1513 pointee: Box<RsTypeKind<'ir>>,
1514 mutability: Mutability,
1515 },
1516 Reference {
1517 referent: Box<RsTypeKind<'ir>>,
1518 mutability: Mutability,
Marcel Hlopkoaf682a02022-04-08 07:27:14 -07001519 lifetime: LifetimeName,
Devin Jeanpierrefec5bfe2022-04-05 10:59:10 -07001520 },
1521 RvalueReference {
1522 referent: Box<RsTypeKind<'ir>>,
1523 mutability: Mutability,
Marcel Hlopkoaf682a02022-04-08 07:27:14 -07001524 lifetime: LifetimeName,
Devin Jeanpierrefec5bfe2022-04-05 10:59:10 -07001525 },
1526 FuncPtr {
1527 abi: &'ir str,
1528 return_type: Box<RsTypeKind<'ir>>,
1529 param_types: Vec<RsTypeKind<'ir>>,
1530 },
Devin Jeanpierrec0543eb2022-04-20 16:00:34 -07001531 /// An incomplete record type.
1532 IncompleteRecord {
1533 incomplete_record: &'ir IncompleteRecord,
1534
1535 /// The imported crate this comes from, or None if the current crate.
1536 crate_ident: Option<Ident>,
1537 },
1538 /// A complete record type.
Devin Jeanpierrefec5bfe2022-04-05 10:59:10 -07001539 Record {
1540 record: &'ir Record,
1541 /// The imported crate this comes from, or None if the current crate.
1542 crate_ident: Option<Ident>,
1543 },
1544 TypeAlias {
1545 type_alias: &'ir TypeAlias,
1546 underlying_type: Box<RsTypeKind<'ir>>,
1547 /// The imported crate this comes from, or None if the current crate.
1548 crate_ident: Option<Ident>,
1549 },
Lukasz Anforowicz40c2eb82022-01-11 18:22:31 +00001550 Unit,
Devin Jeanpierrefec5bfe2022-04-05 10:59:10 -07001551 Other {
1552 name: &'ir str,
1553 type_args: Vec<RsTypeKind<'ir>>,
1554 },
Lukasz Anforowicz40c2eb82022-01-11 18:22:31 +00001555}
1556
1557impl<'ir> RsTypeKind<'ir> {
1558 pub fn new(ty: &'ir ir::RsType, ir: &'ir IR) -> Result<Self> {
1559 // The lambdas deduplicate code needed by multiple `match` branches.
1560 let get_type_args = || -> Result<Vec<RsTypeKind<'ir>>> {
1561 ty.type_args.iter().map(|type_arg| RsTypeKind::<'ir>::new(type_arg, ir)).collect()
1562 };
1563 let get_pointee = || -> Result<Box<RsTypeKind<'ir>>> {
1564 if ty.type_args.len() != 1 {
1565 bail!("Missing pointee/referent type (need exactly 1 type argument): {:?}", ty);
1566 }
1567 Ok(Box::new(get_type_args()?.remove(0)))
1568 };
Marcel Hlopkoaf682a02022-04-08 07:27:14 -07001569 let get_lifetime = || -> Result<LifetimeName> {
Lukasz Anforowicz40c2eb82022-01-11 18:22:31 +00001570 if ty.lifetime_args.len() != 1 {
1571 bail!("Missing reference lifetime (need exactly 1 lifetime argument): {:?}", ty);
1572 }
Devin Jeanpierred2b7a8f2022-04-01 04:37:16 -07001573 let lifetime_id = ty.lifetime_args[0];
1574 ir.get_lifetime(lifetime_id)
1575 .ok_or_else(|| anyhow!("no known lifetime with id {lifetime_id:?}"))
1576 .cloned()
Lukasz Anforowicz40c2eb82022-01-11 18:22:31 +00001577 };
1578
1579 let result = match ty.name.as_deref() {
1580 None => {
1581 ensure!(
1582 ty.type_args.is_empty(),
1583 "Type arguments on records nor type aliases are not yet supported: {:?}",
1584 ty
1585 );
1586 match ir.item_for_type(ty)? {
Devin Jeanpierrec0543eb2022-04-20 16:00:34 -07001587 Item::IncompleteRecord(incomplete_record) => RsTypeKind::IncompleteRecord {
1588 incomplete_record,
1589 crate_ident: rs_imported_crate_name(&incomplete_record.owning_target, ir),
1590 },
Devin Jeanpierrefec5bfe2022-04-05 10:59:10 -07001591 Item::Record(record) => RsTypeKind::new_record(record, ir),
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00001592 Item::TypeAlias(type_alias) => RsTypeKind::TypeAlias {
1593 type_alias,
Devin Jeanpierrefec5bfe2022-04-05 10:59:10 -07001594 crate_ident: rs_imported_crate_name(&type_alias.owning_target, ir),
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00001595 underlying_type: Box::new(RsTypeKind::new(
1596 &type_alias.underlying_type.rs_type,
1597 ir,
1598 )?),
1599 },
Lukasz Anforowicz40c2eb82022-01-11 18:22:31 +00001600 other_item => bail!("Item does not define a type: {:?}", other_item),
1601 }
1602 }
1603 Some(name) => match name {
1604 "()" => {
1605 if !ty.type_args.is_empty() {
1606 bail!("Unit type must not have type arguments: {:?}", ty);
1607 }
1608 RsTypeKind::Unit
1609 }
1610 "*mut" => {
1611 RsTypeKind::Pointer { pointee: get_pointee()?, mutability: Mutability::Mut }
1612 }
1613 "*const" => {
1614 RsTypeKind::Pointer { pointee: get_pointee()?, mutability: Mutability::Const }
1615 }
1616 "&mut" => RsTypeKind::Reference {
1617 referent: get_pointee()?,
1618 mutability: Mutability::Mut,
Devin Jeanpierred2b7a8f2022-04-01 04:37:16 -07001619 lifetime: get_lifetime()?,
Lukasz Anforowicz40c2eb82022-01-11 18:22:31 +00001620 },
1621 "&" => RsTypeKind::Reference {
1622 referent: get_pointee()?,
1623 mutability: Mutability::Const,
Devin Jeanpierred2b7a8f2022-04-01 04:37:16 -07001624 lifetime: get_lifetime()?,
Lukasz Anforowicz40c2eb82022-01-11 18:22:31 +00001625 },
Devin Jeanpierredeea7892022-03-29 02:13:31 -07001626 "#RvalueReference mut" => RsTypeKind::RvalueReference {
1627 referent: get_pointee()?,
1628 mutability: Mutability::Mut,
Devin Jeanpierred2b7a8f2022-04-01 04:37:16 -07001629 lifetime: get_lifetime()?,
Devin Jeanpierredeea7892022-03-29 02:13:31 -07001630 },
1631 "#RvalueReference const" => RsTypeKind::RvalueReference {
1632 referent: get_pointee()?,
1633 mutability: Mutability::Const,
Devin Jeanpierred2b7a8f2022-04-01 04:37:16 -07001634 lifetime: get_lifetime()?,
Devin Jeanpierredeea7892022-03-29 02:13:31 -07001635 },
Lukasz Anforowiczcf230fd2022-02-18 19:20:39 +00001636 name => {
1637 let mut type_args = get_type_args()?;
1638 match name.strip_prefix("#funcPtr ") {
1639 None => RsTypeKind::Other { name, type_args },
1640 Some(abi) => {
1641 // TODO(b/217419782): Consider enforcing `'static` lifetime.
1642 ensure!(!type_args.is_empty(), "No return type in fn type: {:?}", ty);
1643 RsTypeKind::FuncPtr {
1644 abi,
1645 return_type: Box::new(type_args.remove(type_args.len() - 1)),
1646 param_types: type_args,
1647 }
Devin Jeanpierre1b8f4a12022-03-22 21:29:42 +00001648 }
Lukasz Anforowiczcf230fd2022-02-18 19:20:39 +00001649 }
Devin Jeanpierre1b8f4a12022-03-22 21:29:42 +00001650 }
Lukasz Anforowicz40c2eb82022-01-11 18:22:31 +00001651 },
1652 };
1653 Ok(result)
1654 }
1655
Devin Jeanpierrefec5bfe2022-04-05 10:59:10 -07001656 pub fn new_record(record: &'ir Record, ir: &'ir IR) -> Self {
1657 RsTypeKind::Record {
1658 record,
1659 crate_ident: rs_imported_crate_name(&record.owning_target, ir),
1660 }
1661 }
1662
Devin Jeanpierre149950d2022-02-22 21:02:02 +00001663 /// Returns true if the type is known to be `Unpin`, false otherwise.
Devin Jeanpierref85ae592022-04-01 04:33:57 -07001664 pub fn is_unpin(&self) -> bool {
Devin Jeanpierre149950d2022-02-22 21:02:02 +00001665 match self {
Devin Jeanpierrec0543eb2022-04-20 16:00:34 -07001666 RsTypeKind::IncompleteRecord { .. } => false,
Devin Jeanpierrefec5bfe2022-04-05 10:59:10 -07001667 RsTypeKind::Record { record, .. } => record.is_unpin(),
Devin Jeanpierref85ae592022-04-01 04:33:57 -07001668 RsTypeKind::TypeAlias { underlying_type, .. } => underlying_type.is_unpin(),
Devin Jeanpierre149950d2022-02-22 21:02:02 +00001669 _ => true,
1670 }
1671 }
1672
Devin Jeanpierre82e8e362022-04-05 11:04:55 -07001673 pub fn format_as_return_type_fragment(&self) -> TokenStream {
Lukasz Anforowiczcf230fd2022-02-18 19:20:39 +00001674 match self {
Devin Jeanpierred2b7a8f2022-04-01 04:37:16 -07001675 RsTypeKind::Unit => quote! {},
Lukasz Anforowiczcf230fd2022-02-18 19:20:39 +00001676 other_type => {
Devin Jeanpierre92ca2612022-04-06 11:35:13 -07001677 quote! { -> #other_type }
Lukasz Anforowiczcf230fd2022-02-18 19:20:39 +00001678 }
1679 }
1680 }
1681
Lukasz Anforowiczcde4b1b2022-02-03 21:20:55 +00001682 /// Formats this RsTypeKind as `&'a mut MaybeUninit<SomeStruct>`. This is
1683 /// used to format `__this` parameter in a constructor thunk.
Devin Jeanpierre82e8e362022-04-05 11:04:55 -07001684 pub fn format_mut_ref_as_uninitialized(&self) -> Result<TokenStream> {
Lukasz Anforowiczcde4b1b2022-02-03 21:20:55 +00001685 match self {
Devin Jeanpierred2b7a8f2022-04-01 04:37:16 -07001686 RsTypeKind::Reference { referent, lifetime, mutability: Mutability::Mut } => {
Devin Jeanpierred2b7a8f2022-04-01 04:37:16 -07001687 let lifetime = format_lifetime_name(&lifetime.name);
Devin Jeanpierre92ca2612022-04-06 11:35:13 -07001688 Ok(quote! { & #lifetime mut rust_std::mem::MaybeUninit< #referent > })
Lukasz Anforowicz40c2eb82022-01-11 18:22:31 +00001689 }
Devin Jeanpierre149950d2022-02-22 21:02:02 +00001690 _ => bail!("Expected reference to format as MaybeUninit, got: {:?}", self),
Lukasz Anforowiczcde4b1b2022-02-03 21:20:55 +00001691 }
Lukasz Anforowicz40c2eb82022-01-11 18:22:31 +00001692 }
1693
Devin Jeanpierre149950d2022-02-22 21:02:02 +00001694 /// Formats a reference or pointer as a raw pointer.
Devin Jeanpierre82e8e362022-04-05 11:04:55 -07001695 pub fn format_ref_as_raw_ptr(&self) -> Result<TokenStream> {
Devin Jeanpierre149950d2022-02-22 21:02:02 +00001696 match self {
1697 RsTypeKind::Reference { referent: pointee, mutability, .. }
1698 | RsTypeKind::Pointer { pointee, mutability } => {
Devin Jeanpierre149950d2022-02-22 21:02:02 +00001699 let mut_ = mutability.format_for_pointer();
Devin Jeanpierre92ca2612022-04-06 11:35:13 -07001700 Ok(quote! { * #mut_ #pointee })
Devin Jeanpierre149950d2022-02-22 21:02:02 +00001701 }
1702 _ => bail!("Expected reference to format as raw ptr, got: {:?}", self),
1703 }
1704 }
1705
1706 /// Formats this RsTypeKind as the `self` parameter: usually, `&'a self` or
1707 /// `&'a mut self`.
1708 ///
1709 /// If this is !Unpin, however, it uses `self: Pin<&mut Self>` instead.
Devin Jeanpierre9886fb42022-04-01 04:31:20 -07001710 pub fn format_as_self_param(&self, func: &Func, ir: &IR) -> Result<TokenStream> {
Lukasz Anforowicz732ca642022-02-03 20:58:38 +00001711 if func.name == UnqualifiedIdentifier::Destructor {
1712 let record = func
1713 .member_func_metadata
1714 .as_ref()
1715 .ok_or_else(|| anyhow!("Destructors must be member functions: {:?}", func))?
1716 .find_record(ir)?;
1717 if self.is_mut_ptr_to(record) {
1718 // Even in C++ it is UB to retain `this` pointer and dereference it
1719 // after a destructor runs. Therefore it is safe to use `&self` or
1720 // `&mut self` in Rust even if IR represents `__this` as a Rust
1721 // pointer (e.g. when lifetime annotations are missing - lifetime
1722 // annotations are required to represent it as a Rust reference).
1723 return Ok(quote! { &mut self });
1724 }
Lukasz Anforowicz231a3bb2022-01-12 14:05:59 +00001725 }
1726
1727 match self {
Devin Jeanpierred2b7a8f2022-04-01 04:37:16 -07001728 RsTypeKind::Reference { referent, lifetime, mutability } => {
Devin Jeanpierre149950d2022-02-22 21:02:02 +00001729 let mut_ = mutability.format_for_reference();
Devin Jeanpierred2b7a8f2022-04-01 04:37:16 -07001730 let lifetime = format_lifetime_name(&lifetime.name);
Devin Jeanpierre1b8f4a12022-03-22 21:29:42 +00001731 if mutability == &Mutability::Mut
Devin Jeanpierref85ae592022-04-01 04:33:57 -07001732 && !referent.is_unpin()
Devin Jeanpierre1b8f4a12022-03-22 21:29:42 +00001733 && func.name != UnqualifiedIdentifier::Destructor
1734 {
Devin Jeanpierrefec5bfe2022-04-05 10:59:10 -07001735 // TODO(b/200067242): Add a `use rust_std::pin::Pin` to the crate, and use
1736 // `Pin`.
Rosica Dejanovskaaccf00b2022-04-01 13:28:04 -07001737 Ok(quote! {self: rust_std::pin::Pin< & #lifetime #mut_ Self>})
Devin Jeanpierre149950d2022-02-22 21:02:02 +00001738 } else {
1739 Ok(quote! { & #lifetime #mut_ self })
1740 }
Lukasz Anforowicz231a3bb2022-01-12 14:05:59 +00001741 }
Lukasz Anforowicz732ca642022-02-03 20:58:38 +00001742 _ => bail!("Unexpected type of `self` parameter: {:?}", self),
Lukasz Anforowicz231a3bb2022-01-12 14:05:59 +00001743 }
1744 }
1745
Lukasz Anforowicz90bdb962022-02-14 21:07:45 +00001746 /// Returns whether the type represented by `self` implements the `Copy`
1747 /// trait.
Lukasz Anforowicza94ab702022-01-14 22:40:25 +00001748 pub fn implements_copy(&self) -> bool {
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00001749 // TODO(b/212696226): Verify results of `implements_copy` via static
1750 // assertions in the generated Rust code (because incorrect results
1751 // can silently lead to unsafe behavior).
1752 match self {
1753 RsTypeKind::Unit => true,
1754 RsTypeKind::Pointer { .. } => true,
Lukasz Anforowiczcf230fd2022-02-18 19:20:39 +00001755 RsTypeKind::FuncPtr { .. } => true,
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00001756 RsTypeKind::Reference { mutability: Mutability::Const, .. } => true,
1757 RsTypeKind::Reference { mutability: Mutability::Mut, .. } => false,
Devin Jeanpierredeea7892022-03-29 02:13:31 -07001758 RsTypeKind::RvalueReference { .. } => false,
Devin Jeanpierrec0543eb2022-04-20 16:00:34 -07001759 RsTypeKind::IncompleteRecord { .. } => false,
Devin Jeanpierrefec5bfe2022-04-05 10:59:10 -07001760 RsTypeKind::Record { record, .. } => should_derive_copy(record),
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00001761 RsTypeKind::TypeAlias { underlying_type, .. } => underlying_type.implements_copy(),
Lukasz Anforowiczd81bea92022-02-11 08:57:58 +00001762 RsTypeKind::Other { type_args, .. } => {
Lukasz Anforowicz20651e32022-02-10 14:52:15 +00001763 // All types that may appear here without `type_args` (e.g.
1764 // primitive types like `i32`) implement `Copy`. Generic types
1765 // that may be present here (e.g. Option<...>) are `Copy` if all
1766 // of their `type_args` are `Copy`.
1767 type_args.iter().all(|t| t.implements_copy())
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00001768 }
1769 }
1770 }
Lukasz Anforowicza94ab702022-01-14 22:40:25 +00001771
Lukasz Anforowiczf9564622022-01-28 14:31:04 +00001772 pub fn is_mut_ptr_to(&self, expected_record: &Record) -> bool {
1773 match self {
1774 RsTypeKind::Pointer { pointee, mutability: Mutability::Mut, .. } => {
1775 pointee.is_record(expected_record)
1776 }
1777 _ => false,
1778 }
1779 }
1780
1781 pub fn is_ref_to(&self, expected_record: &Record) -> bool {
1782 match self {
1783 RsTypeKind::Reference { referent, .. } => referent.is_record(expected_record),
1784 _ => false,
1785 }
1786 }
1787
Lukasz Anforowicza94ab702022-01-14 22:40:25 +00001788 pub fn is_shared_ref_to(&self, expected_record: &Record) -> bool {
1789 match self {
1790 RsTypeKind::Reference { referent, mutability: Mutability::Const, .. } => {
Lukasz Anforowiczf9564622022-01-28 14:31:04 +00001791 referent.is_record(expected_record)
Lukasz Anforowicza94ab702022-01-14 22:40:25 +00001792 }
1793 _ => false,
1794 }
1795 }
Lukasz Anforowiczf9564622022-01-28 14:31:04 +00001796
1797 pub fn is_record(&self, expected_record: &Record) -> bool {
1798 match self {
Devin Jeanpierrefec5bfe2022-04-05 10:59:10 -07001799 RsTypeKind::Record { record: actual_record, .. } => {
1800 actual_record.id == expected_record.id
1801 }
Lukasz Anforowiczf9564622022-01-28 14:31:04 +00001802 _ => false,
1803 }
1804 }
Lukasz Anforowicz90bdb962022-02-14 21:07:45 +00001805
1806 /// Iterates over `self` and all the nested types (e.g. pointees, generic
1807 /// type args, etc.) in DFS order.
1808 pub fn dfs_iter<'ty>(&'ty self) -> impl Iterator<Item = &'ty RsTypeKind<'ir>> + '_ {
1809 RsTypeKindIter::new(self)
1810 }
1811
1812 /// Iterates over all `LifetimeId`s in `self` and in all the nested types.
1813 /// Note that the results might contain duplicate LifetimeId values (e.g.
1814 /// if the same LifetimeId is used in two `type_args`).
1815 pub fn lifetimes(&self) -> impl Iterator<Item = LifetimeId> + '_ {
1816 self.dfs_iter().filter_map(|t| match t {
Devin Jeanpierred2b7a8f2022-04-01 04:37:16 -07001817 RsTypeKind::Reference { lifetime, .. } => Some(lifetime.id),
Lukasz Anforowicz90bdb962022-02-14 21:07:45 +00001818 _ => None,
1819 })
1820 }
1821}
1822
Devin Jeanpierre92ca2612022-04-06 11:35:13 -07001823impl<'ir> ToTokens for RsTypeKind<'ir> {
1824 fn to_tokens(&self, tokens: &mut TokenStream) {
1825 self.to_token_stream().to_tokens(tokens)
1826 }
1827
1828 fn to_token_stream(&self) -> TokenStream {
1829 match self {
1830 RsTypeKind::Pointer { pointee, mutability } => {
1831 let mutability = mutability.format_for_pointer();
1832 quote! {* #mutability #pointee}
1833 }
1834 RsTypeKind::Reference { referent, mutability, lifetime } => {
1835 let mut_ = mutability.format_for_reference();
1836 let lifetime = format_lifetime_name(&lifetime.name);
1837 let reference = quote! {& #lifetime #mut_ #referent};
1838 if mutability == &Mutability::Mut && !referent.is_unpin() {
1839 // TODO(b/200067242): Add a `use rust_std::pin::Pin` to the crate, and use
1840 // `Pin`. This either requires deciding how to qualify pin at
1841 // RsTypeKind-creation time, or returning an RsSnippet from here (and not
1842 // implementing ToTokens, but instead some other interface.)
1843 quote! {rust_std::pin::Pin< #reference >}
1844 } else {
1845 reference
1846 }
1847 }
1848 RsTypeKind::RvalueReference { referent, mutability, lifetime } => {
1849 let lifetime = format_lifetime_name(&lifetime.name);
1850 // TODO(b/200067242): Add a `use ctor::RvalueReference` (etc.) to the crate.
1851 if mutability == &Mutability::Mut {
1852 quote! {ctor::RvalueReference<#lifetime, #referent>}
1853 } else {
1854 quote! {ctor::ConstRvalueReference<#lifetime, #referent>}
1855 }
1856 }
1857 RsTypeKind::FuncPtr { abi, return_type, param_types } => {
1858 let return_frag = return_type.format_as_return_type_fragment();
1859 quote! { extern #abi fn( #( #param_types ),* ) #return_frag }
1860 }
Devin Jeanpierrec0543eb2022-04-20 16:00:34 -07001861 RsTypeKind::IncompleteRecord { incomplete_record, crate_ident } => {
1862 let record_ident = make_rs_ident(&incomplete_record.cc_name);
1863 let crate_ident = crate_ident.iter();
1864 quote! {#(#crate_ident::)* #record_ident}
1865 }
Devin Jeanpierre92ca2612022-04-06 11:35:13 -07001866 RsTypeKind::Record { record, crate_ident } => {
1867 let record_ident = make_rs_ident(&record.rs_name);
1868 let crate_ident = crate_ident.iter();
1869 quote! {#(#crate_ident::)* #record_ident}
1870 }
1871 RsTypeKind::TypeAlias { type_alias, crate_ident, .. } => {
1872 let alias_ident = make_rs_ident(&type_alias.identifier.identifier);
1873 let crate_ident = crate_ident.iter();
1874 quote! {#(#crate_ident::)* #alias_ident}
1875 }
1876 RsTypeKind::Unit => quote! {()},
1877 RsTypeKind::Other { name, type_args } => {
1878 let ident = make_rs_ident(name);
1879 let generic_params = format_generic_params(type_args);
1880 quote! {#ident #generic_params}
1881 }
1882 }
1883 }
1884}
1885
Lukasz Anforowicz90bdb962022-02-14 21:07:45 +00001886struct RsTypeKindIter<'ty, 'ir> {
1887 todo: Vec<&'ty RsTypeKind<'ir>>,
1888}
1889
1890impl<'ty, 'ir> RsTypeKindIter<'ty, 'ir> {
1891 pub fn new(ty: &'ty RsTypeKind<'ir>) -> Self {
1892 Self { todo: vec![ty] }
1893 }
1894}
1895
1896impl<'ty, 'ir> Iterator for RsTypeKindIter<'ty, 'ir> {
1897 type Item = &'ty RsTypeKind<'ir>;
1898
1899 fn next(&mut self) -> Option<Self::Item> {
1900 match self.todo.pop() {
1901 None => None,
1902 Some(curr) => {
1903 match curr {
Devin Jeanpierrec0543eb2022-04-20 16:00:34 -07001904 RsTypeKind::Unit
1905 | RsTypeKind::IncompleteRecord { .. }
1906 | RsTypeKind::Record { .. } => {}
Lukasz Anforowicz90bdb962022-02-14 21:07:45 +00001907 RsTypeKind::Pointer { pointee, .. } => self.todo.push(pointee),
1908 RsTypeKind::Reference { referent, .. } => self.todo.push(referent),
Devin Jeanpierredeea7892022-03-29 02:13:31 -07001909 RsTypeKind::RvalueReference { referent, .. } => self.todo.push(referent),
Lukasz Anforowicz90bdb962022-02-14 21:07:45 +00001910 RsTypeKind::TypeAlias { underlying_type: t, .. } => self.todo.push(t),
Lukasz Anforowiczcf230fd2022-02-18 19:20:39 +00001911 RsTypeKind::FuncPtr { return_type, param_types, .. } => {
1912 self.todo.push(return_type);
1913 self.todo.extend(param_types.iter().rev());
Devin Jeanpierre1b8f4a12022-03-22 21:29:42 +00001914 }
Lukasz Anforowicz90bdb962022-02-14 21:07:45 +00001915 RsTypeKind::Other { type_args, .. } => self.todo.extend(type_args.iter().rev()),
1916 };
1917 Some(curr)
1918 }
1919 }
1920 }
Lukasz Anforowicz40c2eb82022-01-11 18:22:31 +00001921}
1922
Lukasz Anforowicz95551272022-01-20 00:02:24 +00001923fn format_lifetime_name(lifetime_name: &str) -> TokenStream {
1924 let lifetime =
1925 syn::Lifetime::new(&format!("'{}", lifetime_name), proc_macro2::Span::call_site());
1926 quote! { #lifetime }
1927}
1928
Devin Jeanpierre9886fb42022-04-01 04:31:20 -07001929fn format_rs_type(ty: &ir::RsType, ir: &IR) -> Result<TokenStream> {
Lukasz Anforowicz40c2eb82022-01-11 18:22:31 +00001930 RsTypeKind::new(ty, ir)
Devin Jeanpierre92ca2612022-04-06 11:35:13 -07001931 .map(|kind| kind.to_token_stream())
Lukasz Anforowicz40c2eb82022-01-11 18:22:31 +00001932 .with_context(|| format!("Failed to format Rust type {:?}", ty))
Devin Jeanpierre7a7328e2021-09-17 07:10:08 +00001933}
1934
Googler6a0a5252022-01-11 14:08:09 +00001935fn cc_type_name_for_item(item: &ir::Item) -> Result<TokenStream> {
Devin Jeanpierre1b8f4a12022-03-22 21:29:42 +00001936 Ok(match item {
Lukasz Anforowicz4c3a2cc2022-03-11 00:24:49 +00001937 Item::Record(record) => {
1938 let ident = format_cc_ident(&record.cc_name);
1939 quote! { class #ident }
Devin Jeanpierre1b8f4a12022-03-22 21:29:42 +00001940 }
Lukasz Anforowicz4c3a2cc2022-03-11 00:24:49 +00001941 Item::TypeAlias(type_alias) => {
1942 let ident = format_cc_ident(&type_alias.identifier.identifier);
1943 quote! { #ident }
Devin Jeanpierre1b8f4a12022-03-22 21:29:42 +00001944 }
Googler6a0a5252022-01-11 14:08:09 +00001945 _ => bail!("Item does not define a type: {:?}", item),
Lukasz Anforowicz4c3a2cc2022-03-11 00:24:49 +00001946 })
Googler6a0a5252022-01-11 14:08:09 +00001947}
1948
Lukasz Anforowicz71e9b592022-03-04 19:03:02 +00001949// Maps a Rust ABI [1] into a Clang attribute. See also
1950// `ConvertCcCallConvIntoRsApi` in importer.cc.
1951// [1]
1952// https://doc.rust-lang.org/reference/items/functions.html#extern-function-qualifier
1953fn format_cc_call_conv_as_clang_attribute(rs_abi: &str) -> Result<TokenStream> {
1954 match rs_abi {
1955 "cdecl" => Ok(quote! {}),
1956 "fastcall" => Ok(quote! { __attribute__((fastcall)) }),
1957 "stdcall" => Ok(quote! { __attribute__((stdcall)) }),
1958 "thiscall" => Ok(quote! { __attribute__((thiscall)) }),
1959 "vectorcall" => Ok(quote! { __attribute__((vectorcall)) }),
1960 _ => bail!("Unsupported ABI: {}", rs_abi),
1961 }
1962}
1963
Marcel Hlopkoc0956cf2021-11-29 08:31:28 +00001964fn format_cc_type(ty: &ir::CcType, ir: &IR) -> Result<TokenStream> {
Devin Jeanpierre09c6f452021-09-29 07:34:24 +00001965 let const_fragment = if ty.is_const {
Devin Jeanpierre184f9ac2021-09-17 13:47:03 +00001966 quote! {const}
1967 } else {
1968 quote! {}
1969 };
Marcel Hlopkoc0956cf2021-11-29 08:31:28 +00001970 if let Some(ref name) = ty.name {
1971 match name.as_str() {
1972 "*" => {
Googlerff7fc232021-12-02 09:43:00 +00001973 if ty.type_args.len() != 1 {
1974 bail!("Invalid pointer type (need exactly 1 type argument): {:?}", ty);
Marcel Hlopkoc0956cf2021-11-29 08:31:28 +00001975 }
Googlerff7fc232021-12-02 09:43:00 +00001976 assert_eq!(ty.type_args.len(), 1);
1977 let nested_type = format_cc_type(&ty.type_args[0], ir)?;
Marcel Hlopkoc0956cf2021-11-29 08:31:28 +00001978 Ok(quote! {#nested_type * #const_fragment})
Devin Jeanpierre7a7328e2021-09-17 07:10:08 +00001979 }
Lukasz Anforowicz275fa922022-01-05 16:13:20 +00001980 "&" => {
1981 if ty.type_args.len() != 1 {
1982 bail!("Invalid reference type (need exactly 1 type argument): {:?}", ty);
1983 }
1984 let nested_type = format_cc_type(&ty.type_args[0], ir)?;
1985 Ok(quote! {#nested_type &})
1986 }
Devin Jeanpierredeea7892022-03-29 02:13:31 -07001987 "&&" => {
1988 if ty.type_args.len() != 1 {
1989 bail!("Invalid rvalue reference type (need exactly 1 type argument): {:?}", ty);
1990 }
1991 let nested_type = format_cc_type(&ty.type_args[0], ir)?;
1992 Ok(quote! {#nested_type &&})
1993 }
Lukasz Anforowicz71e9b592022-03-04 19:03:02 +00001994 cc_type_name => match cc_type_name.strip_prefix("#funcValue ") {
1995 None => {
1996 if !ty.type_args.is_empty() {
1997 bail!("Type not yet supported: {:?}", ty);
1998 }
1999 let idents = cc_type_name.split_whitespace().map(format_cc_ident);
2000 Ok(quote! {#( #idents )* #const_fragment})
Devin Jeanpierre1b8f4a12022-03-22 21:29:42 +00002001 }
Lukasz Anforowicz71e9b592022-03-04 19:03:02 +00002002 Some(abi) => match ty.type_args.split_last() {
2003 None => bail!("funcValue type without a return type: {:?}", ty),
2004 Some((ret_type, param_types)) => {
2005 let ret_type = format_cc_type(ret_type, ir)?;
2006 let param_types = param_types
2007 .iter()
2008 .map(|t| format_cc_type(t, ir))
2009 .collect::<Result<Vec<_>>>()?;
2010 let attr = format_cc_call_conv_as_clang_attribute(abi)?;
2011 // `type_identity_t` is used below to avoid having to
2012 // emit spiral-like syntax where some syntax elements of
2013 // an inner type (e.g. function type as below) can
2014 // surround syntax elements of an outer type (e.g. a
2015 // pointer type). Compare: `int (*foo)(int, int)` VS
2016 // `type_identity_t<int(int, int)>* foo`.
Lukasz Anforowicz23171542022-04-11 15:26:17 -07002017 Ok(quote! { crubit::type_identity_t<
Devin Jeanpierre1b8f4a12022-03-22 21:29:42 +00002018 #ret_type ( #( #param_types ),* ) #attr
2019 > })
2020 }
2021 },
2022 },
Devin Jeanpierre7a7328e2021-09-17 07:10:08 +00002023 }
Marcel Hlopkoc0956cf2021-11-29 08:31:28 +00002024 } else {
Googler6a0a5252022-01-11 14:08:09 +00002025 let item = ir.item_for_type(ty)?;
2026 let type_name = cc_type_name_for_item(item)?;
2027 Ok(quote! {#const_fragment #type_name})
Devin Jeanpierre7a7328e2021-09-17 07:10:08 +00002028 }
2029}
2030
Marcel Hlopkoa0f38662021-12-03 08:45:26 +00002031fn cc_struct_layout_assertion(record: &Record, ir: &IR) -> TokenStream {
Googler6a0a5252022-01-11 14:08:09 +00002032 if !ir.is_current_target(&record.owning_target) && !ir.is_stdlib_target(&record.owning_target) {
Marcel Hlopkoa0f38662021-12-03 08:45:26 +00002033 return quote! {};
2034 }
Lukasz Anforowicz4c3a2cc2022-03-11 00:24:49 +00002035 let record_ident = format_cc_ident(&record.cc_name);
Googler5ea88642021-09-29 08:05:59 +00002036 let size = Literal::usize_unsuffixed(record.size);
2037 let alignment = Literal::usize_unsuffixed(record.alignment);
Lukasz Anforowicz74704712021-12-22 15:30:31 +00002038 let field_assertions =
2039 record.fields.iter().filter(|f| f.access == AccessSpecifier::Public).map(|field| {
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +00002040 let field_ident = format_cc_ident(&field.identifier.identifier);
Lukasz Anforowicz74704712021-12-22 15:30:31 +00002041 let offset = Literal::usize_unsuffixed(field.offset);
Devin Jeanpierrec0543eb2022-04-20 16:00:34 -07002042 // The IR contains the offset in bits, while `CRUBIT_OFFSET_OF` returns the
2043 // offset in bytes, so we need to convert.
Lukasz Anforowicz74704712021-12-22 15:30:31 +00002044 quote! {
Lukasz Anforowicz4ee9c222022-04-13 09:33:36 -07002045 static_assert(CRUBIT_OFFSET_OF(#field_ident, class #record_ident) * 8 == #offset);
Lukasz Anforowicz74704712021-12-22 15:30:31 +00002046 }
2047 });
Googler5ea88642021-09-29 08:05:59 +00002048 quote! {
Googler972d3582022-01-11 10:17:22 +00002049 static_assert(sizeof(class #record_ident) == #size);
2050 static_assert(alignof(class #record_ident) == #alignment);
Googler5ea88642021-09-29 08:05:59 +00002051 #( #field_assertions )*
2052 }
2053}
2054
Devin Jeanpierre58181ac2022-02-14 21:30:05 +00002055// Returns the accessor functions for no_unique_address member variables.
2056fn cc_struct_no_unique_address_impl(record: &Record, ir: &IR) -> Result<TokenStream> {
2057 let mut fields = vec![];
2058 let mut types = vec![];
2059 for field in &record.fields {
2060 if field.access != AccessSpecifier::Public || !field.is_no_unique_address {
2061 continue;
2062 }
2063 fields.push(make_rs_ident(&field.identifier.identifier));
Devin Jeanpierrefec5bfe2022-04-05 10:59:10 -07002064 types.push(format_rs_type(&field.type_.rs_type, ir).with_context(|| {
2065 format!("Failed to format type for field {:?} on record {:?}", field, record)
2066 })?)
Devin Jeanpierre58181ac2022-02-14 21:30:05 +00002067 }
2068
2069 if fields.is_empty() {
2070 return Ok(quote! {});
2071 }
2072
Lukasz Anforowicz4c3a2cc2022-03-11 00:24:49 +00002073 let ident = make_rs_ident(&record.rs_name);
Devin Jeanpierre58181ac2022-02-14 21:30:05 +00002074 Ok(quote! {
2075 impl #ident {
2076 #(
2077 pub fn #fields(&self) -> &#types {
2078 unsafe {&* (&self.#fields as *const _ as *const #types)}
2079 }
2080 )*
2081 }
2082 })
2083}
2084
Devin Jeanpierre56777022022-02-03 01:57:15 +00002085/// Returns the implementation of base class conversions, for converting a type
2086/// to its unambiguous public base classes.
2087///
2088/// TODO(b/216195042): Implement this in terms of a supporting trait which casts
2089/// raw pointers. Then, we would have blanket impls for reference, pinned mut
2090/// reference, etc. conversion. The current version is just enough to test the
2091/// logic in importer.
2092//
2093// TODO(b/216195042): Should this use, like, AsRef/AsMut (and some equivalent
2094// for Pin)?
2095fn cc_struct_upcast_impl(record: &Record, ir: &IR) -> Result<TokenStream> {
2096 let mut impls = Vec::with_capacity(record.unambiguous_public_bases.len());
2097 for base in &record.unambiguous_public_bases {
Lukasz Anforowicz44047252022-03-23 13:04:48 +00002098 let base_record: &Record = ir
2099 .find_decl(base.base_record_id)
2100 .with_context(|| format!("Can't find a base record of {:?}", record))?;
Devin Jeanpierre56777022022-02-03 01:57:15 +00002101 if let Some(offset) = base.offset {
2102 let offset = Literal::i64_unsuffixed(offset);
2103 // TODO(b/216195042): Correctly handle imported records, lifetimes.
Lukasz Anforowicz4c3a2cc2022-03-11 00:24:49 +00002104 let base_name = make_rs_ident(&base_record.rs_name);
2105 let derived_name = make_rs_ident(&record.rs_name);
Devin Jeanpierre56777022022-02-03 01:57:15 +00002106 impls.push(quote! {
2107 impl<'a> From<&'a #derived_name> for &'a #base_name {
2108 fn from(x: &'a #derived_name) -> Self {
2109 unsafe {
2110 &*((x as *const _ as *const u8).offset(#offset) as *const #base_name)
2111 }
2112 }
2113 }
2114 });
2115 } else {
2116 // TODO(b/216195042): determine offset dynamically / use a dynamic
2117 // cast. This requires a new C++ function to be
2118 // generated, so that we have something to call.
2119 }
2120 }
2121
2122 Ok(quote! {
2123 #(#impls)*
2124 })
2125}
2126
Googlera675ae02021-12-07 08:04:59 +00002127fn thunk_ident(func: &Func) -> Ident {
2128 format_ident!("__rust_thunk__{}", func.mangled_name)
Devin Jeanpierref2ec8712021-10-13 20:47:16 +00002129}
2130
Marcel Hlopko89547752021-12-10 09:39:41 +00002131fn generate_rs_api_impl(ir: &IR) -> Result<TokenStream> {
Michael Forsterbee84482021-10-13 08:35:38 +00002132 // This function uses quote! to generate C++ source code out of convenience.
2133 // This is a bold idea so we have to continously evaluate if it still makes
2134 // sense or the cost of working around differences in Rust and C++ tokens is
2135 // greather than the value added.
Marcel Hlopko3164eee2021-08-24 20:09:22 +00002136 //
Michael Forsterbee84482021-10-13 08:35:38 +00002137 // See rs_bindings_from_cc/
2138 // token_stream_printer.rs for a list of supported placeholders.
Marcel Hlopko3164eee2021-08-24 20:09:22 +00002139 let mut thunks = vec![];
Michael Forster7ef80732021-10-01 18:12:19 +00002140 for func in ir.functions() {
Lukasz Anforowiczdd9ae0f2022-02-17 15:52:53 +00002141 if can_skip_cc_thunk(func) {
Marcel Hlopko3164eee2021-08-24 20:09:22 +00002142 continue;
2143 }
2144
Googlera675ae02021-12-07 08:04:59 +00002145 let thunk_ident = thunk_ident(func);
Devin Jeanpierref2ec8712021-10-13 20:47:16 +00002146 let implementation_function = match &func.name {
Lukasz Anforowicz9c663ca2022-02-09 01:33:31 +00002147 UnqualifiedIdentifier::Operator(op) => {
2148 let name = syn::parse_str::<TokenStream>(&op.name)?;
2149 quote! { operator #name }
2150 }
Devin Jeanpierref2ec8712021-10-13 20:47:16 +00002151 UnqualifiedIdentifier::Identifier(id) => {
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +00002152 let fn_ident = format_cc_ident(&id.identifier);
Lukasz Anforowiczaab8ad22021-12-19 20:29:26 +00002153 let static_method_metadata = func
2154 .member_func_metadata
2155 .as_ref()
2156 .filter(|meta| meta.instance_method_metadata.is_none());
2157 match static_method_metadata {
2158 None => quote! {#fn_ident},
2159 Some(meta) => {
Devin Jeanpierre1b8f4a12022-03-22 21:29:42 +00002160 let record_ident = format_cc_ident(&meta.find_record(ir)?.cc_name);
Lukasz Anforowiczaab8ad22021-12-19 20:29:26 +00002161 quote! { #record_ident :: #fn_ident }
2162 }
2163 }
Devin Jeanpierref2ec8712021-10-13 20:47:16 +00002164 }
Lukasz Anforowicz7b0042d2022-01-06 23:00:19 +00002165 // Use `destroy_at` to avoid needing to spell out the class name. Destructor identiifers
Devin Jeanpierrecc6cf092021-12-16 04:31:14 +00002166 // use the name of the type itself, without namespace qualification, template
2167 // parameters, or aliases. We do not need to use that naming scheme anywhere else in
2168 // the bindings, and it can be difficult (impossible?) to spell in the general case. By
2169 // using destroy_at, we avoid needing to determine or remember what the correct spelling
Lukasz Anforowicz7b0042d2022-01-06 23:00:19 +00002170 // is. Similar arguments apply to `construct_at`.
Lukasz Anforowicze643ec92021-12-22 15:45:15 +00002171 UnqualifiedIdentifier::Constructor => {
Lukasz Anforowicz23171542022-04-11 15:26:17 -07002172 quote! { crubit::construct_at }
Lukasz Anforowicze643ec92021-12-22 15:45:15 +00002173 }
Devin Jeanpierref2ec8712021-10-13 20:47:16 +00002174 UnqualifiedIdentifier::Destructor => quote! {std::destroy_at},
Devin Jeanpierref2ec8712021-10-13 20:47:16 +00002175 };
Marcel Hlopkoc0956cf2021-11-29 08:31:28 +00002176 let return_type_name = format_cc_type(&func.return_type.cc_type, ir)?;
Lukasz Anforowicze643ec92021-12-22 15:45:15 +00002177 let return_stmt = if func.return_type.cc_type.is_void() {
2178 quote! {}
2179 } else {
2180 quote! { return }
2181 };
Marcel Hlopko3164eee2021-08-24 20:09:22 +00002182
2183 let param_idents =
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +00002184 func.params.iter().map(|p| format_cc_ident(&p.identifier.identifier)).collect_vec();
Marcel Hlopko3164eee2021-08-24 20:09:22 +00002185
Devin Jeanpierre09c6f452021-09-29 07:34:24 +00002186 let param_types = func
2187 .params
2188 .iter()
Marcel Hlopkoc0956cf2021-11-29 08:31:28 +00002189 .map(|p| format_cc_type(&p.type_.cc_type, ir))
Devin Jeanpierre09c6f452021-09-29 07:34:24 +00002190 .collect::<Result<Vec<_>>>()?;
Marcel Hlopko3164eee2021-08-24 20:09:22 +00002191
Lukasz Anforowiczb3d89aa2022-01-12 14:35:52 +00002192 let needs_this_deref = match &func.member_func_metadata {
2193 None => false,
2194 Some(meta) => match &func.name {
2195 UnqualifiedIdentifier::Constructor | UnqualifiedIdentifier::Destructor => false,
Marcel Hlopko14ee3c82022-02-09 09:46:23 +00002196 UnqualifiedIdentifier::Identifier(_) | UnqualifiedIdentifier::Operator(_) => {
2197 meta.instance_method_metadata.is_some()
2198 }
Lukasz Anforowiczb3d89aa2022-01-12 14:35:52 +00002199 },
2200 };
Devin Jeanpierredeea7892022-03-29 02:13:31 -07002201
2202 let arg_expressions: Vec<_> = param_idents
2203 .iter()
2204 .map(
2205 // Forward references along. (If the parameter is a value, not a reference, this
2206 // will create an lvalue reference, and still do the right thing.)
2207 |ident| quote! {std::forward<decltype(#ident)>(#ident)},
2208 )
2209 .collect();
Lukasz Anforowiczb3d89aa2022-01-12 14:35:52 +00002210 let (implementation_function, arg_expressions) = if !needs_this_deref {
Devin Jeanpierredeea7892022-03-29 02:13:31 -07002211 (implementation_function, arg_expressions.clone())
Lukasz Anforowiczb3d89aa2022-01-12 14:35:52 +00002212 } else {
2213 let this_param = func
2214 .params
2215 .first()
2216 .ok_or_else(|| anyhow!("Instance methods must have `__this` param."))?;
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +00002217 let this_arg = format_cc_ident(&this_param.identifier.identifier);
Lukasz Anforowiczb3d89aa2022-01-12 14:35:52 +00002218 (
2219 quote! { #this_arg -> #implementation_function},
Devin Jeanpierredeea7892022-03-29 02:13:31 -07002220 arg_expressions.iter().skip(1).cloned().collect_vec(),
Lukasz Anforowiczb3d89aa2022-01-12 14:35:52 +00002221 )
2222 };
2223
Marcel Hlopko3164eee2021-08-24 20:09:22 +00002224 thunks.push(quote! {
2225 extern "C" #return_type_name #thunk_ident( #( #param_types #param_idents ),* ) {
Lukasz Anforowiczb3d89aa2022-01-12 14:35:52 +00002226 #return_stmt #implementation_function( #( #arg_expressions ),* );
Marcel Hlopko3164eee2021-08-24 20:09:22 +00002227 }
2228 });
2229 }
2230
Marcel Hlopkoa0f38662021-12-03 08:45:26 +00002231 let layout_assertions = ir.records().map(|record| cc_struct_layout_assertion(record, ir));
Googler5ea88642021-09-29 08:05:59 +00002232
Devin Jeanpierre231ef8d2021-10-27 10:50:44 +00002233 let mut standard_headers = <BTreeSet<Ident>>::new();
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +00002234 standard_headers.insert(format_ident!("memory")); // ubiquitous.
Devin Jeanpierre231ef8d2021-10-27 10:50:44 +00002235 if ir.records().next().is_some() {
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +00002236 standard_headers.insert(format_ident!("cstddef"));
Devin Jeanpierre231ef8d2021-10-27 10:50:44 +00002237 };
Googler5ea88642021-09-29 08:05:59 +00002238
Lukasz Anforowicz4ee9c222022-04-13 09:33:36 -07002239 let mut includes = vec![
2240 "rs_bindings_from_cc/support/cxx20_backports.h",
2241 "rs_bindings_from_cc/support/offsetof.h",
2242 ];
Lukasz Anforowicz4457baf2021-12-23 17:24:04 +00002243
Michael Forsterbee84482021-10-13 08:35:38 +00002244 // In order to generate C++ thunk in all the cases Clang needs to be able to
2245 // access declarations from public headers of the C++ library.
Lukasz Anforowicz4457baf2021-12-23 17:24:04 +00002246 includes.extend(ir.used_headers().map(|i| &i.name as &str));
Marcel Hlopko3164eee2021-08-24 20:09:22 +00002247
Marcel Hlopko89547752021-12-10 09:39:41 +00002248 Ok(quote! {
Googler5ea88642021-09-29 08:05:59 +00002249 #( __HASH_TOKEN__ include <#standard_headers> __NEWLINE__)*
Devin Jeanpierre7c74f842022-02-03 07:08:06 +00002250 __NEWLINE__
Michael Forsterdb8101a2021-10-08 06:56:03 +00002251 #( __HASH_TOKEN__ include #includes __NEWLINE__)* __NEWLINE__
Marcel Hlopkob8069ae2022-02-19 09:31:00 +00002252 __HASH_TOKEN__ pragma clang diagnostic push __NEWLINE__
2253 // Disable Clang thread-safety-analysis warnings that would otherwise
2254 // complain about thunks that call mutex locking functions in an unpaired way.
2255 __HASH_TOKEN__ pragma clang diagnostic ignored "-Wthread-safety-analysis" __NEWLINE__
Marcel Hlopko3164eee2021-08-24 20:09:22 +00002256
Michael Forsterdb8101a2021-10-08 06:56:03 +00002257 #( #thunks )* __NEWLINE__ __NEWLINE__
Googler5ea88642021-09-29 08:05:59 +00002258
Michael Forsterdb8101a2021-10-08 06:56:03 +00002259 #( #layout_assertions __NEWLINE__ __NEWLINE__ )*
Marcel Hlopkoc6b726c2021-10-07 06:53:09 +00002260
Marcel Hlopkob8069ae2022-02-19 09:31:00 +00002261 __NEWLINE__
2262 __HASH_TOKEN__ pragma clang diagnostic pop __NEWLINE__
Marcel Hlopkoc6b726c2021-10-07 06:53:09 +00002263 // To satisfy http://cs/symbol:devtools.metadata.Presubmit.CheckTerminatingNewline check.
2264 __NEWLINE__
Marcel Hlopko89547752021-12-10 09:39:41 +00002265 })
Marcel Hlopko3164eee2021-08-24 20:09:22 +00002266}
2267
Marcel Hlopko42abfc82021-08-09 07:03:17 +00002268#[cfg(test)]
2269mod tests {
Devin Jeanpierre45cb1162021-10-27 10:54:28 +00002270 use super::*;
Michael Forstered642022021-10-04 09:48:25 +00002271 use anyhow::anyhow;
Dmitri Gribenko67cbfd22022-03-24 13:39:34 +00002272 use ir_testing::{ir_from_cc, ir_from_cc_dependency, ir_record, retrieve_func};
Lukasz Anforowicz282bfd72022-03-22 22:38:17 +00002273 use static_assertions::{assert_impl_all, assert_not_impl_all};
Marcel Hlopko89547752021-12-10 09:39:41 +00002274 use token_stream_matchers::{
Lukasz Anforowicz71e9b592022-03-04 19:03:02 +00002275 assert_cc_matches, assert_cc_not_matches, assert_ir_matches, assert_rs_matches,
2276 assert_rs_not_matches,
Devin Jeanpierre7e9a1de2021-12-03 08:04:22 +00002277 };
Michael Forsterdb8101a2021-10-08 06:56:03 +00002278 use token_stream_printer::tokens_to_string;
Marcel Hlopko42abfc82021-08-09 07:03:17 +00002279
2280 #[test]
Marcel Hlopkob8069ae2022-02-19 09:31:00 +00002281 fn test_disable_thread_safety_warnings() -> Result<()> {
2282 let ir = ir_from_cc("inline void foo() {}")?;
2283 let rs_api_impl = generate_rs_api_impl(&ir)?;
2284 assert_cc_matches!(
2285 rs_api_impl,
2286 quote! {
2287 ...
2288 __HASH_TOKEN__ pragma clang diagnostic push
2289 __HASH_TOKEN__ pragma clang diagnostic ignored "-Wthread-safety-analysis"
2290 ...
2291
2292 __HASH_TOKEN__ pragma clang diagnostic pop
2293 ...
2294 }
2295 );
2296 Ok(())
2297 }
2298
2299 #[test]
Marcel Hlopko3b9bf9e2021-11-29 08:25:14 +00002300 // TODO(hlopko): Move this test to a more principled place where it can access
2301 // `ir_testing`.
2302 fn test_duplicate_decl_ids_err() {
2303 let mut r1 = ir_record("R1");
Rosica Dejanovskad638cf52022-03-23 15:45:01 +00002304 r1.id = ItemId(42);
Marcel Hlopko3b9bf9e2021-11-29 08:25:14 +00002305 let mut r2 = ir_record("R2");
Rosica Dejanovskad638cf52022-03-23 15:45:01 +00002306 r2.id = ItemId(42);
Marcel Hlopko3b9bf9e2021-11-29 08:25:14 +00002307 let result = make_ir_from_items([r1.into(), r2.into()]);
2308 assert!(result.is_err());
2309 assert!(result.unwrap_err().to_string().contains("Duplicate decl_id found in"));
2310 }
2311
2312 #[test]
Marcel Hlopko45fba972021-08-23 19:52:20 +00002313 fn test_simple_function() -> Result<()> {
Marcel Hlopko89547752021-12-10 09:39:41 +00002314 let ir = ir_from_cc("int Add(int a, int b);")?;
2315 let rs_api = generate_rs_api(&ir)?;
2316 assert_rs_matches!(
2317 rs_api,
2318 quote! {
Michael Forsterdb8101a2021-10-08 06:56:03 +00002319 #[inline(always)]
Marcel Hlopko89547752021-12-10 09:39:41 +00002320 pub fn Add(a: i32, b: i32) -> i32 {
Rosica Dejanovskadd9a9032022-04-12 07:34:41 -07002321 unsafe { detail::__rust_thunk___Z3Addii(a, b) }
Marcel Hlopko89547752021-12-10 09:39:41 +00002322 }
2323 }
2324 );
2325 assert_rs_matches!(
2326 rs_api,
2327 quote! {
Michael Forsterdb8101a2021-10-08 06:56:03 +00002328 mod detail {
Googler55647142022-01-11 12:37:39 +00002329 #[allow(unused_imports)]
Devin Jeanpierred4dde0e2021-10-13 20:48:25 +00002330 use super::*;
Michael Forsterdb8101a2021-10-08 06:56:03 +00002331 extern "C" {
2332 #[link_name = "_Z3Addii"]
Googlera675ae02021-12-07 08:04:59 +00002333 pub(crate) fn __rust_thunk___Z3Addii(a: i32, b: i32) -> i32;
Marcel Hlopko89547752021-12-10 09:39:41 +00002334 }
2335 }
2336 }
Marcel Hlopko42abfc82021-08-09 07:03:17 +00002337 );
Michael Forsterdb8101a2021-10-08 06:56:03 +00002338
Marcel Hlopko89547752021-12-10 09:39:41 +00002339 assert_cc_not_matches!(generate_rs_api_impl(&ir)?, quote! {__rust_thunk___Z3Addii});
Michael Forsterdb8101a2021-10-08 06:56:03 +00002340
Marcel Hlopko3164eee2021-08-24 20:09:22 +00002341 Ok(())
2342 }
2343
2344 #[test]
2345 fn test_inline_function() -> Result<()> {
Marcel Hlopko89547752021-12-10 09:39:41 +00002346 let ir = ir_from_cc("inline int Add(int a, int b);")?;
2347 let rs_api = generate_rs_api(&ir)?;
2348 assert_rs_matches!(
2349 rs_api,
2350 quote! {
Michael Forsterdb8101a2021-10-08 06:56:03 +00002351 #[inline(always)]
Marcel Hlopko89547752021-12-10 09:39:41 +00002352 pub fn Add(a: i32, b: i32) -> i32 {
Rosica Dejanovskadd9a9032022-04-12 07:34:41 -07002353 unsafe { detail::__rust_thunk___Z3Addii(a, b) }
Marcel Hlopko89547752021-12-10 09:39:41 +00002354 }
2355 }
2356 );
2357 assert_rs_matches!(
2358 rs_api,
2359 quote! {
Michael Forsterdb8101a2021-10-08 06:56:03 +00002360 mod detail {
Googler55647142022-01-11 12:37:39 +00002361 #[allow(unused_imports)]
Devin Jeanpierred4dde0e2021-10-13 20:48:25 +00002362 use super::*;
Michael Forsterdb8101a2021-10-08 06:56:03 +00002363 extern "C" {
Googlera675ae02021-12-07 08:04:59 +00002364 pub(crate) fn __rust_thunk___Z3Addii(a: i32, b: i32) -> i32;
Marcel Hlopko89547752021-12-10 09:39:41 +00002365 }
2366 }
2367 }
Marcel Hlopko3164eee2021-08-24 20:09:22 +00002368 );
2369
Marcel Hlopko89547752021-12-10 09:39:41 +00002370 assert_cc_matches!(
2371 generate_rs_api_impl(&ir)?,
2372 quote! {
Googlera675ae02021-12-07 08:04:59 +00002373 extern "C" int __rust_thunk___Z3Addii(int a, int b) {
Devin Jeanpierredeea7892022-03-29 02:13:31 -07002374 return Add(std::forward<decltype(a)>(a), std::forward<decltype(b)>(b));
Marcel Hlopko3164eee2021-08-24 20:09:22 +00002375 }
Marcel Hlopko89547752021-12-10 09:39:41 +00002376 }
Marcel Hlopko3164eee2021-08-24 20:09:22 +00002377 );
Marcel Hlopko42abfc82021-08-09 07:03:17 +00002378 Ok(())
2379 }
Marcel Hlopkob4b28742021-09-15 12:45:20 +00002380
2381 #[test]
Marcel Hlopkoa0f38662021-12-03 08:45:26 +00002382 fn test_simple_function_with_types_from_other_target() -> Result<()> {
2383 let ir = ir_from_cc_dependency(
2384 "inline ReturnStruct DoSomething(ParamStruct param);",
2385 "struct ReturnStruct {}; struct ParamStruct {};",
2386 )?;
2387
Marcel Hlopko89547752021-12-10 09:39:41 +00002388 let rs_api = generate_rs_api(&ir)?;
2389 assert_rs_matches!(
2390 rs_api,
2391 quote! {
Marcel Hlopkoa0f38662021-12-03 08:45:26 +00002392 #[inline(always)]
2393 pub fn DoSomething(param: dependency::ParamStruct)
Marcel Hlopko89547752021-12-10 09:39:41 +00002394 -> dependency::ReturnStruct {
Rosica Dejanovskadd9a9032022-04-12 07:34:41 -07002395 unsafe { detail::__rust_thunk___Z11DoSomething11ParamStruct(param) }
Marcel Hlopko89547752021-12-10 09:39:41 +00002396 }
2397 }
2398 );
2399 assert_rs_matches!(
2400 rs_api,
2401 quote! {
2402 mod detail {
Googler55647142022-01-11 12:37:39 +00002403 #[allow(unused_imports)]
Marcel Hlopko89547752021-12-10 09:39:41 +00002404 use super::*;
2405 extern "C" {
2406 pub(crate) fn __rust_thunk___Z11DoSomething11ParamStruct(param: dependency::ParamStruct)
2407 -> dependency::ReturnStruct;
2408 }
2409 }}
Marcel Hlopkoa0f38662021-12-03 08:45:26 +00002410 );
2411
Marcel Hlopko89547752021-12-10 09:39:41 +00002412 assert_cc_matches!(
2413 generate_rs_api_impl(&ir)?,
2414 quote! {
Googler972d3582022-01-11 10:17:22 +00002415 extern "C" class ReturnStruct __rust_thunk___Z11DoSomething11ParamStruct(class ParamStruct param) {
Devin Jeanpierredeea7892022-03-29 02:13:31 -07002416 return DoSomething(std::forward<decltype(param)>(param));
Marcel Hlopkoa0f38662021-12-03 08:45:26 +00002417 }
Marcel Hlopko89547752021-12-10 09:39:41 +00002418 }
Marcel Hlopkoa0f38662021-12-03 08:45:26 +00002419 );
2420 Ok(())
2421 }
2422
2423 #[test]
Marcel Hlopkob4b28742021-09-15 12:45:20 +00002424 fn test_simple_struct() -> Result<()> {
Marcel Hlopko89547752021-12-10 09:39:41 +00002425 let ir = ir_from_cc(&tokens_to_string(quote! {
Devin Jeanpierre88343c72022-01-15 01:10:23 +00002426 struct SomeStruct final {
Marcel Hlopko89547752021-12-10 09:39:41 +00002427 int public_int;
2428 protected:
2429 int protected_int;
2430 private:
2431 int private_int;
2432 };
2433 })?)?;
Michael Forster028800b2021-10-05 12:39:59 +00002434
Marcel Hlopko89547752021-12-10 09:39:41 +00002435 let rs_api = generate_rs_api(&ir)?;
2436 assert_rs_matches!(
2437 rs_api,
2438 quote! {
Michael Forsterdb8101a2021-10-08 06:56:03 +00002439 #[derive(Clone, Copy)]
2440 #[repr(C)]
2441 pub struct SomeStruct {
2442 pub public_int: i32,
2443 protected_int: i32,
2444 private_int: i32,
Marcel Hlopko89547752021-12-10 09:39:41 +00002445 }
2446 }
2447 );
2448 assert_rs_matches!(
2449 rs_api,
2450 quote! {
Rosica Dejanovskaaccf00b2022-04-01 13:28:04 -07002451 const _: () = assert!(rust_std::mem::size_of::<Option<&i32>>() == rust_std::mem::size_of::<&i32>());
2452 const _: () = assert!(rust_std::mem::size_of::<SomeStruct>() == 12usize);
2453 const _: () = assert!(rust_std::mem::align_of::<SomeStruct>() == 4usize);
Lukasz Anforowicz768bba32022-04-11 14:06:13 -07002454 const _: () = { static_assertions::assert_impl_all!(SomeStruct: Clone); };
2455 const _: () = { static_assertions::assert_impl_all!(SomeStruct: Copy); };
2456 const _: () = { static_assertions::assert_not_impl_all!(SomeStruct: Drop); };
Googler209b10a2021-12-06 09:11:57 +00002457 const _: () = assert!(offset_of!(SomeStruct, public_int) * 8 == 0usize);
2458 const _: () = assert!(offset_of!(SomeStruct, protected_int) * 8 == 32usize);
2459 const _: () = assert!(offset_of!(SomeStruct, private_int) * 8 == 64usize);
Marcel Hlopko89547752021-12-10 09:39:41 +00002460 }
Marcel Hlopkob4b28742021-09-15 12:45:20 +00002461 );
Marcel Hlopko89547752021-12-10 09:39:41 +00002462 let rs_api_impl = generate_rs_api_impl(&ir)?;
2463 assert_cc_matches!(
2464 rs_api_impl,
2465 quote! {
Googler972d3582022-01-11 10:17:22 +00002466 extern "C" void __rust_thunk___ZN10SomeStructD1Ev(class SomeStruct * __this) {
Devin Jeanpierredeea7892022-03-29 02:13:31 -07002467 std :: destroy_at (std::forward<decltype(__this)>(__this)) ;
Marcel Hlopko89547752021-12-10 09:39:41 +00002468 }
2469 }
2470 );
2471 assert_cc_matches!(
2472 rs_api_impl,
2473 quote! {
Googler972d3582022-01-11 10:17:22 +00002474 static_assert(sizeof(class SomeStruct) == 12);
2475 static_assert(alignof(class SomeStruct) == 4);
Lukasz Anforowicz4ee9c222022-04-13 09:33:36 -07002476 static_assert(CRUBIT_OFFSET_OF(public_int, class SomeStruct) * 8 == 0);
Marcel Hlopko89547752021-12-10 09:39:41 +00002477 }
Googler5ea88642021-09-29 08:05:59 +00002478 );
Marcel Hlopkob4b28742021-09-15 12:45:20 +00002479 Ok(())
2480 }
Devin Jeanpierre7a7328e2021-09-17 07:10:08 +00002481
Devin Jeanpierre2ed14ec2021-10-06 11:32:19 +00002482 #[test]
Lukasz Anforowicz275fa922022-01-05 16:13:20 +00002483 fn test_ref_to_struct_in_thunk_impls() -> Result<()> {
Googler972d3582022-01-11 10:17:22 +00002484 let ir = ir_from_cc("struct S{}; inline void foo(class S& s) {} ")?;
Lukasz Anforowicz275fa922022-01-05 16:13:20 +00002485 let rs_api_impl = generate_rs_api_impl(&ir)?;
2486 assert_cc_matches!(
2487 rs_api_impl,
2488 quote! {
Googler972d3582022-01-11 10:17:22 +00002489 extern "C" void __rust_thunk___Z3fooR1S(class S& s) {
Devin Jeanpierredeea7892022-03-29 02:13:31 -07002490 foo(std::forward<decltype(s)>(s));
Lukasz Anforowicz275fa922022-01-05 16:13:20 +00002491 }
2492 }
2493 );
2494 Ok(())
2495 }
2496
2497 #[test]
2498 fn test_const_ref_to_struct_in_thunk_impls() -> Result<()> {
Googler972d3582022-01-11 10:17:22 +00002499 let ir = ir_from_cc("struct S{}; inline void foo(const class S& s) {} ")?;
Lukasz Anforowicz275fa922022-01-05 16:13:20 +00002500 let rs_api_impl = generate_rs_api_impl(&ir)?;
2501 assert_cc_matches!(
2502 rs_api_impl,
2503 quote! {
Googler972d3582022-01-11 10:17:22 +00002504 extern "C" void __rust_thunk___Z3fooRK1S(const class S& s) {
Devin Jeanpierredeea7892022-03-29 02:13:31 -07002505 foo(std::forward<decltype(s)>(s));
Lukasz Anforowicz275fa922022-01-05 16:13:20 +00002506 }
2507 }
2508 );
2509 Ok(())
2510 }
2511
2512 #[test]
Lukasz Anforowicz957cbf22022-01-05 16:14:05 +00002513 fn test_unsigned_int_in_thunk_impls() -> Result<()> {
2514 let ir = ir_from_cc("inline void foo(unsigned int i) {} ")?;
2515 let rs_api_impl = generate_rs_api_impl(&ir)?;
2516 assert_cc_matches!(
2517 rs_api_impl,
2518 quote! {
2519 extern "C" void __rust_thunk___Z3fooj(unsigned int i) {
Devin Jeanpierredeea7892022-03-29 02:13:31 -07002520 foo(std::forward<decltype(i)>(i));
Lukasz Anforowicz957cbf22022-01-05 16:14:05 +00002521 }
2522 }
2523 );
2524 Ok(())
2525 }
2526
2527 #[test]
Marcel Hlopkodd1fcb12021-12-22 14:13:59 +00002528 fn test_record_static_methods_qualify_call_in_thunk() -> Result<()> {
2529 let ir = ir_from_cc(&tokens_to_string(quote! {
2530 struct SomeStruct {
2531 static inline int some_func() { return 42; }
2532 };
2533 })?)?;
2534
2535 assert_cc_matches!(
2536 generate_rs_api_impl(&ir)?,
2537 quote! {
2538 extern "C" int __rust_thunk___ZN10SomeStruct9some_funcEv() {
2539 return SomeStruct::some_func();
2540 }
2541 }
2542 );
2543 Ok(())
2544 }
2545
2546 #[test]
Lukasz Anforowiczb3d89aa2022-01-12 14:35:52 +00002547 fn test_record_instance_methods_deref_this_in_thunk() -> Result<()> {
2548 let ir = ir_from_cc(&tokens_to_string(quote! {
2549 struct SomeStruct {
2550 inline int some_func(int arg) const { return 42 + arg; }
2551 };
2552 })?)?;
2553
2554 assert_cc_matches!(
2555 generate_rs_api_impl(&ir)?,
2556 quote! {
2557 extern "C" int __rust_thunk___ZNK10SomeStruct9some_funcEi(
2558 const class SomeStruct* __this, int arg) {
Devin Jeanpierredeea7892022-03-29 02:13:31 -07002559 return __this->some_func(std::forward<decltype(arg)>(arg));
Lukasz Anforowiczb3d89aa2022-01-12 14:35:52 +00002560 }
2561 }
2562 );
2563 Ok(())
2564 }
2565
2566 #[test]
Marcel Hlopkoa0f38662021-12-03 08:45:26 +00002567 fn test_struct_from_other_target() -> Result<()> {
2568 let ir = ir_from_cc_dependency("// intentionally empty", "struct SomeStruct {};")?;
Marcel Hlopko89547752021-12-10 09:39:41 +00002569 assert_rs_not_matches!(generate_rs_api(&ir)?, quote! { SomeStruct });
2570 assert_cc_not_matches!(generate_rs_api_impl(&ir)?, quote! { SomeStruct });
Marcel Hlopkoa0f38662021-12-03 08:45:26 +00002571 Ok(())
2572 }
2573
2574 #[test]
Devin Jeanpierre2ed14ec2021-10-06 11:32:19 +00002575 fn test_copy_derives() {
Devin Jeanpierreccfefc82021-10-27 10:54:00 +00002576 let record = ir_record("S");
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00002577 assert_eq!(generate_derives(&record), &["Clone", "Copy"]);
Devin Jeanpierre2ed14ec2021-10-06 11:32:19 +00002578 }
2579
2580 #[test]
2581 fn test_copy_derives_not_is_trivial_abi() {
Devin Jeanpierreccfefc82021-10-27 10:54:00 +00002582 let mut record = ir_record("S");
Devin Jeanpierre2ed14ec2021-10-06 11:32:19 +00002583 record.is_trivial_abi = false;
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00002584 assert_eq!(generate_derives(&record), &[""; 0]);
Devin Jeanpierre2ed14ec2021-10-06 11:32:19 +00002585 }
2586
Devin Jeanpierre88343c72022-01-15 01:10:23 +00002587 /// Even if it's trivially relocatable, !Unpin C++ type cannot be
2588 /// cloned/copied or otherwise used by value, because values would allow
2589 /// assignment into the Pin.
2590 ///
2591 /// All !Unpin C++ types, not just non trivially relocatable ones, are
2592 /// unsafe to assign in the Rust sense.
Devin Jeanpierree6e16652021-12-22 15:54:46 +00002593 #[test]
Devin Jeanpierre88343c72022-01-15 01:10:23 +00002594 fn test_copy_derives_not_final() {
Devin Jeanpierree6e16652021-12-22 15:54:46 +00002595 let mut record = ir_record("S");
Teddy Katzd2cd1422022-04-04 09:41:33 -07002596 record.is_inheritable = true;
Devin Jeanpierre88343c72022-01-15 01:10:23 +00002597 assert_eq!(generate_derives(&record), &[""; 0]);
Devin Jeanpierree6e16652021-12-22 15:54:46 +00002598 }
2599
Devin Jeanpierre2ed14ec2021-10-06 11:32:19 +00002600 #[test]
2601 fn test_copy_derives_ctor_nonpublic() {
Devin Jeanpierreccfefc82021-10-27 10:54:00 +00002602 let mut record = ir_record("S");
Devin Jeanpierre2ed14ec2021-10-06 11:32:19 +00002603 for access in [ir::AccessSpecifier::Protected, ir::AccessSpecifier::Private] {
2604 record.copy_constructor.access = access;
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00002605 assert_eq!(generate_derives(&record), &[""; 0]);
Devin Jeanpierre2ed14ec2021-10-06 11:32:19 +00002606 }
2607 }
2608
2609 #[test]
2610 fn test_copy_derives_ctor_deleted() {
Devin Jeanpierreccfefc82021-10-27 10:54:00 +00002611 let mut record = ir_record("S");
Devin Jeanpierre2ed14ec2021-10-06 11:32:19 +00002612 record.copy_constructor.definition = ir::SpecialMemberDefinition::Deleted;
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00002613 assert_eq!(generate_derives(&record), &[""; 0]);
Devin Jeanpierre2ed14ec2021-10-06 11:32:19 +00002614 }
2615
2616 #[test]
Devin Jeanpierrebe2f33b2021-10-21 12:54:19 +00002617 fn test_copy_derives_ctor_nontrivial_members() {
Devin Jeanpierreccfefc82021-10-27 10:54:00 +00002618 let mut record = ir_record("S");
Devin Jeanpierrebe2f33b2021-10-21 12:54:19 +00002619 record.copy_constructor.definition = ir::SpecialMemberDefinition::NontrivialMembers;
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00002620 assert_eq!(generate_derives(&record), &[""; 0]);
Devin Jeanpierrebe2f33b2021-10-21 12:54:19 +00002621 }
2622
2623 #[test]
2624 fn test_copy_derives_ctor_nontrivial_self() {
Devin Jeanpierreccfefc82021-10-27 10:54:00 +00002625 let mut record = ir_record("S");
Devin Jeanpierre7b62e952021-12-08 21:43:30 +00002626 record.copy_constructor.definition = ir::SpecialMemberDefinition::NontrivialUserDefined;
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00002627 assert_eq!(generate_derives(&record), &[""; 0]);
Devin Jeanpierre2ed14ec2021-10-06 11:32:19 +00002628 }
2629
Devin Jeanpierre7a7328e2021-09-17 07:10:08 +00002630 #[test]
2631 fn test_ptr_func() -> Result<()> {
Marcel Hlopko89547752021-12-10 09:39:41 +00002632 let ir = ir_from_cc(&tokens_to_string(quote! {
2633 inline int* Deref(int*const* p);
2634 })?)?;
Devin Jeanpierred6da7002021-10-21 12:55:20 +00002635
Marcel Hlopko89547752021-12-10 09:39:41 +00002636 let rs_api = generate_rs_api(&ir)?;
2637 assert_rs_matches!(
2638 rs_api,
2639 quote! {
Michael Forsterdb8101a2021-10-08 06:56:03 +00002640 #[inline(always)]
Lukasz Anforowiczf7bdd392022-01-21 00:33:39 +00002641 pub unsafe fn Deref(p: *const *mut i32) -> *mut i32 {
Rosica Dejanovskadd9a9032022-04-12 07:34:41 -07002642 detail::__rust_thunk___Z5DerefPKPi(p)
Marcel Hlopko89547752021-12-10 09:39:41 +00002643 }
2644 }
2645 );
2646 assert_rs_matches!(
2647 rs_api,
2648 quote! {
Michael Forsterdb8101a2021-10-08 06:56:03 +00002649 mod detail {
Googler55647142022-01-11 12:37:39 +00002650 #[allow(unused_imports)]
Devin Jeanpierred4dde0e2021-10-13 20:48:25 +00002651 use super::*;
Michael Forsterdb8101a2021-10-08 06:56:03 +00002652 extern "C" {
Googlera675ae02021-12-07 08:04:59 +00002653 pub(crate) fn __rust_thunk___Z5DerefPKPi(p: *const *mut i32) -> *mut i32;
Marcel Hlopko89547752021-12-10 09:39:41 +00002654 }
2655 }
2656 }
Devin Jeanpierre7a7328e2021-09-17 07:10:08 +00002657 );
Devin Jeanpierre184f9ac2021-09-17 13:47:03 +00002658
Marcel Hlopko89547752021-12-10 09:39:41 +00002659 assert_cc_matches!(
2660 generate_rs_api_impl(&ir)?,
2661 quote! {
Googlera675ae02021-12-07 08:04:59 +00002662 extern "C" int* __rust_thunk___Z5DerefPKPi(int* const * p) {
Devin Jeanpierredeea7892022-03-29 02:13:31 -07002663 return Deref(std::forward<decltype(p)>(p));
Devin Jeanpierre184f9ac2021-09-17 13:47:03 +00002664 }
Marcel Hlopko89547752021-12-10 09:39:41 +00002665 }
Devin Jeanpierre184f9ac2021-09-17 13:47:03 +00002666 );
Devin Jeanpierre7a7328e2021-09-17 07:10:08 +00002667 Ok(())
2668 }
Michael Forstered642022021-10-04 09:48:25 +00002669
2670 #[test]
Googlerdb111532022-01-05 06:12:13 +00002671 fn test_const_char_ptr_func() -> Result<()> {
2672 // This is a regression test: We used to include the "const" in the name
2673 // of the CcType, which caused a panic in the code generator
2674 // ('"const char" is not a valid Ident').
2675 // It's therefore important that f() is inline so that we need to
2676 // generate a thunk for it (where we then process the CcType).
2677 let ir = ir_from_cc(&tokens_to_string(quote! {
2678 inline void f(const char *str);
2679 })?)?;
2680
2681 let rs_api = generate_rs_api(&ir)?;
2682 assert_rs_matches!(
2683 rs_api,
2684 quote! {
2685 #[inline(always)]
Lukasz Anforowiczf7bdd392022-01-21 00:33:39 +00002686 pub unsafe fn f(str: *const i8) {
Rosica Dejanovskadd9a9032022-04-12 07:34:41 -07002687 detail::__rust_thunk___Z1fPKc(str)
Googlerdb111532022-01-05 06:12:13 +00002688 }
2689 }
2690 );
2691 assert_rs_matches!(
2692 rs_api,
2693 quote! {
2694 extern "C" {
2695 pub(crate) fn __rust_thunk___Z1fPKc(str: *const i8);
2696 }
2697 }
2698 );
2699
2700 assert_cc_matches!(
2701 generate_rs_api_impl(&ir)?,
2702 quote! {
Devin Jeanpierredeea7892022-03-29 02:13:31 -07002703 extern "C" void __rust_thunk___Z1fPKc(char const * str){ f(std::forward<decltype(str)>(str)) ; }
Googlerdb111532022-01-05 06:12:13 +00002704 }
2705 );
2706 Ok(())
2707 }
2708
2709 #[test]
Lukasz Anforowiczcf230fd2022-02-18 19:20:39 +00002710 fn test_func_ptr_where_params_are_primitive_types() -> Result<()> {
2711 let ir = ir_from_cc(r#" int (*get_ptr_to_func())(float, double); "#)?;
2712 let rs_api = generate_rs_api(&ir)?;
2713 let rs_api_impl = generate_rs_api_impl(&ir)?;
2714 assert_rs_matches!(
2715 rs_api,
2716 quote! {
2717 #[inline(always)]
2718 pub fn get_ptr_to_func() -> Option<extern "C" fn (f32, f64) -> i32> {
Rosica Dejanovskadd9a9032022-04-12 07:34:41 -07002719 unsafe { detail::__rust_thunk___Z15get_ptr_to_funcv() }
Lukasz Anforowiczcf230fd2022-02-18 19:20:39 +00002720 }
2721 }
2722 );
2723 assert_rs_matches!(
2724 rs_api,
2725 quote! {
2726 mod detail {
2727 #[allow(unused_imports)]
2728 use super::*;
2729 extern "C" {
2730 #[link_name = "_Z15get_ptr_to_funcv"]
2731 pub(crate) fn __rust_thunk___Z15get_ptr_to_funcv()
2732 -> Option<extern "C" fn(f32, f64) -> i32>;
2733 }
2734 }
2735 }
2736 );
2737 // Verify that no C++ thunk got generated.
2738 assert_cc_not_matches!(rs_api_impl, quote! { __rust_thunk___Z15get_ptr_to_funcv });
2739
2740 // TODO(b/217419782): Add another test for more exotic calling conventions /
2741 // abis.
2742
2743 // TODO(b/217419782): Add another test for pointer to a function that
2744 // takes/returns non-trivially-movable types by value. See also
2745 // <internal link>
2746
2747 Ok(())
2748 }
2749
2750 #[test]
Lukasz Anforowicz92c81c32022-03-04 19:03:56 +00002751 fn test_func_ref() -> Result<()> {
2752 let ir = ir_from_cc(r#" int (&get_ref_to_func())(float, double); "#)?;
2753 let rs_api = generate_rs_api(&ir)?;
2754 assert_rs_matches!(
2755 rs_api,
2756 quote! {
2757 #[inline(always)]
2758 pub fn get_ref_to_func() -> extern "C" fn (f32, f64) -> i32 {
Rosica Dejanovskadd9a9032022-04-12 07:34:41 -07002759 unsafe { detail::__rust_thunk___Z15get_ref_to_funcv() }
Lukasz Anforowicz92c81c32022-03-04 19:03:56 +00002760 }
2761 }
2762 );
2763 Ok(())
2764 }
2765
2766 #[test]
Lukasz Anforowiczcf230fd2022-02-18 19:20:39 +00002767 fn test_func_ptr_with_non_static_lifetime() -> Result<()> {
2768 let ir = ir_from_cc(
2769 r#"
Googler53f65942022-02-23 11:23:30 +00002770 [[clang::annotate("lifetimes", "-> a")]]
Lukasz Anforowiczcf230fd2022-02-18 19:20:39 +00002771 int (*get_ptr_to_func())(float, double); "#,
2772 )?;
2773 let rs_api = generate_rs_api(&ir)?;
2774 assert_rs_matches!(
2775 rs_api,
2776 quote! {
2777 // Error while generating bindings for item 'get_ptr_to_func':
2778 // Return type is not supported: Function pointers with non-'static lifetimes are not supported: int (*)(float, double)
2779 }
2780 );
2781 Ok(())
2782 }
2783
2784 #[test]
2785 fn test_func_ptr_where_params_are_raw_ptrs() -> Result<()> {
2786 let ir = ir_from_cc(r#" const int* (*get_ptr_to_func())(const int*); "#)?;
2787 let rs_api = generate_rs_api(&ir)?;
2788 let rs_api_impl = generate_rs_api_impl(&ir)?;
2789 assert_rs_matches!(
2790 rs_api,
2791 quote! {
2792 #[inline(always)]
2793 pub fn get_ptr_to_func() -> Option<extern "C" fn (*const i32) -> *const i32> {
Rosica Dejanovskadd9a9032022-04-12 07:34:41 -07002794 unsafe { detail::__rust_thunk___Z15get_ptr_to_funcv() }
Lukasz Anforowiczcf230fd2022-02-18 19:20:39 +00002795 }
2796 }
2797 );
2798 assert_rs_matches!(
2799 rs_api,
2800 quote! {
2801 mod detail {
2802 #[allow(unused_imports)]
2803 use super::*;
2804 extern "C" {
2805 #[link_name = "_Z15get_ptr_to_funcv"]
2806 pub(crate) fn __rust_thunk___Z15get_ptr_to_funcv()
2807 -> Option<extern "C" fn(*const i32) -> *const i32>;
2808 }
2809 }
2810 }
2811 );
2812 // Verify that no C++ thunk got generated.
2813 assert_cc_not_matches!(rs_api_impl, quote! { __rust_thunk___Z15get_ptr_to_funcv });
2814
2815 // TODO(b/217419782): Add another test where params (and the return
2816 // type) are references with lifetimes. Something like this:
2817 // #pragma clang lifetime_elision
2818 // const int& (*get_ptr_to_func())(const int&, const int&); "#)?;
2819 // 1) Need to investigate why this fails - seeing raw pointers in Rust
2820 // seems to indicate that no lifetimes are present at the `importer.cc`
2821 // level. Maybe lifetime elision doesn't support this scenario? Unclear
Googler53f65942022-02-23 11:23:30 +00002822 // how to explicitly apply [[clang::annotate("lifetimes", "a, b -> a")]]
Lukasz Anforowiczcf230fd2022-02-18 19:20:39 +00002823 // to the _inner_ function.
2824 // 2) It is important to have 2 reference parameters, so see if the problem
2825 // of passing `lifetimes` by value would have been caught - see:
2826 // cl/428079010/depot/rs_bindings_from_cc/
2827 // importer.cc?version=s6#823
2828
2829 // TODO(b/217419782): Decide what to do if the C++ pointer is *not*
2830 // annotated with a lifetime - emit `unsafe fn(...) -> ...` in that
2831 // case?
2832
2833 Ok(())
2834 }
2835
2836 #[test]
Lukasz Anforowicz71e9b592022-03-04 19:03:02 +00002837 fn test_func_ptr_with_custom_abi() -> Result<()> {
2838 let ir = ir_from_cc(r#" int (*get_ptr_to_func())(float, double) [[clang::vectorcall]]; "#)?;
2839
2840 // Verify that the test input correctly represents what we intend to
2841 // test - we want [[clang::vectorcall]] to apply to the returned
2842 // function pointer, but *not* apply to the `get_ptr_to_func` function.
Lukasz Anforowicz71e9b592022-03-04 19:03:02 +00002843 assert_ir_matches!(
2844 ir,
2845 quote! {
2846 Func(Func {
2847 name: "get_ptr_to_func", ...
2848 return_type: MappedType {
2849 rs_type: RsType {
2850 name: Some("Option"), ...
2851 type_args: [RsType { name: Some("#funcPtr vectorcall"), ... }], ...
2852 },
2853 cc_type: CcType {
2854 name: Some("*"), ...
2855 type_args: [CcType { name: Some("#funcValue vectorcall"), ... }], ...
2856 },
Lukasz Anforowicz0b6a6ac2022-03-22 22:32:23 +00002857 }, ...
2858 has_c_calling_convention: true, ...
Lukasz Anforowicz71e9b592022-03-04 19:03:02 +00002859 }),
2860 }
2861 );
2862
2863 // Check that the custom "vectorcall" ABI gets propagated into the
2864 // return type (i.e. into `extern "vectorcall" fn`).
2865 let rs_api = generate_rs_api(&ir)?;
2866 assert_rs_matches!(
2867 rs_api,
2868 quote! {
2869 #[inline(always)]
2870 pub fn get_ptr_to_func() -> Option<extern "vectorcall" fn (f32, f64) -> i32> {
Rosica Dejanovskadd9a9032022-04-12 07:34:41 -07002871 unsafe { detail::__rust_thunk___Z15get_ptr_to_funcv() }
Lukasz Anforowicz71e9b592022-03-04 19:03:02 +00002872 }
2873 }
2874 );
2875
2876 // The usual `extern "C"` ABI should be used for "get_ptr_to_func".
2877 assert_rs_matches!(
2878 rs_api,
2879 quote! {
2880 mod detail {
2881 #[allow(unused_imports)]
2882 use super::*;
2883 extern "C" {
2884 #[link_name = "_Z15get_ptr_to_funcv"]
2885 pub(crate) fn __rust_thunk___Z15get_ptr_to_funcv()
2886 -> Option<extern "vectorcall" fn(f32, f64) -> i32>;
2887 }
2888 }
2889 }
2890 );
2891
2892 // Verify that no C++ thunk got generated.
2893 let rs_api_impl = generate_rs_api_impl(&ir)?;
2894 assert_cc_not_matches!(rs_api_impl, quote! { __rust_thunk___Z15get_ptr_to_funcv });
2895 Ok(())
2896 }
2897
2898 #[test]
2899 fn test_func_ptr_thunk() -> Result<()> {
2900 // Using an `inline` keyword forces generation of a C++ thunk in
2901 // `rs_api_impl` (i.e. exercises `format_cc_type` and similar code).
2902 let ir = ir_from_cc(
2903 r#"
2904 int multiply(int x, int y);
2905 inline int (*inline_get_pointer_to_function())(int, int) {
2906 return multiply;
2907 }
2908 "#,
2909 )?;
2910 let rs_api_impl = generate_rs_api_impl(&ir)?;
2911 assert_cc_matches!(
2912 rs_api_impl,
2913 quote! {
Lukasz Anforowicz23171542022-04-11 15:26:17 -07002914 extern "C" crubit::type_identity_t<int(int , int)>*
Lukasz Anforowicz71e9b592022-03-04 19:03:02 +00002915 __rust_thunk___Z30inline_get_pointer_to_functionv() {
2916 return inline_get_pointer_to_function();
2917 }
2918 }
2919 );
2920 Ok(())
2921 }
2922
2923 #[test]
Lukasz Anforowicz0b6a6ac2022-03-22 22:32:23 +00002924 fn test_func_ptr_with_custom_abi_thunk() -> Result<()> {
2925 // Using an `inline` keyword forces generation of a C++ thunk in
2926 // `rs_api_impl` (i.e. exercises `format_cc_type`,
2927 // `format_cc_call_conv_as_clang_attribute` and similar code).
2928 let ir = ir_from_cc(
2929 r#"
2930 inline int (*inline_get_ptr_to_func())(float, double) [[clang::vectorcall]];
2931 "#,
2932 )?;
2933
2934 // Verify that the test input correctly represents what we intend to
2935 // test - we want [[clang::vectorcall]] to apply to the returned
2936 // function pointer, but *not* apply to the `get_ptr_to_func` function.
2937 assert_ir_matches!(
2938 ir,
2939 quote! {
2940 Func(Func {
2941 name: "inline_get_ptr_to_func", ...
2942 return_type: MappedType {
2943 rs_type: RsType {
2944 name: Some("Option"), ...
2945 type_args: [RsType { name: Some("#funcPtr vectorcall"), ... }], ...
2946 },
2947 cc_type: CcType {
2948 name: Some("*"), ...
2949 type_args: [CcType { name: Some("#funcValue vectorcall"), ... }], ...
2950 },
2951 }, ...
2952 has_c_calling_convention: true, ...
2953 }),
2954 }
2955 );
2956
2957 // This test is quite similar to `test_func_ptr_thunk` - the main
2958 // difference is verification of the `__attribute__((vectorcall))` in
2959 // the expected signature of the generated thunk below.
2960 let rs_api_impl = generate_rs_api_impl(&ir)?;
2961 assert_cc_matches!(
2962 rs_api_impl,
2963 quote! {
Lukasz Anforowicz23171542022-04-11 15:26:17 -07002964 extern "C" crubit::type_identity_t<
Lukasz Anforowicz0b6a6ac2022-03-22 22:32:23 +00002965 int(float , double) __attribute__((vectorcall))
2966 >* __rust_thunk___Z22inline_get_ptr_to_funcv() {
2967 return inline_get_ptr_to_func();
2968 }
2969 }
2970 );
2971 Ok(())
2972 }
2973
2974 #[test]
Michael Forstered642022021-10-04 09:48:25 +00002975 fn test_item_order() -> Result<()> {
Marcel Hlopko89547752021-12-10 09:39:41 +00002976 let ir = ir_from_cc(
2977 "int first_func();
2978 struct FirstStruct {};
2979 int second_func();
2980 struct SecondStruct {};",
2981 )?;
Michael Forstered642022021-10-04 09:48:25 +00002982
Marcel Hlopko89547752021-12-10 09:39:41 +00002983 let rs_api = rs_tokens_to_formatted_string(generate_rs_api(&ir)?)?;
2984
Lukasz Anforowiczdd9ae0f2022-02-17 15:52:53 +00002985 let idx = |s: &str| rs_api.find(s).ok_or_else(|| anyhow!("'{}' missing", s));
Michael Forstered642022021-10-04 09:48:25 +00002986
2987 let f1 = idx("fn first_func")?;
2988 let f2 = idx("fn second_func")?;
2989 let s1 = idx("struct FirstStruct")?;
2990 let s2 = idx("struct SecondStruct")?;
Googlera675ae02021-12-07 08:04:59 +00002991 let t1 = idx("fn __rust_thunk___Z10first_funcv")?;
2992 let t2 = idx("fn __rust_thunk___Z11second_funcv")?;
Michael Forstered642022021-10-04 09:48:25 +00002993
2994 assert!(f1 < s1);
2995 assert!(s1 < f2);
2996 assert!(f2 < s2);
2997 assert!(s2 < t1);
2998 assert!(t1 < t2);
2999
3000 Ok(())
3001 }
Michael Forster028800b2021-10-05 12:39:59 +00003002
3003 #[test]
Devin Jeanpierrec80e6242022-02-03 01:56:40 +00003004 fn test_base_class_subobject_layout() -> Result<()> {
3005 let ir = ir_from_cc(
3006 r#"
3007 // We use a class here to force `Derived::z` to live inside the tail padding of `Base`.
3008 // On the Itanium ABI, this would not happen if `Base` were a POD type.
Devin Jeanpierre56777022022-02-03 01:57:15 +00003009 class Base {__INT64_TYPE__ x; char y;};
3010 struct Derived final : Base {__INT16_TYPE__ z;};
Devin Jeanpierrec80e6242022-02-03 01:56:40 +00003011 "#,
3012 )?;
3013 let rs_api = generate_rs_api(&ir)?;
3014 assert_rs_matches!(
3015 rs_api,
3016 quote! {
3017 #[repr(C, align(8))]
3018 pub struct Derived {
Rosica Dejanovskaaccf00b2022-04-01 13:28:04 -07003019 __base_class_subobjects: [rust_std::mem::MaybeUninit<u8>; 10],
Devin Jeanpierrec80e6242022-02-03 01:56:40 +00003020 pub z: i16,
3021 }
3022 }
3023 );
3024 Ok(())
3025 }
3026
3027 /// The same as test_base_class_subobject_layout, but with multiple
3028 /// inheritance.
3029 #[test]
3030 fn test_base_class_multiple_inheritance_subobject_layout() -> Result<()> {
3031 let ir = ir_from_cc(
3032 r#"
Devin Jeanpierre56777022022-02-03 01:57:15 +00003033 class Base1 {__INT64_TYPE__ x;};
Devin Jeanpierrec80e6242022-02-03 01:56:40 +00003034 class Base2 {char y;};
Devin Jeanpierre56777022022-02-03 01:57:15 +00003035 struct Derived final : Base1, Base2 {__INT16_TYPE__ z;};
Devin Jeanpierrec80e6242022-02-03 01:56:40 +00003036 "#,
3037 )?;
3038 let rs_api = generate_rs_api(&ir)?;
3039 assert_rs_matches!(
3040 rs_api,
3041 quote! {
3042 #[repr(C, align(8))]
3043 pub struct Derived {
Rosica Dejanovskaaccf00b2022-04-01 13:28:04 -07003044 __base_class_subobjects: [rust_std::mem::MaybeUninit<u8>; 10],
Devin Jeanpierrec80e6242022-02-03 01:56:40 +00003045 pub z: i16,
3046 }
3047 }
3048 );
3049 Ok(())
3050 }
3051
3052 /// The same as test_base_class_subobject_layout, but with a chain of
3053 /// inheritance.
3054 #[test]
3055 fn test_base_class_deep_inheritance_subobject_layout() -> Result<()> {
3056 let ir = ir_from_cc(
3057 r#"
Devin Jeanpierre56777022022-02-03 01:57:15 +00003058 class Base1 {__INT64_TYPE__ x;};
Devin Jeanpierrec80e6242022-02-03 01:56:40 +00003059 class Base2 : Base1 {char y;};
Devin Jeanpierre56777022022-02-03 01:57:15 +00003060 struct Derived final : Base2 {__INT16_TYPE__ z;};
Devin Jeanpierrec80e6242022-02-03 01:56:40 +00003061 "#,
3062 )?;
3063 let rs_api = generate_rs_api(&ir)?;
3064 assert_rs_matches!(
3065 rs_api,
3066 quote! {
3067 #[repr(C, align(8))]
3068 pub struct Derived {
Rosica Dejanovskaaccf00b2022-04-01 13:28:04 -07003069 __base_class_subobjects: [rust_std::mem::MaybeUninit<u8>; 10],
Devin Jeanpierrec80e6242022-02-03 01:56:40 +00003070 pub z: i16,
3071 }
3072 }
3073 );
3074 Ok(())
3075 }
3076
3077 /// For derived classes with no data members, we can't use the offset of the
3078 /// first member to determine the size of the base class subobjects.
3079 #[test]
3080 fn test_base_class_subobject_fieldless_layout() -> Result<()> {
3081 let ir = ir_from_cc(
3082 r#"
Devin Jeanpierre56777022022-02-03 01:57:15 +00003083 class Base {__INT64_TYPE__ x; char y;};
Devin Jeanpierrec80e6242022-02-03 01:56:40 +00003084 struct Derived final : Base {};
3085 "#,
3086 )?;
3087 let rs_api = generate_rs_api(&ir)?;
3088 assert_rs_matches!(
3089 rs_api,
3090 quote! {
3091 #[repr(C, align(8))]
3092 pub struct Derived {
Rosica Dejanovskaaccf00b2022-04-01 13:28:04 -07003093 __base_class_subobjects: [rust_std::mem::MaybeUninit<u8>; 9],
Devin Jeanpierrec80e6242022-02-03 01:56:40 +00003094 }
3095 }
3096 );
3097 Ok(())
3098 }
3099
3100 #[test]
3101 fn test_base_class_subobject_empty_fieldless() -> Result<()> {
3102 let ir = ir_from_cc(
3103 r#"
3104 class Base {};
3105 struct Derived final : Base {};
3106 "#,
3107 )?;
3108 let rs_api = generate_rs_api(&ir)?;
3109 assert_rs_matches!(
3110 rs_api,
3111 quote! {
3112 #[repr(C)]
3113 pub struct Derived {
Rosica Dejanovskaaccf00b2022-04-01 13:28:04 -07003114 __base_class_subobjects: [rust_std::mem::MaybeUninit<u8>; 0],
Devin Jeanpierrec80e6242022-02-03 01:56:40 +00003115 /// Prevent empty C++ struct being zero-size in Rust.
Rosica Dejanovskaaccf00b2022-04-01 13:28:04 -07003116 placeholder: rust_std::mem::MaybeUninit<u8>,
Devin Jeanpierrec80e6242022-02-03 01:56:40 +00003117 }
3118 }
3119 );
3120 Ok(())
3121 }
3122
3123 #[test]
3124 fn test_base_class_subobject_empty() -> Result<()> {
3125 let ir = ir_from_cc(
3126 r#"
3127 class Base {};
3128 struct Derived final : Base {};
3129 "#,
3130 )?;
3131 let rs_api = generate_rs_api(&ir)?;
3132 assert_rs_matches!(
3133 rs_api,
3134 quote! {
3135 #[repr(C)]
3136 pub struct Derived {
Rosica Dejanovskaaccf00b2022-04-01 13:28:04 -07003137 __base_class_subobjects: [rust_std::mem::MaybeUninit<u8>; 0],
Devin Jeanpierrec80e6242022-02-03 01:56:40 +00003138 /// Prevent empty C++ struct being zero-size in Rust.
Rosica Dejanovskaaccf00b2022-04-01 13:28:04 -07003139 placeholder: rust_std::mem::MaybeUninit<u8>,
Devin Jeanpierrec80e6242022-02-03 01:56:40 +00003140 }
3141 }
3142 );
3143 Ok(())
3144 }
3145
Devin Jeanpierreb69bcae2022-02-03 09:45:50 +00003146 /// When a field is [[no_unique_address]], it occupies the space up to the
3147 /// next field.
3148 #[test]
3149 fn test_no_unique_address() -> Result<()> {
3150 let ir = ir_from_cc(
3151 r#"
3152 class Field1 {__INT64_TYPE__ x;};
3153 class Field2 {char y;};
3154 struct Struct final {
3155 [[no_unique_address]] Field1 field1;
3156 [[no_unique_address]] Field2 field2;
3157 __INT16_TYPE__ z;
3158 };
3159 "#,
3160 )?;
3161 let rs_api = generate_rs_api(&ir)?;
3162 assert_rs_matches!(
3163 rs_api,
3164 quote! {
3165 #[derive(Clone, Copy)]
3166 #[repr(C, align(8))]
3167 pub struct Struct {
Rosica Dejanovskaaccf00b2022-04-01 13:28:04 -07003168 field1: [rust_std::mem::MaybeUninit<u8>; 8],
3169 field2: [rust_std::mem::MaybeUninit<u8>; 2],
Devin Jeanpierreb69bcae2022-02-03 09:45:50 +00003170 pub z: i16,
3171 }
Devin Jeanpierrec0543eb2022-04-20 16:00:34 -07003172 }
3173 );
Devin Jeanpierre27450132022-04-11 13:52:01 -07003174 assert_rs_matches!(
3175 rs_api,
3176 quote! {
Devin Jeanpierre58181ac2022-02-14 21:30:05 +00003177 impl Struct {
3178 pub fn field1(&self) -> &Field1 {
3179 unsafe {&* (&self.field1 as *const _ as *const Field1)}
3180 }
3181 pub fn field2(&self) -> &Field2 {
3182 unsafe {&* (&self.field2 as *const _ as *const Field2)}
3183 }
3184 }
Devin Jeanpierreb69bcae2022-02-03 09:45:50 +00003185 }
3186 );
3187 Ok(())
3188 }
3189
3190 /// When a [[no_unique_address]] field is the last one, it occupies the rest
3191 /// of the object.
3192 #[test]
3193 fn test_no_unique_address_last_field() -> Result<()> {
3194 let ir = ir_from_cc(
3195 r#"
3196 class Field1 {__INT64_TYPE__ x;};
3197 class Field2 {char y;};
3198 struct Struct final {
3199 [[no_unique_address]] Field1 field1;
3200 [[no_unique_address]] Field2 field2;
3201 };
3202 "#,
3203 )?;
3204 let rs_api = generate_rs_api(&ir)?;
3205 assert_rs_matches!(
3206 rs_api,
3207 quote! {
3208 #[derive(Clone, Copy)]
3209 #[repr(C, align(8))]
3210 pub struct Struct {
Rosica Dejanovskaaccf00b2022-04-01 13:28:04 -07003211 field1: [rust_std::mem::MaybeUninit<u8>; 8],
3212 field2: [rust_std::mem::MaybeUninit<u8>; 8],
Devin Jeanpierreb69bcae2022-02-03 09:45:50 +00003213 }
3214 }
3215 );
3216 Ok(())
3217 }
3218
3219 #[test]
3220 fn test_no_unique_address_empty() -> Result<()> {
3221 let ir = ir_from_cc(
3222 r#"
3223 class Field {};
3224 struct Struct final {
3225 [[no_unique_address]] Field field;
3226 int x;
3227 };
3228 "#,
3229 )?;
3230 let rs_api = generate_rs_api(&ir)?;
3231 assert_rs_matches!(
3232 rs_api,
3233 quote! {
3234 #[repr(C, align(4))]
3235 pub struct Struct {
Rosica Dejanovskaaccf00b2022-04-01 13:28:04 -07003236 field: [rust_std::mem::MaybeUninit<u8>; 0],
Devin Jeanpierreb69bcae2022-02-03 09:45:50 +00003237 pub x: i32,
3238 }
3239 }
3240 );
3241 Ok(())
3242 }
3243
3244 #[test]
3245 fn test_base_class_subobject_empty_last_field() -> Result<()> {
3246 let ir = ir_from_cc(
3247 r#"
3248 class Field {};
3249 struct Struct final {
3250 [[no_unique_address]] Field field;
3251 };
3252 "#,
3253 )?;
3254 let rs_api = generate_rs_api(&ir)?;
3255 assert_rs_matches!(
3256 rs_api,
3257 quote! {
3258 #[repr(C)]
3259 pub struct Struct {
Rosica Dejanovskaaccf00b2022-04-01 13:28:04 -07003260 field: [rust_std::mem::MaybeUninit<u8>; 1],
Devin Jeanpierreb69bcae2022-02-03 09:45:50 +00003261 }
3262 }
3263 );
3264 Ok(())
3265 }
3266
Devin Jeanpierrec80e6242022-02-03 01:56:40 +00003267 #[test]
Teddy Katz76fa42b2022-02-23 01:22:56 +00003268 fn test_generate_enum_basic() -> Result<()> {
3269 let ir = ir_from_cc("enum Color { kRed = 5, kBlue };")?;
3270 let rs_api = generate_rs_api(&ir)?;
3271 assert_rs_matches!(
3272 rs_api,
3273 quote! {
3274 #[repr(transparent)]
3275 #[derive(Debug, PartialEq, Eq, Copy, Clone, Hash, PartialOrd, Ord)]
3276 pub struct Color(u32);
3277 impl Color {
3278 pub const kRed: Color = Color(5);
3279 pub const kBlue: Color = Color(6);
3280 }
3281 impl From<u32> for Color {
3282 fn from(value: u32) -> Color {
3283 Color(v)
3284 }
3285 }
3286 impl From<Color> for u32 {
3287 fn from(value: Color) -> u32 {
3288 v.0
3289 }
3290 }
3291 }
3292 );
3293 Ok(())
3294 }
3295
3296 #[test]
3297 fn test_generate_scoped_enum_basic() -> Result<()> {
3298 let ir = ir_from_cc("enum class Color { kRed = -5, kBlue };")?;
3299 let rs_api = generate_rs_api(&ir)?;
3300 assert_rs_matches!(
3301 rs_api,
3302 quote! {
3303 #[repr(transparent)]
3304 #[derive(Debug, PartialEq, Eq, Copy, Clone, Hash, PartialOrd, Ord)]
3305 pub struct Color(i32);
3306 impl Color {
3307 pub const kRed: Color = Color(-5);
3308 pub const kBlue: Color = Color(-4);
3309 }
3310 impl From<i32> for Color {
3311 fn from(value: i32) -> Color {
3312 Color(v)
3313 }
3314 }
3315 impl From<Color> for i32 {
3316 fn from(value: Color) -> i32 {
3317 v.0
3318 }
3319 }
3320 }
3321 );
3322 Ok(())
3323 }
3324
3325 #[test]
3326 fn test_generate_enum_with_64_bit_signed_vals() -> Result<()> {
3327 let ir = ir_from_cc(
3328 "enum Color : long { kViolet = -9223372036854775807 - 1LL, kRed = -5, kBlue, kGreen = 3, kMagenta = 9223372036854775807 };",
3329 )?;
3330 let rs_api = generate_rs_api(&ir)?;
3331 assert_rs_matches!(
3332 rs_api,
3333 quote! {
3334 #[repr(transparent)]
3335 #[derive(Debug, PartialEq, Eq, Copy, Clone, Hash, PartialOrd, Ord)]
3336 pub struct Color(i64);
3337 impl Color {
3338 pub const kViolet: Color = Color(-9223372036854775808);
3339 pub const kRed: Color = Color(-5);
3340 pub const kBlue: Color = Color(-4);
3341 pub const kGreen: Color = Color(3);
3342 pub const kMagenta: Color = Color(9223372036854775807);
3343 }
3344 impl From<i64> for Color {
3345 fn from(value: i64) -> Color {
3346 Color(v)
3347 }
3348 }
3349 impl From<Color> for i64 {
3350 fn from(value: Color) -> i64 {
3351 v.0
3352 }
3353 }
3354 }
3355 );
3356 Ok(())
3357 }
3358
3359 #[test]
3360 fn test_generate_enum_with_64_bit_unsigned_vals() -> Result<()> {
3361 let ir = ir_from_cc(
3362 "enum Color: unsigned long { kRed, kBlue, kLimeGreen = 18446744073709551615 };",
3363 )?;
3364 let rs_api = generate_rs_api(&ir)?;
3365 assert_rs_matches!(
3366 rs_api,
3367 quote! {
3368 #[repr(transparent)]
3369 #[derive(Debug, PartialEq, Eq, Copy, Clone, Hash, PartialOrd, Ord)]
3370 pub struct Color(u64);
3371 impl Color {
3372 pub const kRed: Color = Color(0);
3373 pub const kBlue: Color = Color(1);
3374 pub const kLimeGreen: Color = Color(18446744073709551615);
3375 }
3376 impl From<u64> for Color {
3377 fn from(value: u64) -> Color {
3378 Color(v)
3379 }
3380 }
3381 impl From<Color> for u64 {
3382 fn from(value: Color) -> u64 {
3383 v.0
3384 }
3385 }
3386 }
3387 );
3388 Ok(())
3389 }
3390
3391 #[test]
3392 fn test_generate_enum_with_32_bit_signed_vals() -> Result<()> {
3393 let ir = ir_from_cc(
3394 "enum Color { kViolet = -2147483647 - 1, kRed = -5, kBlue, kGreen = 3, kMagenta = 2147483647 };",
3395 )?;
3396 let rs_api = generate_rs_api(&ir)?;
3397 assert_rs_matches!(
3398 rs_api,
3399 quote! {
3400 #[repr(transparent)]
3401 #[derive(Debug, PartialEq, Eq, Copy, Clone, Hash, PartialOrd, Ord)]
3402 pub struct Color(i32);
3403 impl Color {
3404 pub const kViolet: Color = Color(-2147483648);
3405 pub const kRed: Color = Color(-5);
3406 pub const kBlue: Color = Color(-4);
3407 pub const kGreen: Color = Color(3);
3408 pub const kMagenta: Color = Color(2147483647);
3409 }
3410 impl From<i32> for Color {
3411 fn from(value: i32) -> Color {
3412 Color(v)
3413 }
3414 }
3415 impl From<Color> for i32 {
3416 fn from(value: Color) -> i32 {
3417 v.0
3418 }
3419 }
3420 }
3421 );
3422 Ok(())
3423 }
3424
3425 #[test]
3426 fn test_generate_enum_with_32_bit_unsigned_vals() -> Result<()> {
3427 let ir = ir_from_cc("enum Color: unsigned int { kRed, kBlue, kLimeGreen = 4294967295 };")?;
3428 let rs_api = generate_rs_api(&ir)?;
3429 assert_rs_matches!(
3430 rs_api,
3431 quote! {
3432 #[repr(transparent)]
3433 #[derive(Debug, PartialEq, Eq, Copy, Clone, Hash, PartialOrd, Ord)]
3434 pub struct Color(u32);
3435 impl Color {
3436 pub const kRed: Color = Color(0);
3437 pub const kBlue: Color = Color(1);
3438 pub const kLimeGreen: Color = Color(4294967295);
3439 }
3440 impl From<u32> for Color {
3441 fn from(value: u32) -> Color {
3442 Color(v)
3443 }
3444 }
3445 impl From<Color> for u32 {
3446 fn from(value: Color) -> u32 {
3447 v.0
3448 }
3449 }
3450 }
3451 );
3452 Ok(())
3453 }
3454
3455 #[test]
Michael Forster409d9412021-10-07 08:35:29 +00003456 fn test_doc_comment_func() -> Result<()> {
Marcel Hlopko89547752021-12-10 09:39:41 +00003457 let ir = ir_from_cc(
3458 "
3459 // Doc Comment
3460 // with two lines
3461 int func();",
3462 )?;
Michael Forster409d9412021-10-07 08:35:29 +00003463
Marcel Hlopko89547752021-12-10 09:39:41 +00003464 assert_rs_matches!(
3465 generate_rs_api(&ir)?,
3466 // leading space is intentional so there is a space between /// and the text of the
3467 // comment
3468 quote! {
3469 #[doc = " Doc Comment\n with two lines"]
3470 #[inline(always)]
3471 pub fn func
3472 }
Michael Forster409d9412021-10-07 08:35:29 +00003473 );
3474
3475 Ok(())
3476 }
3477
3478 #[test]
3479 fn test_doc_comment_record() -> Result<()> {
Marcel Hlopko89547752021-12-10 09:39:41 +00003480 let ir = ir_from_cc(
3481 "// Doc Comment\n\
3482 //\n\
3483 // * with bullet\n\
Devin Jeanpierre88343c72022-01-15 01:10:23 +00003484 struct SomeStruct final {\n\
Marcel Hlopko89547752021-12-10 09:39:41 +00003485 // Field doc\n\
3486 int field;\
3487 };",
3488 )?;
Michael Forster028800b2021-10-05 12:39:59 +00003489
Marcel Hlopko89547752021-12-10 09:39:41 +00003490 assert_rs_matches!(
3491 generate_rs_api(&ir)?,
3492 quote! {
3493 #[doc = " Doc Comment\n \n * with bullet"]
3494 #[derive(Clone, Copy)]
3495 #[repr(C)]
3496 pub struct SomeStruct {
3497 # [doc = " Field doc"]
3498 pub field: i32,
3499 }
3500 }
Michael Forstercc5941a2021-10-07 07:12:24 +00003501 );
Michael Forster028800b2021-10-05 12:39:59 +00003502 Ok(())
3503 }
Devin Jeanpierre91de7012021-10-21 12:53:51 +00003504
Devin Jeanpierre96839c12021-12-14 00:27:38 +00003505 #[test]
Teddy Katzd2cd1422022-04-04 09:41:33 -07003506 fn test_basic_union() -> Result<()> {
3507 let ir = ir_from_cc(
3508 r#"
3509 union SomeUnion {
3510 int some_field;
3511 long long some_bigger_field;
3512 };
3513 "#,
3514 )?;
3515 let rs_api = generate_rs_api(&ir)?;
3516
3517 assert_rs_matches!(
3518 rs_api,
3519 quote! {
3520 #[derive(Clone, Copy)]
3521 #[repr(C)]
3522 pub union SomeUnion {
3523 pub some_field: i32,
3524 pub some_bigger_field: i64,
3525 }
3526 }
3527 );
3528 Ok(())
3529 }
3530
3531 #[test]
3532 fn test_union_with_private_fields() -> Result<()> {
3533 let ir = ir_from_cc(
3534 r#"
3535 union SomeUnionWithPrivateFields {
3536 public:
3537 int public_field;
3538 private:
3539 long long private_field;
3540 };
3541 "#,
3542 )?;
3543 let rs_api = generate_rs_api(&ir)?;
3544
3545 assert_rs_matches!(
3546 rs_api,
3547 quote! {
3548 #[derive(Clone, Copy)]
3549 #[repr(C)]
3550 pub union SomeUnionWithPrivateFields {
3551 pub public_field: i32,
3552 private_field: i64,
3553 }
3554 }
3555 );
3556
3557 assert_rs_matches!(
3558 rs_api,
3559 quote! {
3560 const _: () = assert!(rust_std::mem::size_of::<SomeUnionWithPrivateFields>() == 8usize);
3561 const _: () = assert!(rust_std::mem::align_of::<SomeUnionWithPrivateFields>() == 8usize);
3562 const _: () = {
Lukasz Anforowicz768bba32022-04-11 14:06:13 -07003563 static_assertions::assert_impl_all!(SomeUnionWithPrivateFields: Clone);
Teddy Katzd2cd1422022-04-04 09:41:33 -07003564 };
3565 const _: () = {
Lukasz Anforowicz768bba32022-04-11 14:06:13 -07003566 static_assertions::assert_impl_all!(SomeUnionWithPrivateFields: Copy);
Teddy Katzd2cd1422022-04-04 09:41:33 -07003567 };
3568 const _: () = {
Lukasz Anforowicz768bba32022-04-11 14:06:13 -07003569 static_assertions::assert_not_impl_all!(SomeUnionWithPrivateFields: Drop);
Teddy Katzd2cd1422022-04-04 09:41:33 -07003570 };
3571 const _: () = assert!(offset_of!(SomeUnionWithPrivateFields, public_field) * 8 == 0usize);
3572 const _: () = assert!(offset_of!(SomeUnionWithPrivateFields, private_field) * 8 == 0usize);
3573 }
3574 );
3575 Ok(())
3576 }
3577
3578 #[test]
3579 fn test_empty_union() -> Result<()> {
3580 let ir = ir_from_cc(
3581 r#"
3582 union EmptyUnion {};
3583 "#,
3584 )?;
3585 let rs_api = generate_rs_api(&ir)?;
3586
3587 assert_rs_matches!(
3588 rs_api,
3589 quote! {
3590 #[derive(Clone, Copy)]
3591 #[repr(C)]
3592 pub union EmptyUnion {
3593 /// Prevent empty C++ struct being zero-size in Rust.
3594 placeholder: rust_std::mem::MaybeUninit<u8>,
3595 }
3596 }
3597 );
3598
3599 assert_rs_matches!(
3600 rs_api,
3601 quote! {
3602 const _: () = assert!(rust_std::mem::size_of::<EmptyUnion>() == 1usize);
3603 const _: () = assert!(rust_std::mem::align_of::<EmptyUnion>() == 1usize);
3604 }
3605 );
3606
3607 Ok(())
3608 }
3609
3610 #[test]
3611 fn test_union_field_with_nontrivial_destructor() -> Result<()> {
3612 let ir = ir_from_cc(
3613 r#"
3614 struct NontrivialStruct { ~NontrivialStruct(); };
3615 union UnionWithNontrivialField {
3616 int trivial_field;
3617 NontrivialStruct nontrivial_field;
3618 };
3619 "#,
3620 )?;
3621 let rs_api = generate_rs_api(&ir)?;
3622
3623 assert_rs_matches!(
3624 rs_api,
3625 quote! {
3626 #[derive(Clone, Copy)]
3627 #[repr(C)]
3628 pub union UnionWithNontrivialField {
3629 pub trivial_field: i32,
3630 pub nontrivial_field: rust_std::mem::ManuallyDrop<NontrivialStruct>,
3631 }
3632 }
3633 );
3634
3635 assert_rs_matches!(
3636 rs_api,
3637 quote! {
3638 const _: () = assert!(rust_std::mem::size_of::<UnionWithNontrivialField>() == 4usize);
3639 const _: () = assert!(rust_std::mem::align_of::<UnionWithNontrivialField>() == 4usize);
3640 }
3641 );
3642 Ok(())
3643 }
3644
3645 #[test]
3646 fn test_union_with_constructors() -> Result<()> {
3647 let ir = ir_from_cc(
3648 r#"
3649 #pragma clang lifetime_elision
3650 union UnionWithDefaultConstructors {
3651 int a;
3652 };
3653 "#,
3654 )?;
3655 let rs_api = generate_rs_api(&ir)?;
3656
3657 assert_rs_matches!(
3658 rs_api,
3659 quote! {
3660 #[derive(Clone, Copy)]
3661 #[repr(C)]
3662 pub union UnionWithDefaultConstructors {
3663 pub a: i32,
3664 }
3665 }
3666 );
3667
3668 assert_rs_matches!(
3669 rs_api,
3670 quote! {
3671 impl Default for UnionWithDefaultConstructors {
3672 #[inline(always)]
3673 fn default() -> Self {
3674 let mut tmp = rust_std::mem::MaybeUninit::<Self>::zeroed();
3675 unsafe {
Rosica Dejanovskadd9a9032022-04-12 07:34:41 -07003676 detail::__rust_thunk___ZN28UnionWithDefaultConstructorsC1Ev(&mut tmp);
Teddy Katzd2cd1422022-04-04 09:41:33 -07003677 tmp.assume_init()
3678 }
3679 }
3680 }
3681 }
3682 );
3683
3684 assert_rs_matches!(
3685 rs_api,
3686 quote! {
3687 impl<'b> From<ctor::RvalueReference<'b, UnionWithDefaultConstructors>> for UnionWithDefaultConstructors {
3688 #[inline(always)]
3689 fn from(__param_0: ctor::RvalueReference<'b, UnionWithDefaultConstructors>) -> Self {
3690 let mut tmp = rust_std::mem::MaybeUninit::<Self>::zeroed();
3691 unsafe {
Rosica Dejanovskadd9a9032022-04-12 07:34:41 -07003692 detail::__rust_thunk___ZN28UnionWithDefaultConstructorsC1EOS_(&mut tmp, __param_0);
Teddy Katzd2cd1422022-04-04 09:41:33 -07003693 tmp.assume_init()
3694 }
3695 }
3696 }
3697 }
3698 );
3699
3700 Ok(())
3701 }
3702
3703 #[test]
Devin Jeanpierre56777022022-02-03 01:57:15 +00003704 fn test_unambiguous_public_bases() -> Result<()> {
3705 let ir = ir_from_cc_dependency(
3706 "
3707 struct VirtualBase {};
3708 struct PrivateBase {};
3709 struct ProtectedBase {};
3710 struct UnambiguousPublicBase {};
3711 struct AmbiguousPublicBase {};
3712 struct MultipleInheritance : UnambiguousPublicBase, AmbiguousPublicBase {};
3713 struct Derived : private PrivateBase, protected ProtectedBase, MultipleInheritance, AmbiguousPublicBase, virtual VirtualBase {};
3714 ",
3715 "",
3716 )?;
3717 let rs_api = generate_rs_api(&ir)?;
3718 // TODO(b/216195042): virtual bases.
3719 assert_rs_not_matches!(rs_api, quote! { From<&'a Derived> for &'a VirtualBase });
3720 assert_rs_matches!(rs_api, quote! { From<&'a Derived> for &'a UnambiguousPublicBase });
3721 assert_rs_matches!(rs_api, quote! { From<&'a Derived> for &'a MultipleInheritance });
3722 assert_rs_not_matches!(rs_api, quote! {From<&'a Derived> for &'a PrivateBase});
3723 assert_rs_not_matches!(rs_api, quote! {From<&'a Derived> for &'a ProtectedBase});
3724 assert_rs_not_matches!(rs_api, quote! {From<&'a Derived> for &'a AmbiguousPublicBase});
3725 Ok(())
3726 }
3727
3728 /// Contrary to intuitions: a base class conversion is ambiguous even if the
3729 /// ambiguity is from a private base class cast that you can't even
3730 /// perform.
3731 ///
3732 /// Explanation (courtesy James Dennett):
3733 ///
3734 /// > Once upon a time, there was a rule in C++ that changing all access
3735 /// > specifiers to "public" would not change the meaning of code.
3736 /// > That's no longer true, but some of its effects can still be seen.
3737 ///
3738 /// So, we need to be sure to not allow casting to privately-ambiguous
3739 /// bases.
3740 #[test]
3741 fn test_unambiguous_public_bases_private_ambiguity() -> Result<()> {
3742 let ir = ir_from_cc_dependency(
3743 "
3744 struct Base {};
3745 struct Intermediate : public Base {};
3746 struct Derived : Base, private Intermediate {};
3747 ",
3748 "",
3749 )?;
3750 let rs_api = generate_rs_api(&ir)?;
3751 assert_rs_not_matches!(rs_api, quote! { From<&'a Derived> for &'a Base });
3752 Ok(())
3753 }
3754
3755 #[test]
Devin Jeanpierre96839c12021-12-14 00:27:38 +00003756 fn test_virtual_thunk() -> Result<()> {
3757 let ir = ir_from_cc("struct Polymorphic { virtual void Foo(); };")?;
3758
3759 assert_cc_matches!(
3760 generate_rs_api_impl(&ir)?,
3761 quote! {
Googler972d3582022-01-11 10:17:22 +00003762 extern "C" void __rust_thunk___ZN11Polymorphic3FooEv(class Polymorphic * __this)
Devin Jeanpierre96839c12021-12-14 00:27:38 +00003763 }
3764 );
3765 Ok(())
3766 }
3767
Lukasz Anforowicz0b6a6ac2022-03-22 22:32:23 +00003768 #[test]
3769 fn test_custom_abi_thunk() -> Result<()> {
3770 let ir = ir_from_cc(
3771 r#"
3772 float f_vectorcall_calling_convention(float p1, float p2) [[clang::vectorcall]];
3773 double f_c_calling_convention(double p1, double p2);
3774 "#,
3775 )?;
3776 let rs_api = generate_rs_api(&ir)?;
3777 let rs_api_impl = generate_rs_api_impl(&ir)?;
3778 assert_rs_matches!(
3779 rs_api,
3780 quote! {
3781 #[inline(always)]
3782 pub fn f_vectorcall_calling_convention(p1: f32, p2: f32) -> f32 {
3783 unsafe {
Rosica Dejanovskadd9a9032022-04-12 07:34:41 -07003784 detail::__rust_thunk___Z31f_vectorcall_calling_conventionff(p1, p2)
Lukasz Anforowicz0b6a6ac2022-03-22 22:32:23 +00003785 }
3786 }
3787 }
3788 );
3789 assert_rs_matches!(
3790 rs_api,
3791 quote! {
3792 #[inline(always)]
3793 pub fn f_c_calling_convention(p1: f64, p2: f64) -> f64 {
Rosica Dejanovskadd9a9032022-04-12 07:34:41 -07003794 unsafe { detail::__rust_thunk___Z22f_c_calling_conventiondd(p1, p2) }
Lukasz Anforowicz0b6a6ac2022-03-22 22:32:23 +00003795 }
3796 }
3797 );
3798 // `link_name` (i.e. no thunk) for `f_c_calling_convention`. No
3799 // `link_name` (i.e. indicates presence of a thunk) for
3800 // `f_vectorcall_calling_convention`.
3801 assert_rs_matches!(
3802 rs_api,
3803 quote! {
3804 mod detail {
3805 #[allow(unused_imports)]
3806 use super::*;
3807 extern "C" {
3808 pub(crate) fn __rust_thunk___Z31f_vectorcall_calling_conventionff(
3809 p1: f32, p2: f32) -> f32;
3810 #[link_name = "_Z22f_c_calling_conventiondd"]
3811 pub(crate) fn __rust_thunk___Z22f_c_calling_conventiondd(
3812 p1: f64, p2: f64) -> f64;
3813 }
3814 }
3815 }
3816 );
3817 // C++ thunk needed for `f_vectorcall_calling_convention`.
3818 assert_cc_matches!(
3819 rs_api_impl,
3820 quote! {
3821 extern "C" float __rust_thunk___Z31f_vectorcall_calling_conventionff(
3822 float p1, float p2) {
Devin Jeanpierredeea7892022-03-29 02:13:31 -07003823 return f_vectorcall_calling_convention (std::forward<decltype(p1)>(p1), std::forward<decltype(p2)>(p2));
Lukasz Anforowicz0b6a6ac2022-03-22 22:32:23 +00003824 }
3825 }
3826 );
3827 // No C++ thunk expected for `f_c_calling_convention`.
3828 assert_cc_not_matches!(rs_api_impl, quote! { f_c_calling_convention });
3829 Ok(())
3830 }
3831
Devin Jeanpierree6e16652021-12-22 15:54:46 +00003832 /// A trivially relocatable final struct is safe to use in Rust as normal,
3833 /// and is Unpin.
3834 #[test]
3835 fn test_no_negative_impl_unpin() -> Result<()> {
3836 let ir = ir_from_cc("struct Trivial final {};")?;
3837 let rs_api = generate_rs_api(&ir)?;
3838 assert_rs_not_matches!(rs_api, quote! {impl !Unpin});
3839 Ok(())
3840 }
3841
3842 /// A non-final struct, even if it's trivial, is not usable by mut
3843 /// reference, and so is !Unpin.
3844 #[test]
3845 fn test_negative_impl_unpin_nonfinal() -> Result<()> {
3846 let ir = ir_from_cc("struct Nonfinal {};")?;
3847 let rs_api = generate_rs_api(&ir)?;
3848 assert_rs_matches!(rs_api, quote! {impl !Unpin for Nonfinal {}});
3849 Ok(())
3850 }
3851
Devin Jeanpierre91de7012021-10-21 12:53:51 +00003852 /// At the least, a trivial type should have no drop impl if or until we add
3853 /// empty drop impls.
3854 #[test]
3855 fn test_no_impl_drop() -> Result<()> {
Googler7cced422021-12-06 11:58:39 +00003856 let ir = ir_from_cc("struct Trivial {};")?;
Marcel Hlopko89547752021-12-10 09:39:41 +00003857 let rs_api = rs_tokens_to_formatted_string(generate_rs_api(&ir)?)?;
Devin Jeanpierre91de7012021-10-21 12:53:51 +00003858 assert!(!rs_api.contains("impl Drop"));
3859 Ok(())
3860 }
3861
Devin Jeanpierre7e9a1de2021-12-03 08:04:22 +00003862 /// User-defined destructors *must* become Drop impls with ManuallyDrop
3863 /// fields
Devin Jeanpierre91de7012021-10-21 12:53:51 +00003864 #[test]
Devin Jeanpierre7e9a1de2021-12-03 08:04:22 +00003865 fn test_impl_drop_user_defined_destructor() -> Result<()> {
Googler7cced422021-12-06 11:58:39 +00003866 let ir = ir_from_cc(
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00003867 r#" struct NontrivialStruct { ~NontrivialStruct(); };
3868 struct UserDefinedDestructor {
Devin Jeanpierre91de7012021-10-21 12:53:51 +00003869 ~UserDefinedDestructor();
Devin Jeanpierre7e9a1de2021-12-03 08:04:22 +00003870 int x;
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00003871 NontrivialStruct nts;
Devin Jeanpierre91de7012021-10-21 12:53:51 +00003872 };"#,
3873 )?;
3874 let rs_api = generate_rs_api(&ir)?;
Lukasz Anforowicz6d553632022-01-06 21:36:14 +00003875 assert_rs_matches!(
3876 rs_api,
3877 quote! {
3878 impl Drop for UserDefinedDestructor {
3879 #[inline(always)]
3880 fn drop(&mut self) {
Rosica Dejanovskadd9a9032022-04-12 07:34:41 -07003881 unsafe { detail::__rust_thunk___ZN21UserDefinedDestructorD1Ev(self) }
Lukasz Anforowicz6d553632022-01-06 21:36:14 +00003882 }
3883 }
3884 }
3885 );
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00003886 assert_rs_matches!(rs_api, quote! {pub x: i32,});
Rosica Dejanovskaaccf00b2022-04-01 13:28:04 -07003887 assert_rs_matches!(
3888 rs_api,
3889 quote! {pub nts: rust_std::mem::ManuallyDrop<NontrivialStruct>,}
3890 );
Devin Jeanpierre7e9a1de2021-12-03 08:04:22 +00003891 Ok(())
3892 }
3893
Lukasz Anforowicz6d553632022-01-06 21:36:14 +00003894 /// nontrivial types without user-defined destructors should invoke
3895 /// the C++ destructor to preserve the order of field destructions.
Devin Jeanpierre7e9a1de2021-12-03 08:04:22 +00003896 #[test]
3897 fn test_impl_drop_nontrivial_member_destructor() -> Result<()> {
3898 // TODO(jeanpierreda): This would be cleaner if the UserDefinedDestructor code were
3899 // omitted. For example, we simulate it so that UserDefinedDestructor
3900 // comes from another library.
Googler7cced422021-12-06 11:58:39 +00003901 let ir = ir_from_cc(
Devin Jeanpierre88343c72022-01-15 01:10:23 +00003902 r#"struct UserDefinedDestructor final {
Devin Jeanpierre7e9a1de2021-12-03 08:04:22 +00003903 ~UserDefinedDestructor();
3904 };
Devin Jeanpierre88343c72022-01-15 01:10:23 +00003905 struct TrivialStruct final { int i; };
3906 struct NontrivialMembers final {
Devin Jeanpierre7e9a1de2021-12-03 08:04:22 +00003907 UserDefinedDestructor udd;
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00003908 TrivialStruct ts;
Devin Jeanpierre7e9a1de2021-12-03 08:04:22 +00003909 int x;
3910 };"#,
3911 )?;
3912 let rs_api = generate_rs_api(&ir)?;
Lukasz Anforowicz6d553632022-01-06 21:36:14 +00003913 assert_rs_matches!(
3914 rs_api,
3915 quote! {
3916 impl Drop for NontrivialMembers {
3917 #[inline(always)]
3918 fn drop(&mut self) {
Rosica Dejanovskadd9a9032022-04-12 07:34:41 -07003919 unsafe { detail::__rust_thunk___ZN17NontrivialMembersD1Ev(self) }
Lukasz Anforowicz6d553632022-01-06 21:36:14 +00003920 }
3921 }
3922 }
3923 );
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00003924 assert_rs_matches!(rs_api, quote! {pub x: i32,});
3925 assert_rs_matches!(rs_api, quote! {pub ts: TrivialStruct,});
Lukasz Anforowicz6d553632022-01-06 21:36:14 +00003926 assert_rs_matches!(
3927 rs_api,
Rosica Dejanovskaaccf00b2022-04-01 13:28:04 -07003928 quote! {pub udd: rust_std::mem::ManuallyDrop<UserDefinedDestructor>,}
Lukasz Anforowicz6d553632022-01-06 21:36:14 +00003929 );
Devin Jeanpierre7e9a1de2021-12-03 08:04:22 +00003930 Ok(())
3931 }
3932
3933 /// Trivial types (at least those that are mapped to Copy rust types) do not
3934 /// get a Drop impl.
3935 #[test]
3936 fn test_impl_drop_trivial() -> Result<()> {
Googler7cced422021-12-06 11:58:39 +00003937 let ir = ir_from_cc(
Devin Jeanpierre88343c72022-01-15 01:10:23 +00003938 r#"struct Trivial final {
Devin Jeanpierre7e9a1de2021-12-03 08:04:22 +00003939 ~Trivial() = default;
3940 int x;
3941 };"#,
3942 )?;
3943 let rs_api = generate_rs_api(&ir)?;
Marcel Hlopko89547752021-12-10 09:39:41 +00003944 assert_rs_not_matches!(rs_api, quote! {impl Drop});
3945 assert_rs_matches!(rs_api, quote! {pub x: i32});
Lukasz Anforowicz2f074162022-01-06 22:50:51 +00003946 let rs_api_impl = generate_rs_api_impl(&ir)?;
3947 // TODO(b/213326125): Avoid generating thunk impls that are never called.
3948 // (The test assertion below should be reversed once this bug is fixed.)
3949 assert_cc_matches!(rs_api_impl, quote! { std::destroy_at });
Devin Jeanpierre91de7012021-10-21 12:53:51 +00003950 Ok(())
3951 }
Devin Jeanpierre45cb1162021-10-27 10:54:28 +00003952
3953 #[test]
Lukasz Anforowicze643ec92021-12-22 15:45:15 +00003954 fn test_impl_default_explicitly_defaulted_constructor() -> Result<()> {
3955 let ir = ir_from_cc(
Lukasz Anforowicz95551272022-01-20 00:02:24 +00003956 r#"#pragma clang lifetime_elision
3957 struct DefaultedConstructor final {
Lukasz Anforowicze643ec92021-12-22 15:45:15 +00003958 DefaultedConstructor() = default;
3959 };"#,
3960 )?;
3961 let rs_api = generate_rs_api(&ir)?;
3962 assert_rs_matches!(
3963 rs_api,
3964 quote! {
3965 impl Default for DefaultedConstructor {
3966 #[inline(always)]
3967 fn default() -> Self {
Rosica Dejanovskaaccf00b2022-04-01 13:28:04 -07003968 let mut tmp = rust_std::mem::MaybeUninit::<Self>::zeroed();
Lukasz Anforowicze643ec92021-12-22 15:45:15 +00003969 unsafe {
Rosica Dejanovskadd9a9032022-04-12 07:34:41 -07003970 detail::__rust_thunk___ZN20DefaultedConstructorC1Ev(&mut tmp);
Lukasz Anforowicze643ec92021-12-22 15:45:15 +00003971 tmp.assume_init()
3972 }
3973 }
3974 }
3975 }
3976 );
3977 let rs_api_impl = generate_rs_api_impl(&ir)?;
3978 assert_cc_matches!(
3979 rs_api_impl,
3980 quote! {
3981 extern "C" void __rust_thunk___ZN20DefaultedConstructorC1Ev(
Googler972d3582022-01-11 10:17:22 +00003982 class DefaultedConstructor* __this) {
Lukasz Anforowicz23171542022-04-11 15:26:17 -07003983 crubit::construct_at (std::forward<decltype(__this)>(__this)) ;
Lukasz Anforowicze643ec92021-12-22 15:45:15 +00003984 }
3985 }
3986 );
3987 Ok(())
3988 }
3989
3990 #[test]
Lukasz Anforowicz326c4e42022-01-27 14:43:00 +00003991 fn test_impl_clone_that_propagates_lifetime() -> Result<()> {
3992 // This test covers the case where a single lifetime applies to 1)
3993 // the `__this` parameter and 2) other constructor parameters. For
3994 // example, maybe the newly constructed object needs to have the
3995 // same lifetime as the constructor's parameter. (This might require
3996 // annotating the whole C++ struct with a lifetime, so maybe the
3997 // example below is not fully realistic/accurate...).
3998 let mut ir = ir_from_cc(
3999 r#"#pragma clang lifetime_elision
4000 struct Foo final {
Googler53f65942022-02-23 11:23:30 +00004001 [[clang::annotate("lifetimes", "a: a")]]
Lukasz Anforowicz326c4e42022-01-27 14:43:00 +00004002 Foo(const int& i);
4003 };"#,
4004 )?;
4005 let ctor: &mut Func = ir
4006 .items_mut()
4007 .filter_map(|item| match item {
4008 Item::Func(func) => Some(func),
4009 _ => None,
4010 })
4011 .find(|f| {
4012 matches!(&f.name, UnqualifiedIdentifier::Constructor)
4013 && f.params.get(1).map(|p| p.identifier.identifier == "i").unwrap_or_default()
4014 })
4015 .unwrap();
4016 {
4017 // Double-check that the test scenario set up above uses the same lifetime
4018 // for both of the constructor's parameters: `__this` and `i`.
4019 assert_eq!(ctor.params.len(), 2);
4020 let this_lifetime: LifetimeId =
4021 *ctor.params[0].type_.rs_type.lifetime_args.first().unwrap();
4022 let i_lifetime: LifetimeId =
4023 *ctor.params[1].type_.rs_type.lifetime_args.first_mut().unwrap();
4024 assert_eq!(i_lifetime, this_lifetime);
4025 }
4026
4027 // Before cl/423346348 the generated Rust code would incorrectly look
4028 // like this (note the mismatched 'a and 'b lifetimes):
4029 // fn from<'b>(i: &'a i32) -> Self
4030 // After this CL, this scenario will result in an explicit error.
Devin Jeanpierred7b48102022-03-31 04:15:03 -07004031 let rs_api = generate_rs_api(&ir)?;
4032 assert_rs_not_matches!(rs_api, quote! {impl From});
4033 let rs_api_str = tokens_to_string(rs_api)?;
4034 assert!(rs_api_str.contains(
4035 "// The lifetime of `__this` is unexpectedly also used by another parameter"
4036 ));
Lukasz Anforowicz326c4e42022-01-27 14:43:00 +00004037 Ok(())
4038 }
4039
4040 #[test]
Lukasz Anforowicz9bab8352021-12-22 17:35:31 +00004041 fn test_impl_default_non_trivial_struct() -> Result<()> {
4042 let ir = ir_from_cc(
Lukasz Anforowicz71716b72022-01-26 17:05:05 +00004043 r#"#pragma clang lifetime_elision
4044 struct NonTrivialStructWithConstructors final {
Lukasz Anforowicz9bab8352021-12-22 17:35:31 +00004045 NonTrivialStructWithConstructors();
4046 ~NonTrivialStructWithConstructors(); // Non-trivial
4047 };"#,
4048 )?;
4049 let rs_api = generate_rs_api(&ir)?;
4050 assert_rs_not_matches!(rs_api, quote! {impl Default});
4051 Ok(())
4052 }
4053
4054 #[test]
Lukasz Anforowicz71716b72022-01-26 17:05:05 +00004055 fn test_impl_from_for_explicit_conversion_constructor() -> Result<()> {
4056 let ir = ir_from_cc(
4057 r#"#pragma clang lifetime_elision
4058 struct SomeStruct final {
4059 explicit SomeStruct(int i);
4060 };"#,
4061 )?;
4062 let rs_api = generate_rs_api(&ir)?;
4063 // As discussed in b/214020567 for now we only generate `From::from` bindings
4064 // for *implicit* C++ conversion constructors.
4065 assert_rs_not_matches!(rs_api, quote! {impl From});
4066 Ok(())
4067 }
4068
4069 #[test]
4070 fn test_impl_from_for_implicit_conversion_constructor() -> Result<()> {
4071 let ir = ir_from_cc(
4072 r#"#pragma clang lifetime_elision
4073 struct SomeStruct final {
4074 SomeStruct(int i); // implicit - no `explicit` keyword
4075 };"#,
4076 )?;
4077 let rs_api = generate_rs_api(&ir)?;
4078 // As discussed in b/214020567 we generate `From::from` bindings for
4079 // *implicit* C++ conversion constructors.
4080 assert_rs_matches!(
4081 rs_api,
4082 quote! {
4083 impl From<i32> for SomeStruct {
4084 #[inline(always)]
4085 fn from(i: i32) -> Self {
Rosica Dejanovskaaccf00b2022-04-01 13:28:04 -07004086 let mut tmp = rust_std::mem::MaybeUninit::<Self>::zeroed();
Lukasz Anforowicz71716b72022-01-26 17:05:05 +00004087 unsafe {
Rosica Dejanovskadd9a9032022-04-12 07:34:41 -07004088 detail::__rust_thunk___ZN10SomeStructC1Ei(&mut tmp, i);
Lukasz Anforowicz71716b72022-01-26 17:05:05 +00004089 tmp.assume_init()
4090 }
4091 }
4092 }
4093 }
4094 );
4095 Ok(())
4096 }
4097
4098 #[test]
Lukasz Anforowicz5e623782022-03-14 16:52:23 +00004099 fn test_impl_from_for_implicit_conversion_from_reference() -> Result<()> {
4100 let ir = ir_from_cc(
4101 r#"#pragma clang lifetime_elision
4102 struct SomeOtherStruct final { int i; };
4103 struct StructUnderTest final {
4104 StructUnderTest(const SomeOtherStruct& other); // implicit - no `explicit` keyword
4105 };"#,
4106 )?;
4107 let rs_api = generate_rs_api(&ir)?;
4108 // This is a regression test for b/223800038: We want to ensure that the
4109 // code says `impl<'b>` (instead of incorrectly declaring that lifetime
4110 // in `fn from<'b>`).
4111 assert_rs_matches!(
4112 rs_api,
4113 quote! {
4114 impl<'b> From<&'b SomeOtherStruct> for StructUnderTest {
4115 #[inline(always)]
4116 fn from(other: &'b SomeOtherStruct) -> Self {
Rosica Dejanovskaaccf00b2022-04-01 13:28:04 -07004117 let mut tmp = rust_std::mem::MaybeUninit::<Self>::zeroed();
Lukasz Anforowicz5e623782022-03-14 16:52:23 +00004118 unsafe {
Rosica Dejanovskadd9a9032022-04-12 07:34:41 -07004119 detail::__rust_thunk___ZN15StructUnderTestC1ERK15SomeOtherStruct(
Lukasz Anforowicz5e623782022-03-14 16:52:23 +00004120 &mut tmp, other);
4121 tmp.assume_init()
4122 }
4123 }
4124 }
4125 },
4126 );
4127 Ok(())
4128 }
4129
4130 #[test]
Lukasz Anforowicz732ca642022-02-03 20:58:38 +00004131 fn test_impl_eq_for_member_function() -> Result<()> {
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +00004132 let ir = ir_from_cc(
4133 r#"#pragma clang lifetime_elision
4134 struct SomeStruct final {
4135 inline bool operator==(const SomeStruct& other) const {
4136 return i == other.i;
4137 }
4138 int i;
4139 };"#,
4140 )?;
4141 let rs_api = generate_rs_api(&ir)?;
4142 assert_rs_matches!(
4143 rs_api,
4144 quote! {
4145 impl PartialEq<SomeStruct> for SomeStruct {
4146 #[inline(always)]
4147 fn eq<'a, 'b>(&'a self, other: &'b SomeStruct) -> bool {
Rosica Dejanovskadd9a9032022-04-12 07:34:41 -07004148 unsafe { detail::__rust_thunk___ZNK10SomeStructeqERKS_(self, other) }
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +00004149 }
4150 }
4151 }
4152 );
4153 let rs_api_impl = generate_rs_api_impl(&ir)?;
4154 assert_cc_matches!(
4155 rs_api_impl,
4156 quote! {
4157 extern "C" bool __rust_thunk___ZNK10SomeStructeqERKS_(
4158 const class SomeStruct* __this, const class SomeStruct& other) {
Devin Jeanpierredeea7892022-03-29 02:13:31 -07004159 return __this->operator==(std::forward<decltype(other)>(other));
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +00004160 }
4161 }
4162 );
4163 Ok(())
4164 }
4165
4166 #[test]
Lukasz Anforowicz732ca642022-02-03 20:58:38 +00004167 fn test_impl_eq_for_free_function() -> Result<()> {
4168 let ir = ir_from_cc(
4169 r#"#pragma clang lifetime_elision
4170 struct SomeStruct final { int i; };
4171 bool operator==(const SomeStruct& lhs, const SomeStruct& rhs) {
4172 return lhs.i == rhs.i;
4173 }"#,
4174 )?;
4175 let rs_api = generate_rs_api(&ir)?;
4176 assert_rs_matches!(
4177 rs_api,
4178 quote! {
4179 impl PartialEq<SomeStruct> for SomeStruct {
4180 #[inline(always)]
4181 fn eq<'a, 'b>(&'a self, rhs: &'b SomeStruct) -> bool {
Rosica Dejanovskadd9a9032022-04-12 07:34:41 -07004182 unsafe { detail::__rust_thunk___ZeqRK10SomeStructS1_(self, rhs) }
Lukasz Anforowicz732ca642022-02-03 20:58:38 +00004183 }
4184 }
4185 }
4186 );
4187 Ok(())
4188 }
4189
4190 #[test]
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +00004191 fn test_impl_eq_non_const_member_function() -> Result<()> {
4192 let ir = ir_from_cc(
4193 r#"#pragma clang lifetime_elision
4194 struct SomeStruct final {
4195 bool operator==(const SomeStruct& other) /* no `const` here */;
4196 };"#,
4197 )?;
4198 let rs_api = generate_rs_api(&ir)?;
4199 assert_rs_not_matches!(rs_api, quote! {impl PartialEq});
4200 Ok(())
4201 }
4202
4203 #[test]
4204 fn test_impl_eq_rhs_by_value() -> Result<()> {
4205 let ir = ir_from_cc(
4206 r#"#pragma clang lifetime_elision
4207 struct SomeStruct final {
4208 bool operator==(SomeStruct other) const;
4209 };"#,
4210 )?;
4211 let rs_api = generate_rs_api(&ir)?;
4212 assert_rs_not_matches!(rs_api, quote! {impl PartialEq});
4213 Ok(())
4214 }
4215
4216 #[test]
Dmitri Gribenko67cbfd22022-03-24 13:39:34 +00004217 fn test_thunk_ident_function() -> Result<()> {
4218 let ir = ir_from_cc("inline int foo() {}")?;
4219 let func = retrieve_func(&ir, "foo");
4220 assert_eq!(thunk_ident(func), make_rs_ident("__rust_thunk___Z3foov"));
4221 Ok(())
Devin Jeanpierre45cb1162021-10-27 10:54:28 +00004222 }
4223
4224 #[test]
4225 fn test_thunk_ident_special_names() {
Marcel Hlopko4b13b962021-12-06 12:40:56 +00004226 let ir = ir_from_cc("struct Class {};").unwrap();
Devin Jeanpierre45cb1162021-10-27 10:54:28 +00004227
Googler45ad2752021-12-06 12:12:35 +00004228 let destructor =
4229 ir.functions().find(|f| f.name == UnqualifiedIdentifier::Destructor).unwrap();
Lukasz Anforowiczdd9ae0f2022-02-17 15:52:53 +00004230 assert_eq!(thunk_ident(destructor), make_rs_ident("__rust_thunk___ZN5ClassD1Ev"));
Devin Jeanpierre45cb1162021-10-27 10:54:28 +00004231
Lukasz Anforowicz49b5bbc2022-02-04 23:40:10 +00004232 let default_constructor = ir
4233 .functions()
4234 .find(|f| f.name == UnqualifiedIdentifier::Constructor && f.params.len() == 1)
4235 .unwrap();
Lukasz Anforowiczdd9ae0f2022-02-17 15:52:53 +00004236 assert_eq!(thunk_ident(default_constructor), make_rs_ident("__rust_thunk___ZN5ClassC1Ev"));
Devin Jeanpierre45cb1162021-10-27 10:54:28 +00004237 }
Googler7cced422021-12-06 11:58:39 +00004238
4239 #[test]
Marcel Hlopko89547752021-12-10 09:39:41 +00004240 fn test_elided_lifetimes() -> Result<()> {
Googler7cced422021-12-06 11:58:39 +00004241 let ir = ir_from_cc(
4242 r#"#pragma clang lifetime_elision
Devin Jeanpierre88343c72022-01-15 01:10:23 +00004243 struct S final {
Googler7cced422021-12-06 11:58:39 +00004244 int& f(int& i);
4245 };"#,
Marcel Hlopko89547752021-12-10 09:39:41 +00004246 )?;
4247 let rs_api = generate_rs_api(&ir)?;
4248 assert_rs_matches!(
4249 rs_api,
4250 quote! {
Lukasz Anforowicz231a3bb2022-01-12 14:05:59 +00004251 pub fn f<'a, 'b>(&'a mut self, i: &'b mut i32) -> &'a mut i32 { ... }
Marcel Hlopko89547752021-12-10 09:39:41 +00004252 }
Googler7cced422021-12-06 11:58:39 +00004253 );
Marcel Hlopko89547752021-12-10 09:39:41 +00004254 assert_rs_matches!(
4255 rs_api,
4256 quote! {
Googler6804a012022-01-05 07:04:36 +00004257 pub(crate) fn __rust_thunk___ZN1S1fERi<'a, 'b>(__this: &'a mut S, i: &'b mut i32)
4258 -> &'a mut i32;
Marcel Hlopko89547752021-12-10 09:39:41 +00004259 }
Googler7cced422021-12-06 11:58:39 +00004260 );
Marcel Hlopko89547752021-12-10 09:39:41 +00004261 Ok(())
Googler7cced422021-12-06 11:58:39 +00004262 }
Lukasz Anforowiczdaff0402021-12-23 00:37:50 +00004263
4264 #[test]
Googler386e5942022-02-24 08:53:29 +00004265 fn test_annotated_lifetimes() -> Result<()> {
4266 let ir = ir_from_cc(
4267 r#"[[clang::annotate("lifetimes", "a, a -> a")]]
4268 int& f(int& i1, int& i2);
4269 "#,
4270 )?;
4271 let rs_api = generate_rs_api(&ir)?;
4272 assert_rs_matches!(
4273 rs_api,
4274 quote! {
4275 pub fn f<'a>(i1: &'a mut i32, i2: &'a mut i32) -> &'a mut i32 { ... }
4276 }
4277 );
4278 assert_rs_matches!(
4279 rs_api,
4280 quote! {
4281 pub(crate) fn __rust_thunk___Z1fRiS_<'a>(i1: &'a mut i32, i2: &'a mut i32)
4282 -> &'a mut i32;
4283 }
4284 );
4285 Ok(())
4286 }
4287
4288 #[test]
Lukasz Anforowiczdaff0402021-12-23 00:37:50 +00004289 fn test_format_generic_params() -> Result<()> {
4290 assert_rs_matches!(format_generic_params(std::iter::empty::<syn::Ident>()), quote! {});
4291
Marcel Hlopkoeaae9b72022-01-21 15:54:11 +00004292 let idents = ["T1", "T2"].iter().map(|s| make_rs_ident(s));
Lukasz Anforowiczdaff0402021-12-23 00:37:50 +00004293 assert_rs_matches!(format_generic_params(idents), quote! { < T1, T2 > });
4294
4295 let lifetimes = ["a", "b"]
4296 .iter()
4297 .map(|s| syn::Lifetime::new(&format!("'{}", s), proc_macro2::Span::call_site()));
4298 assert_rs_matches!(format_generic_params(lifetimes), quote! { < 'a, 'b > });
4299
4300 Ok(())
4301 }
Googlerd03d05b2022-01-07 10:10:57 +00004302
4303 #[test]
Devin Jeanpierread125742022-04-11 13:50:28 -07004304 fn test_format_tuple_except_singleton() {
4305 fn format(xs: &[TokenStream]) -> TokenStream {
4306 format_tuple_except_singleton(xs)
4307 }
4308 assert_rs_matches!(format(&[]), quote! {()});
4309 assert_rs_matches!(format(&[quote! {a}]), quote! {a});
4310 assert_rs_matches!(format(&[quote! {a}, quote! {b}]), quote! {(a, b)});
4311 }
4312
4313 #[test]
Googlerd03d05b2022-01-07 10:10:57 +00004314 fn test_overloaded_functions() -> Result<()> {
4315 // TODO(b/213280424): We don't support creating bindings for overloaded
4316 // functions yet, except in the case of overloaded constructors with a
4317 // single parameter.
4318 let ir = ir_from_cc(
Lukasz Anforowicz55673c92022-01-27 19:37:26 +00004319 r#" #pragma clang lifetime_elision
4320 void f();
Googlerd03d05b2022-01-07 10:10:57 +00004321 void f(int i);
Devin Jeanpierre88343c72022-01-15 01:10:23 +00004322 struct S1 final {
Googlerd03d05b2022-01-07 10:10:57 +00004323 void f();
4324 void f(int i);
4325 };
Devin Jeanpierre88343c72022-01-15 01:10:23 +00004326 struct S2 final {
Googlerd03d05b2022-01-07 10:10:57 +00004327 void f();
4328 };
Devin Jeanpierre88343c72022-01-15 01:10:23 +00004329 struct S3 final {
Googlerd03d05b2022-01-07 10:10:57 +00004330 S3(int i);
4331 S3(double d);
4332 };
4333 "#,
4334 )?;
4335 let rs_api = generate_rs_api(&ir)?;
4336 let rs_api_str = tokens_to_string(rs_api.clone())?;
4337
4338 // Cannot overload free functions.
4339 assert!(rs_api_str.contains("Error while generating bindings for item 'f'"));
4340 assert_rs_not_matches!(rs_api, quote! {pub fn f()});
4341 assert_rs_not_matches!(rs_api, quote! {pub fn f(i: i32)});
4342
4343 // Cannot overload member functions.
4344 assert!(rs_api_str.contains("Error while generating bindings for item 'S1::f'"));
4345 assert_rs_not_matches!(rs_api, quote! {pub fn f(... S1 ...)});
4346
4347 // But we can import member functions that have the same name as a free
4348 // function.
Lukasz Anforowicz55673c92022-01-27 19:37:26 +00004349 assert_rs_matches!(rs_api, quote! {pub fn f<'a>(&'a mut self)});
Googlerd03d05b2022-01-07 10:10:57 +00004350
4351 // We can also import overloaded single-parameter constructors.
4352 assert_rs_matches!(rs_api, quote! {impl From<i32> for S3});
4353 assert_rs_matches!(rs_api, quote! {impl From<f64> for S3});
4354 Ok(())
4355 }
Googlerdcca7f72022-01-10 12:30:43 +00004356
4357 #[test]
4358 fn test_type_alias() -> Result<()> {
4359 let ir = ir_from_cc(
4360 r#"
Lukasz Anforowiczc503af42022-03-04 23:18:15 +00004361 // MyTypedefDecl doc comment
Googlerdcca7f72022-01-10 12:30:43 +00004362 typedef int MyTypedefDecl;
Lukasz Anforowiczc503af42022-03-04 23:18:15 +00004363
Googlerdcca7f72022-01-10 12:30:43 +00004364 using MyTypeAliasDecl = int;
Googler6a0a5252022-01-11 14:08:09 +00004365 using MyTypeAliasDecl_Alias = MyTypeAliasDecl;
4366
Devin Jeanpierre88343c72022-01-15 01:10:23 +00004367 struct S final {};
Googler6a0a5252022-01-11 14:08:09 +00004368 using S_Alias = S;
4369 using S_Alias_Alias = S_Alias;
4370
4371 inline void f(MyTypedefDecl t) {}
Googlerdcca7f72022-01-10 12:30:43 +00004372 "#,
4373 )?;
4374 let rs_api = generate_rs_api(&ir)?;
Lukasz Anforowiczc503af42022-03-04 23:18:15 +00004375 assert_rs_matches!(
4376 rs_api,
4377 quote! {
4378 #[doc = " MyTypedefDecl doc comment"]
4379 pub type MyTypedefDecl = i32;
4380 }
4381 );
Googler6a0a5252022-01-11 14:08:09 +00004382 assert_rs_matches!(rs_api, quote! { pub type MyTypeAliasDecl = i32; });
4383 assert_rs_matches!(rs_api, quote! { pub type MyTypeAliasDecl_Alias = MyTypeAliasDecl; });
4384 assert_rs_matches!(rs_api, quote! { pub type S_Alias = S; });
4385 assert_rs_matches!(rs_api, quote! { pub type S_Alias_Alias = S_Alias; });
4386 assert_rs_matches!(rs_api, quote! { pub fn f(t: MyTypedefDecl) });
4387 assert_cc_matches!(
4388 generate_rs_api_impl(&ir)?,
4389 quote! {
Devin Jeanpierredeea7892022-03-29 02:13:31 -07004390 extern "C" void __rust_thunk___Z1fi(MyTypedefDecl t){ f (std::forward<decltype(t)>(t)) ; }
Googler6a0a5252022-01-11 14:08:09 +00004391 }
4392 );
Googlerdcca7f72022-01-10 12:30:43 +00004393 Ok(())
4394 }
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00004395
4396 #[test]
4397 fn test_rs_type_kind_implements_copy() -> Result<()> {
Lukasz Anforowicz20651e32022-02-10 14:52:15 +00004398 let template = r#" LIFETIMES
Devin Jeanpierre88343c72022-01-15 01:10:23 +00004399 struct [[clang::trivial_abi]] TrivialStruct final { int i; };
4400 struct [[clang::trivial_abi]] UserDefinedCopyConstructor final {
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00004401 UserDefinedCopyConstructor(const UserDefinedCopyConstructor&);
4402 };
4403 using IntAlias = int;
4404 using TrivialAlias = TrivialStruct;
4405 using NonTrivialAlias = UserDefinedCopyConstructor;
4406 void func(PARAM_TYPE some_param);
4407 "#;
4408 assert_impl_all!(i32: Copy);
4409 assert_impl_all!(&i32: Copy);
4410 assert_not_impl_all!(&mut i32: Copy);
Lukasz Anforowicz20651e32022-02-10 14:52:15 +00004411 assert_impl_all!(Option<&i32>: Copy);
4412 assert_not_impl_all!(Option<&mut i32>: Copy);
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00004413 assert_impl_all!(*const i32: Copy);
4414 assert_impl_all!(*mut i32: Copy);
Lukasz Anforowicz20651e32022-02-10 14:52:15 +00004415 struct Test {
4416 // Test inputs:
4417 cc: &'static str,
4418 lifetimes: bool,
4419 // Expected test outputs:
4420 rs: &'static str,
4421 is_copy: bool,
4422 }
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00004423 let tests = vec![
4424 // Validity of the next few tests is verified via
4425 // `assert_[not_]impl_all!` static assertions above.
Lukasz Anforowicz20651e32022-02-10 14:52:15 +00004426 Test { cc: "int", lifetimes: true, rs: "i32", is_copy: true },
4427 Test { cc: "const int&", lifetimes: true, rs: "&'a i32", is_copy: true },
4428 Test { cc: "int&", lifetimes: true, rs: "&'a mut i32", is_copy: false },
4429 Test { cc: "const int*", lifetimes: true, rs: "Option<&'a i32>", is_copy: true },
4430 Test { cc: "int*", lifetimes: true, rs: "Option<&'a mut i32>", is_copy: false },
4431 Test { cc: "const int*", lifetimes: false, rs: "*const i32", is_copy: true },
4432 Test { cc: "int*", lifetimes: false, rs: "*mut i32", is_copy: true },
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00004433 // Tests below have been thought-through and verified "manually".
Lukasz Anforowicz20651e32022-02-10 14:52:15 +00004434 // TrivialStruct is expected to derive Copy.
4435 Test { cc: "TrivialStruct", lifetimes: true, rs: "TrivialStruct", is_copy: true },
4436 Test {
4437 cc: "UserDefinedCopyConstructor",
4438 lifetimes: true,
4439 rs: "UserDefinedCopyConstructor",
4440 is_copy: false,
4441 },
4442 Test { cc: "IntAlias", lifetimes: true, rs: "IntAlias", is_copy: true },
4443 Test { cc: "TrivialAlias", lifetimes: true, rs: "TrivialAlias", is_copy: true },
4444 Test { cc: "NonTrivialAlias", lifetimes: true, rs: "NonTrivialAlias", is_copy: false },
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00004445 ];
Lukasz Anforowicz20651e32022-02-10 14:52:15 +00004446 for test in tests.iter() {
4447 let test_name = format!("cc='{}', lifetimes={}", test.cc, test.lifetimes);
4448 let cc_input = template.replace("PARAM_TYPE", test.cc).replace(
4449 "LIFETIMES",
4450 if test.lifetimes { "#pragma clang lifetime_elision" } else { "" },
4451 );
4452 let ir = ir_from_cc(&cc_input)?;
Lukasz Anforowicz9c663ca2022-02-09 01:33:31 +00004453 let f = retrieve_func(&ir, "func");
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00004454 let t = RsTypeKind::new(&f.params[0].type_.rs_type, &ir)?;
Lukasz Anforowicz20651e32022-02-10 14:52:15 +00004455
Devin Jeanpierre92ca2612022-04-06 11:35:13 -07004456 let fmt = tokens_to_string(t.to_token_stream())?;
Lukasz Anforowicz20651e32022-02-10 14:52:15 +00004457 assert_eq!(test.rs, fmt, "Testing: {}", test_name);
4458
4459 assert_eq!(test.is_copy, t.implements_copy(), "Testing: {}", test_name);
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00004460 }
4461 Ok(())
4462 }
Lukasz Anforowicza94ab702022-01-14 22:40:25 +00004463
4464 #[test]
4465 fn test_rs_type_kind_is_shared_ref_to_with_lifetimes() -> Result<()> {
4466 let ir = ir_from_cc(
4467 "#pragma clang lifetime_elision
4468 struct SomeStruct {};
4469 void foo(const SomeStruct& foo_param);
4470 void bar(SomeStruct& bar_param);",
4471 )?;
4472 let record = ir.records().next().unwrap();
Lukasz Anforowicz9c663ca2022-02-09 01:33:31 +00004473 let foo_func = retrieve_func(&ir, "foo");
4474 let bar_func = retrieve_func(&ir, "bar");
Lukasz Anforowicza94ab702022-01-14 22:40:25 +00004475
4476 // const-ref + lifetimes in C++ ===> shared-ref in Rust
4477 assert_eq!(foo_func.params.len(), 1);
4478 let foo_param = &foo_func.params[0];
4479 assert_eq!(&foo_param.identifier.identifier, "foo_param");
4480 let foo_type = RsTypeKind::new(&foo_param.type_.rs_type, &ir)?;
4481 assert!(foo_type.is_shared_ref_to(record));
4482 assert!(matches!(foo_type, RsTypeKind::Reference { mutability: Mutability::Const, .. }));
4483
4484 // non-const-ref + lifetimes in C++ ===> mutable-ref in Rust
4485 assert_eq!(bar_func.params.len(), 1);
4486 let bar_param = &bar_func.params[0];
4487 assert_eq!(&bar_param.identifier.identifier, "bar_param");
4488 let bar_type = RsTypeKind::new(&bar_param.type_.rs_type, &ir)?;
4489 assert!(!bar_type.is_shared_ref_to(record));
4490 assert!(matches!(bar_type, RsTypeKind::Reference { mutability: Mutability::Mut, .. }));
4491
4492 Ok(())
4493 }
4494
4495 #[test]
4496 fn test_rs_type_kind_is_shared_ref_to_without_lifetimes() -> Result<()> {
4497 let ir = ir_from_cc(
4498 "struct SomeStruct {};
4499 void foo(const SomeStruct& foo_param);",
4500 )?;
4501 let record = ir.records().next().unwrap();
Lukasz Anforowicz9c663ca2022-02-09 01:33:31 +00004502 let foo_func = retrieve_func(&ir, "foo");
Lukasz Anforowicza94ab702022-01-14 22:40:25 +00004503
4504 // const-ref + *no* lifetimes in C++ ===> const-pointer in Rust
4505 assert_eq!(foo_func.params.len(), 1);
4506 let foo_param = &foo_func.params[0];
4507 assert_eq!(&foo_param.identifier.identifier, "foo_param");
4508 let foo_type = RsTypeKind::new(&foo_param.type_.rs_type, &ir)?;
4509 assert!(!foo_type.is_shared_ref_to(record));
4510 assert!(matches!(foo_type, RsTypeKind::Pointer { mutability: Mutability::Const, .. }));
4511
4512 Ok(())
4513 }
Marcel Hlopkoeaae9b72022-01-21 15:54:11 +00004514
4515 #[test]
Lukasz Anforowicz90bdb962022-02-14 21:07:45 +00004516 fn test_rs_type_kind_dfs_iter_ordering() {
4517 // Set up a test input representing: A<B<C>, D<E>>.
4518 let a = {
4519 let b = {
4520 let c = RsTypeKind::Other { name: "C", type_args: vec![] };
4521 RsTypeKind::Other { name: "B", type_args: vec![c] }
4522 };
4523 let d = {
4524 let e = RsTypeKind::Other { name: "E", type_args: vec![] };
4525 RsTypeKind::Other { name: "D", type_args: vec![e] }
4526 };
4527 RsTypeKind::Other { name: "A", type_args: vec![b, d] }
4528 };
4529 let dfs_names = a
4530 .dfs_iter()
4531 .map(|t| match t {
4532 RsTypeKind::Other { name, .. } => *name,
4533 _ => unreachable!("Only 'other' types are used in this test"),
4534 })
4535 .collect_vec();
4536 assert_eq!(vec!["A", "B", "C", "D", "E"], dfs_names);
4537 }
4538
4539 #[test]
Lukasz Anforowiczcf230fd2022-02-18 19:20:39 +00004540 fn test_rs_type_kind_dfs_iter_ordering_for_func_ptr() {
4541 // Set up a test input representing: fn(A, B) -> C
4542 let f = {
4543 let a = RsTypeKind::Other { name: "A", type_args: vec![] };
4544 let b = RsTypeKind::Other { name: "B", type_args: vec![] };
4545 let c = RsTypeKind::Other { name: "C", type_args: vec![] };
4546 RsTypeKind::FuncPtr { abi: "blah", param_types: vec![a, b], return_type: Box::new(c) }
4547 };
4548 let dfs_names = f
4549 .dfs_iter()
4550 .map(|t| match t {
4551 RsTypeKind::FuncPtr { .. } => "fn",
4552 RsTypeKind::Other { name, .. } => *name,
4553 _ => unreachable!("Only FuncPtr and Other kinds are used in this test"),
4554 })
4555 .collect_vec();
4556 assert_eq!(vec!["fn", "A", "B", "C"], dfs_names);
4557 }
4558
4559 #[test]
Lukasz Anforowicz90bdb962022-02-14 21:07:45 +00004560 fn test_rs_type_kind_lifetimes() -> Result<()> {
4561 let ir = ir_from_cc(
4562 r#"
4563 #pragma clang lifetime_elision
4564 using TypeAlias = int&;
4565 struct SomeStruct {};
4566 void foo(int a, int& b, int* c, int** d, TypeAlias e, SomeStruct f); "#,
4567 )?;
4568 let f = retrieve_func(&ir, "foo");
4569 let ret = RsTypeKind::new(&f.return_type.rs_type, &ir)?;
4570 let a = RsTypeKind::new(&f.params[0].type_.rs_type, &ir)?;
4571 let b = RsTypeKind::new(&f.params[1].type_.rs_type, &ir)?;
4572 let c = RsTypeKind::new(&f.params[2].type_.rs_type, &ir)?;
4573 let d = RsTypeKind::new(&f.params[3].type_.rs_type, &ir)?;
4574 let e = RsTypeKind::new(&f.params[4].type_.rs_type, &ir)?;
4575 let f = RsTypeKind::new(&f.params[5].type_.rs_type, &ir)?;
4576
4577 assert_eq!(0, ret.lifetimes().count()); // No lifetimes on `void`.
4578 assert_eq!(0, a.lifetimes().count()); // No lifetimes on `int`.
4579 assert_eq!(1, b.lifetimes().count()); // `&'a i32` has a single lifetime.
4580 assert_eq!(1, c.lifetimes().count()); // `Option<&'b i32>` has a single lifetime.
4581 assert_eq!(2, d.lifetimes().count()); // `&'c Option<&'d i32>` has two lifetimes.
4582 assert_eq!(1, e.lifetimes().count()); // Lifetime of underlying type should show through.
4583 assert_eq!(0, f.lifetimes().count()); // No lifetimes on structs (yet).
4584 Ok(())
4585 }
4586
4587 #[test]
4588 fn test_rs_type_kind_lifetimes_raw_ptr() -> Result<()> {
4589 let ir = ir_from_cc("void foo(int* a);")?;
4590 let f = retrieve_func(&ir, "foo");
4591 let a = RsTypeKind::new(&f.params[0].type_.rs_type, &ir)?;
4592 assert_eq!(0, a.lifetimes().count()); // No lifetimes on `int*`.
4593 Ok(())
4594 }
4595
4596 #[test]
Marcel Hlopkoeaae9b72022-01-21 15:54:11 +00004597 fn test_rust_keywords_are_escaped_in_rs_api_file() -> Result<()> {
4598 let ir = ir_from_cc("struct type { int dyn; };")?;
4599 let rs_api = generate_rs_api(&ir)?;
4600 assert_rs_matches!(rs_api, quote! { struct r#type { ... r#dyn: i32 ... } });
4601 Ok(())
4602 }
4603
4604 #[test]
4605 fn test_rust_keywords_are_not_escaped_in_rs_api_impl_file() -> Result<()> {
4606 let ir = ir_from_cc("struct type { int dyn; };")?;
4607 let rs_api_impl = generate_rs_api_impl(&ir)?;
Lukasz Anforowicz4ee9c222022-04-13 09:33:36 -07004608 assert_cc_matches!(
4609 rs_api_impl,
4610 quote! { static_assert(CRUBIT_OFFSET_OF(dyn, class type) ... ) }
4611 );
Marcel Hlopkoeaae9b72022-01-21 15:54:11 +00004612 Ok(())
4613 }
Marcel Hlopko14ee3c82022-02-09 09:46:23 +00004614
4615 #[test]
4616 fn test_no_aligned_attr() {
4617 let ir = ir_from_cc("struct SomeStruct {};").unwrap();
4618 let rs_api = generate_rs_api(&ir).unwrap();
4619
4620 assert_rs_matches! {rs_api, quote! {
4621 #[repr(C)]
4622 pub struct SomeStruct { ... }
4623 }};
4624 }
4625
4626 #[test]
4627 fn test_aligned_attr() {
4628 let ir = ir_from_cc("struct SomeStruct {} __attribute__((aligned(64)));").unwrap();
4629 let rs_api = generate_rs_api(&ir).unwrap();
4630
4631 assert_rs_matches! {rs_api, quote! {
4632 #[repr(C, align(64))]
4633 pub struct SomeStruct { ... }
4634 }
4635 };
4636 }
Devin Jeanpierre149950d2022-02-22 21:02:02 +00004637
4638 /// !Unpin references should not be pinned.
4639 #[test]
4640 fn test_nonunpin_ref_param() -> Result<()> {
4641 let rs_api_impl = generate_rs_api(&ir_from_cc(
4642 r#"
4643 #pragma clang lifetime_elision
4644 struct S {~S();};
4645 void Function(const S& s);
4646 "#,
4647 )?)?;
4648 assert_rs_matches!(
4649 rs_api_impl,
4650 quote! {
4651 fn Function<'a>(s: &'a S) { ... }
4652 }
4653 );
4654 Ok(())
4655 }
4656
4657 /// !Unpin mut references must be pinned.
4658 #[test]
4659 fn test_nonunpin_mut_param() -> Result<()> {
4660 let rs_api_impl = generate_rs_api(&ir_from_cc(
4661 r#"
4662 #pragma clang lifetime_elision
4663 struct S {~S();};
4664 void Function(S& s);
4665 "#,
4666 )?)?;
4667 assert_rs_matches!(
4668 rs_api_impl,
4669 quote! {
Rosica Dejanovskaaccf00b2022-04-01 13:28:04 -07004670 fn Function<'a>(s: rust_std::pin::Pin<&'a mut S>) { ... }
Devin Jeanpierre149950d2022-02-22 21:02:02 +00004671 }
4672 );
4673 Ok(())
4674 }
4675
4676 /// !Unpin &self should not be pinned.
4677 #[test]
4678 fn test_nonunpin_ref_self() -> Result<()> {
4679 let rs_api_impl = generate_rs_api(&ir_from_cc(
4680 r#"
4681 #pragma clang lifetime_elision
4682 struct S {
4683 ~S();
4684 void Function() const;
4685 };
4686 "#,
4687 )?)?;
4688 assert_rs_matches!(
4689 rs_api_impl,
4690 quote! {
4691 fn Function<'a>(&'a self) { ... }
4692 }
4693 );
4694 Ok(())
4695 }
4696
4697 /// !Unpin &mut self must be pinned.
4698 #[test]
4699 fn test_nonunpin_mut_self() -> Result<()> {
4700 let rs_api_impl = generate_rs_api(&ir_from_cc(
4701 r#"
4702 #pragma clang lifetime_elision
4703 struct S {
4704 ~S();
4705 void Function();
4706 };
4707 "#,
4708 )?)?;
4709 assert_rs_matches!(
4710 rs_api_impl,
4711 quote! {
Rosica Dejanovskaaccf00b2022-04-01 13:28:04 -07004712 fn Function<'a>(self: rust_std::pin::Pin<&'a mut Self>) { ... }
Devin Jeanpierre149950d2022-02-22 21:02:02 +00004713 }
4714 );
4715 Ok(())
4716 }
4717
4718 /// Drop::drop must not use self : Pin<...>.
4719 #[test]
4720 fn test_nonunpin_drop() -> Result<()> {
4721 let rs_api_impl = generate_rs_api(&ir_from_cc(
4722 r#"
4723 struct S {~S();};
4724 "#,
4725 )?)?;
4726 assert_rs_matches!(
4727 rs_api_impl,
4728 quote! {
4729 fn drop(&mut self) { ... }
4730 }
4731 );
4732 Ok(())
4733 }
Devin Jeanpierre6f607372022-03-22 21:34:38 +00004734
4735 #[test]
Devin Jeanpierread125742022-04-11 13:50:28 -07004736 fn test_nonunpin_0_arg_constructor() -> Result<()> {
4737 let ir = ir_from_cc(
4738 r#"#pragma clang lifetime_elision
4739 // This type must be `!Unpin`.
4740 struct HasConstructor {explicit HasConstructor() {}};"#,
4741 )?;
4742 let rs_api = generate_rs_api(&ir)?;
4743 assert_rs_matches!(rs_api, quote! {impl !Unpin for HasConstructor {} });
4744 assert_rs_matches!(
4745 rs_api,
4746 quote! {
4747 impl ctor::CtorNew<()> for HasConstructor {
4748 type CtorType = impl ctor::Ctor<Output = Self>;
4749
4750 #[inline (always)]
4751 fn ctor_new(args: ()) -> Self::CtorType {
4752 let () = args;
4753 ctor::FnCtor::new(move |dest: rust_std::pin::Pin<&mut rust_std::mem::MaybeUninit<Self>>| {
4754 unsafe {
Rosica Dejanovskadd9a9032022-04-12 07:34:41 -07004755 detail::__rust_thunk___ZN14HasConstructorC1Ev(rust_std::pin::Pin::into_inner_unchecked(dest));
Devin Jeanpierread125742022-04-11 13:50:28 -07004756 }
4757 })
4758 }
4759 }
4760 }
4761 );
4762 Ok(())
4763 }
4764
4765 #[test]
4766 fn test_nonunpin_1_arg_constructor() -> Result<()> {
Devin Jeanpierre6f607372022-03-22 21:34:38 +00004767 let ir = ir_from_cc(
4768 r#"#pragma clang lifetime_elision
4769 // This type must be `!Unpin`.
4770 struct HasConstructor {explicit HasConstructor(unsigned char input) {}};"#,
4771 )?;
4772 let rs_api = generate_rs_api(&ir)?;
4773 assert_rs_matches!(rs_api, quote! {impl !Unpin for HasConstructor {} });
4774 assert_rs_matches!(
4775 rs_api,
4776 quote! {
4777 impl ctor::CtorNew<u8> for HasConstructor {
4778 type CtorType = impl ctor::Ctor<Output = Self>;
4779
4780 #[inline (always)]
Devin Jeanpierread125742022-04-11 13:50:28 -07004781 fn ctor_new(args: u8) -> Self::CtorType {
4782 let input = args;
Rosica Dejanovskaaccf00b2022-04-01 13:28:04 -07004783 ctor::FnCtor::new(move |dest: rust_std::pin::Pin<&mut rust_std::mem::MaybeUninit<Self>>| {
Devin Jeanpierre6f607372022-03-22 21:34:38 +00004784 unsafe {
Rosica Dejanovskadd9a9032022-04-12 07:34:41 -07004785 detail::__rust_thunk___ZN14HasConstructorC1Eh(rust_std::pin::Pin::into_inner_unchecked(dest), input);
Devin Jeanpierre6f607372022-03-22 21:34:38 +00004786 }
4787 })
4788 }
4789 }
4790 }
4791 );
4792 Ok(())
4793 }
Devin Jeanpierread125742022-04-11 13:50:28 -07004794
4795 #[test]
4796 fn test_nonunpin_2_arg_constructor() -> Result<()> {
4797 let ir = ir_from_cc(
4798 r#"#pragma clang lifetime_elision
4799 // This type must be `!Unpin`.
4800 struct HasConstructor {explicit HasConstructor(unsigned char input1, signed char input2) {}};"#,
4801 )?;
4802 let rs_api = generate_rs_api(&ir)?;
4803 assert_rs_matches!(rs_api, quote! {impl !Unpin for HasConstructor {} });
4804 assert_rs_matches!(
4805 rs_api,
4806 quote! {
4807 impl ctor::CtorNew<(u8, i8)> for HasConstructor {
4808 type CtorType = impl ctor::Ctor<Output = Self>;
4809
4810 #[inline (always)]
4811 fn ctor_new(args: (u8, i8)) -> Self::CtorType {
4812 let (input1, input2) = args;
4813 ctor::FnCtor::new(move |dest: rust_std::pin::Pin<&mut rust_std::mem::MaybeUninit<Self>>| {
4814 unsafe {
Rosica Dejanovskadd9a9032022-04-12 07:34:41 -07004815 detail::__rust_thunk___ZN14HasConstructorC1Eha(rust_std::pin::Pin::into_inner_unchecked(dest), input1, input2);
Devin Jeanpierread125742022-04-11 13:50:28 -07004816 }
4817 })
4818 }
4819 }
4820 }
4821 );
4822 Ok(())
4823 }
Rosica Dejanovskadd9a9032022-04-12 07:34:41 -07004824
4825 #[test]
Devin Jeanpierrec0543eb2022-04-20 16:00:34 -07004826 fn test_forward_declared() -> Result<()> {
4827 let ir = ir_from_cc(
4828 r#"#pragma clang lifetime_elision
4829 struct ForwardDeclared;"#,
4830 )?;
4831 let rs_api = generate_rs_api(&ir)?;
4832 assert_rs_matches!(
4833 rs_api,
4834 quote! {
4835 forward_declare::forward_declare!(pub ForwardDeclared = forward_declare::symbol!("ForwardDeclared"));
4836 }
4837 );
4838 assert_rs_not_matches!(rs_api, quote! {struct ForwardDeclared});
4839 Ok(())
4840 }
4841
4842 #[test]
Rosica Dejanovskadd9a9032022-04-12 07:34:41 -07004843 fn test_namespace_module_items() -> Result<()> {
4844 let rs_api_impl = generate_rs_api(&ir_from_cc(
4845 r#"
4846 namespace test_namespace_bindings {
4847 int func();
4848 struct S {};
4849 namespace inner {
4850 int inner_func();
4851 struct InnerS {};
4852 }
4853 }
4854 "#,
4855 )?)?;
4856 assert_rs_matches!(
4857 rs_api_impl,
4858 quote! {
4859 pub mod test_namespace_bindings {
4860 ...
4861 pub fn func() -> i32 { ... }
4862 ...
4863 pub struct S { ... }
4864 ...
4865 pub mod inner {
4866 ...
4867 pub fn inner_func() -> i32 { ... }
4868 ...
4869 pub struct InnerS { ... }
4870 ...
4871 }
4872 ...
4873 }
4874 }
4875 );
4876 Ok(())
4877 }
4878
4879 #[test]
4880 fn test_namespace_module_contains_detail() -> Result<()> {
4881 let rs_api_impl = generate_rs_api(&ir_from_cc(
4882 r#"
4883 namespace test_namespace_bindings {
4884 int f();
4885 }
4886 "#,
4887 )?)?;
4888 assert_rs_matches!(
4889 rs_api_impl,
4890 quote! {
4891 pub mod test_namespace_bindings {
4892 ...
4893 mod detail {
4894 #[allow(unused_imports)]
4895 use super::*;
4896 extern "C" {
4897 #[link_name = "_ZN23test_namespace_bindings1fEv"]
4898 pub(crate) fn __rust_thunk___ZN23test_namespace_bindings1fEv() -> i32;
4899 }
4900 }
4901 ...
4902 }
4903 }
4904 );
4905 Ok(())
4906 }
4907
4908 #[test]
4909 fn test_namespace_module_contains_assertions() -> Result<()> {
4910 let rs_api_impl = generate_rs_api(&ir_from_cc(
4911 r#"
4912 namespace test_namespace_bindings {
4913 struct S {
4914 int i;
4915 };
4916 }
4917 "#,
4918 )?)?;
4919 assert_rs_matches!(
4920 rs_api_impl,
4921 quote! {
4922 pub mod test_namespace_bindings {
4923 ...
4924 const _: () = assert!(rust_std::mem::size_of::<S>() == 4usize);
4925 const _: () = assert!(rust_std::mem::align_of::<S>() == 4usize);
4926 ...
4927 const _: () = assert!(offset_of!(S, i) * 8 == 0usize);
4928 }
4929 }
4930 );
4931 Ok(())
4932 }
Marcel Hlopko42abfc82021-08-09 07:03:17 +00004933}