blob: 01118a18f023d99c57c1b6ec30855fdc81318e36 [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};
Marcel Hlopko42abfc82021-08-09 07:03:17 +000010use quote::format_ident;
11use quote::quote;
Devin Jeanpierre9886fb42022-04-01 04:31:20 -070012use std::collections::{BTreeSet, HashSet};
Marcel Hlopko42abfc82021-08-09 07:03:17 +000013use std::iter::Iterator;
14use std::panic::catch_unwind;
15use std::process;
Marcel Hlopko65d05f02021-12-09 12:29:24 +000016use token_stream_printer::{rs_tokens_to_formatted_string, tokens_to_string};
Marcel Hlopko42abfc82021-08-09 07:03:17 +000017
Marcel Hlopko45fba972021-08-23 19:52:20 +000018/// FFI equivalent of `Bindings`.
19#[repr(C)]
20pub struct FfiBindings {
21 rs_api: FfiU8SliceBox,
22 rs_api_impl: FfiU8SliceBox,
23}
24
25/// Deserializes IR from `json` and generates bindings source code.
Marcel Hlopko42abfc82021-08-09 07:03:17 +000026///
27/// This function panics on error.
28///
Lukasz Anforowiczdd9ae0f2022-02-17 15:52:53 +000029/// # Safety
30///
31/// Expectations:
32/// * function expects that param `json` is a FfiU8Slice for a valid array of
33/// bytes with the given size.
34/// * function expects that param `json` doesn't change during the call.
35///
Marcel Hlopko42abfc82021-08-09 07:03:17 +000036/// Ownership:
Michael Forsterbee84482021-10-13 08:35:38 +000037/// * function doesn't take ownership of (in other words it borrows) the
38/// param `json`
Marcel Hlopko42abfc82021-08-09 07:03:17 +000039/// * function passes ownership of the returned value to the caller
Marcel Hlopko42abfc82021-08-09 07:03:17 +000040#[no_mangle]
Marcel Hlopko45fba972021-08-23 19:52:20 +000041pub unsafe extern "C" fn GenerateBindingsImpl(json: FfiU8Slice) -> FfiBindings {
Marcel Hlopko42abfc82021-08-09 07:03:17 +000042 catch_unwind(|| {
Marcel Hlopko45fba972021-08-23 19:52:20 +000043 // It is ok to abort here.
44 let Bindings { rs_api, rs_api_impl } = generate_bindings(json.as_slice()).unwrap();
45
46 FfiBindings {
47 rs_api: FfiU8SliceBox::from_boxed_slice(rs_api.into_bytes().into_boxed_slice()),
48 rs_api_impl: FfiU8SliceBox::from_boxed_slice(
49 rs_api_impl.into_bytes().into_boxed_slice(),
50 ),
51 }
Marcel Hlopko42abfc82021-08-09 07:03:17 +000052 })
53 .unwrap_or_else(|_| process::abort())
54}
55
Marcel Hlopko45fba972021-08-23 19:52:20 +000056/// Source code for generated bindings.
57struct Bindings {
58 // Rust source code.
59 rs_api: String,
60 // C++ source code.
61 rs_api_impl: String,
Marcel Hlopko42abfc82021-08-09 07:03:17 +000062}
63
Marcel Hlopko45fba972021-08-23 19:52:20 +000064fn generate_bindings(json: &[u8]) -> Result<Bindings> {
65 let ir = deserialize_ir(json)?;
Marcel Hlopkoca84ff42021-12-09 14:15:14 +000066
67 // The code is formatted with a non-default rustfmt configuration. Prevent
Lukasz Anforowicz5b3f5302022-02-07 01:04:47 +000068 // downstream workflows from reformatting with a different configuration by
69 // marking the output with `@generated`. See also
70 // https://rust-lang.github.io/rustfmt/?version=v1.4.38&search=#format_generated_files
71 //
72 // TODO(lukasza): It would be nice to include "by $argv[0]"" in the
73 // @generated comment below. OTOH, `std::env::current_exe()` in our
74 // current build environment returns a guid-like path... :-/
Lukasz Anforowicz72c4d222022-02-18 19:07:28 +000075 //
76 // TODO(lukasza): Try to remove `#![rustfmt:skip]` - in theory it shouldn't
77 // be needed when `@generated` comment/keyword is present...
Lukasz Anforowicz5b3f5302022-02-07 01:04:47 +000078 let rs_api = format!(
Lukasz Anforowicz72c4d222022-02-18 19:07:28 +000079 "// Automatically @generated Rust bindings for C++ target\n\
80 // {target}\n\
81 #![rustfmt::skip]\n\
82 {code}",
Lukasz Anforowicz5b3f5302022-02-07 01:04:47 +000083 target = ir.current_target().0,
84 code = rs_tokens_to_formatted_string(generate_rs_api(&ir)?)?
85 );
Marcel Hlopko89547752021-12-10 09:39:41 +000086 let rs_api_impl = tokens_to_string(generate_rs_api_impl(&ir)?)?;
Marcel Hlopkoca84ff42021-12-09 14:15:14 +000087
Marcel Hlopko45fba972021-08-23 19:52:20 +000088 Ok(Bindings { rs_api, rs_api_impl })
89}
90
Devin Jeanpierre6d5e7cc2021-10-21 12:56:07 +000091/// Rust source code with attached information about how to modify the parent
92/// crate.
Devin Jeanpierre273eeae2021-10-06 13:29:35 +000093///
Michael Forsterbee84482021-10-13 08:35:38 +000094/// For example, the snippet `vec![].into_raw_parts()` is not valid unless the
95/// `vec_into_raw_parts` feature is enabled. So such a snippet should be
96/// represented as:
Devin Jeanpierre273eeae2021-10-06 13:29:35 +000097///
98/// ```
99/// RsSnippet {
Marcel Hlopkoeaae9b72022-01-21 15:54:11 +0000100/// features: btree_set![make_rs_ident("vec_into_raw_parts")],
Devin Jeanpierre273eeae2021-10-06 13:29:35 +0000101/// tokens: quote!{vec![].into_raw_parts()},
102/// }
103/// ```
Lukasz Anforowiczeb19ac62022-02-05 00:10:16 +0000104#[derive(Clone, Debug)]
Devin Jeanpierre273eeae2021-10-06 13:29:35 +0000105struct RsSnippet {
106 /// Rust feature flags used by this snippet.
107 features: BTreeSet<Ident>,
108 /// The snippet itself, as a token stream.
109 tokens: TokenStream,
110}
111
112impl From<TokenStream> for RsSnippet {
113 fn from(tokens: TokenStream) -> Self {
114 RsSnippet { features: BTreeSet::new(), tokens }
115 }
116}
117
Michael Forsterbee84482021-10-13 08:35:38 +0000118/// If we know the original C++ function is codegenned and already compatible
119/// with `extern "C"` calling convention we skip creating/calling the C++ thunk
120/// since we can call the original C++ directly.
Marcel Hlopko3164eee2021-08-24 20:09:22 +0000121fn can_skip_cc_thunk(func: &Func) -> bool {
Devin Jeanpierre96839c12021-12-14 00:27:38 +0000122 // ## Inline functions
123 //
Michael Forsterbee84482021-10-13 08:35:38 +0000124 // Inline functions may not be codegenned in the C++ library since Clang doesn't
125 // know if Rust calls the function or not. Therefore in order to make inline
126 // functions callable from Rust we need to generate a C++ file that defines
127 // a thunk that delegates to the original inline function. When compiled,
128 // Clang will emit code for this thunk and Rust code will call the
Marcel Hlopko3164eee2021-08-24 20:09:22 +0000129 // thunk when the user wants to call the original inline function.
130 //
Michael Forsterbee84482021-10-13 08:35:38 +0000131 // This is not great runtime-performance-wise in regular builds (inline function
132 // will not be inlined, there will always be a function call), but it is
133 // correct. ThinLTO builds will be able to see through the thunk and inline
134 // code across the language boundary. For non-ThinLTO builds we plan to
135 // implement <internal link> which removes the runtime performance overhead.
Devin Jeanpierre96839c12021-12-14 00:27:38 +0000136 if func.is_inline {
137 return false;
138 }
139 // ## Virtual functions
140 //
141 // When calling virtual `A::Method()`, it's not necessarily the case that we'll
142 // specifically call the concrete `A::Method` impl. For example, if this is
143 // called on something whose dynamic type is some subclass `B` with an
144 // overridden `B::Method`, then we'll call that.
145 //
146 // We must reuse the C++ dynamic dispatching system. In this case, the easiest
147 // way to do it is by resorting to a C++ thunk, whose implementation will do
148 // the lookup.
149 //
150 // In terms of runtime performance, since this only occurs for virtual function
151 // calls, which are already slow, it may not be such a big deal. We can
152 // benchmark it later. :)
153 if let Some(meta) = &func.member_func_metadata {
154 if let Some(inst_meta) = &meta.instance_method_metadata {
155 if inst_meta.is_virtual {
156 return false;
157 }
158 }
159 }
Lukasz Anforowicz0b6a6ac2022-03-22 22:32:23 +0000160 // ## Custom calling convention requires a thunk.
161 //
162 // The thunk has the "C" calling convention, and internally can call the
163 // C++ function using any of the calling conventions supported by the C++
164 // compiler (which might not always match the set supported by Rust - e.g.,
165 // abi.rs doesn't contain "swiftcall" from
166 // clang::FunctionType::getNameForCallConv)
167 if !func.has_c_calling_convention {
168 return false;
169 }
Devin Jeanpierre96839c12021-12-14 00:27:38 +0000170
171 true
Marcel Hlopko3164eee2021-08-24 20:09:22 +0000172}
173
Googlerd03d05b2022-01-07 10:10:57 +0000174/// Uniquely identifies a generated Rust function.
Lukasz Anforowiczeb19ac62022-02-05 00:10:16 +0000175#[derive(Clone, Debug, PartialEq, Eq, Hash)]
Googlerd03d05b2022-01-07 10:10:57 +0000176struct FunctionId {
177 // If the function is on a trait impl, contains the name of the Self type for
178 // which the trait is being implemented.
179 self_type: Option<syn::Path>,
180 // Fully qualified path of the function. For functions in impl blocks, this
181 // includes the name of the type or trait on which the function is being
182 // implemented, e.g. `Default::default`.
183 function_path: syn::Path,
184}
185
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +0000186/// Returns the name of `func` in C++ syntax.
Googlerd03d05b2022-01-07 10:10:57 +0000187fn cxx_function_name(func: &Func, ir: &IR) -> Result<String> {
188 let record: Option<&str> = func
189 .member_func_metadata
190 .as_ref()
191 .map(|meta| meta.find_record(ir))
192 .transpose()?
Lukasz Anforowicz4c3a2cc2022-03-11 00:24:49 +0000193 .map(|r| &*r.cc_name);
Googlerd03d05b2022-01-07 10:10:57 +0000194
195 let func_name = match &func.name {
196 UnqualifiedIdentifier::Identifier(id) => id.identifier.clone(),
Lukasz Anforowicz9c663ca2022-02-09 01:33:31 +0000197 UnqualifiedIdentifier::Operator(op) => op.cc_name(),
Googlerd03d05b2022-01-07 10:10:57 +0000198 UnqualifiedIdentifier::Destructor => {
199 format!("~{}", record.expect("destructor must be associated with a record"))
200 }
201 UnqualifiedIdentifier::Constructor => {
Lukasz Anforowiczeb19ac62022-02-05 00:10:16 +0000202 record.expect("constructor must be associated with a record").to_string()
Googlerd03d05b2022-01-07 10:10:57 +0000203 }
204 };
205
206 if let Some(record_name) = record {
207 Ok(format!("{}::{}", record_name, func_name))
208 } else {
209 Ok(func_name)
210 }
211}
212
Lukasz Anforowiczeb19ac62022-02-05 00:10:16 +0000213fn make_unsupported_fn(func: &Func, ir: &IR, message: impl ToString) -> Result<UnsupportedItem> {
214 Ok(UnsupportedItem {
215 name: cxx_function_name(func, ir)?,
216 message: message.to_string(),
217 source_loc: func.source_loc.clone(),
Rosica Dejanovskad638cf52022-03-23 15:45:01 +0000218 id: func.id,
Lukasz Anforowiczeb19ac62022-02-05 00:10:16 +0000219 })
220}
221
Devin Jeanpierre9c34da62022-03-31 04:57:16 -0700222/// The name of a trait, with extra entries for specially-understood traits and
223/// families of traits.
224enum TraitName {
225 /// The constructor trait for !Unpin types. e.g. `CtorNew(quote! { ()
226 /// })` is the default constructor.
227 CtorNew(TokenStream),
228 /// An Unpin constructor trait, e.g. From or Clone.
229 UnpinConstructor(TokenStream),
230 /// Any other trait, e.g. Eq.
231 Other(TokenStream),
232}
233impl quote::ToTokens for TraitName {
234 fn to_tokens(&self, tokens: &mut TokenStream) {
235 match self {
236 Self::UnpinConstructor(t) | Self::Other(t) => t.to_tokens(tokens),
237 Self::CtorNew(args) => quote! { ctor::CtorNew < #args > }.to_tokens(tokens),
238 }
239 }
240}
241
242/// The kind of the `impl` block the function needs to be generated in.
243enum ImplKind {
244 /// Used for free functions for which we don't want the `impl` block.
245 None { is_unsafe: bool },
246 /// Used for inherent methods for which we need an `impl SomeStruct { ... }`
247 /// block.
248 Struct {
249 /// For example, `SomeStruct`. Retrieved from
250 /// `func.member_func_metadata`.
251 record_name: Ident,
252 is_unsafe: bool,
253 /// Whether to format the first parameter as "self" (e.g. `__this:
254 /// &mut T` -> `&mut self`)
255 format_first_param_as_self: bool,
256 },
257 /// Used for trait methods for which we need an `impl TraitName for
258 /// SomeStruct { ... }` block.
259 Trait {
260 /// For example, `SomeStruct`.
261 /// Note that `record_name` might *not* be from
262 /// `func.member_func_metadata`.
263 record_name: Ident,
264 /// For example, `quote!{ From<i32> }`.
265 trait_name: TraitName,
266
267 /// Where to declare lifetimes: `impl<'b>` VS `fn foo<'b>`.
268 declare_lifetimes: bool,
269 /// The generic params of trait `impl` (e.g. `<'b>`). These start
270 /// empty and only later are mutated into the correct value.
271 trait_generic_params: TokenStream,
272 /// Whether to format the first parameter as "self" (e.g. `__this:
273 /// &mut T` -> `&mut self`)
274 format_first_param_as_self: bool,
275 },
276}
277impl ImplKind {
278 fn new_trait(
279 trait_name: TraitName,
280 record_name: Ident,
281 format_first_param_as_self: bool,
282 ) -> Self {
283 ImplKind::Trait {
284 trait_name,
285 record_name,
286 declare_lifetimes: false,
287 trait_generic_params: quote! {},
288 format_first_param_as_self,
289 }
290 }
291 fn new_generic_trait(
292 trait_name: TraitName,
293 record_name: Ident,
294 format_first_param_as_self: bool,
295 ) -> Self {
296 ImplKind::Trait {
297 trait_name,
298 record_name,
299 declare_lifetimes: true,
300 trait_generic_params: quote! {},
301 format_first_param_as_self,
302 }
303 }
304 fn format_first_param_as_self(&self) -> bool {
305 matches!(
306 self,
307 Self::Trait { format_first_param_as_self: true, .. }
308 | Self::Struct { format_first_param_as_self: true, .. }
309 )
310 }
311 /// Returns whether the function is defined as `unsafe fn ...`.
312 fn is_unsafe(&self) -> bool {
313 matches!(self, Self::None { is_unsafe: true, .. } | Self::Struct { is_unsafe: true, .. })
314 }
315}
316
317/// Returns the shape of the generated Rust API for a given function definition.
Devin Jeanpierred7b48102022-03-31 04:15:03 -0700318///
Devin Jeanpierre9c34da62022-03-31 04:57:16 -0700319/// Returns:
Devin Jeanpierred7b48102022-03-31 04:15:03 -0700320///
321/// * `Err(_)`: something went wrong importing this function.
322/// * `Ok(None)`: the function imported as "nothing". (For example, a defaulted
323/// destructor might be mapped to no `Drop` impl at all.)
Devin Jeanpierre9c34da62022-03-31 04:57:16 -0700324/// * `Ok((func_name, impl_kind))`: The function name and ImplKind.
325fn api_func_shape(
326 func: &Func,
327 ir: &IR,
328 param_type_kinds: &[RsTypeKind],
Devin Jeanpierre9c34da62022-03-31 04:57:16 -0700329) -> Result<Option<(Ident, ImplKind)>> {
Lukasz Anforowiczab65e292022-01-14 23:04:21 +0000330 let maybe_record: Option<&Record> =
Lukasz Anforowicz13cf7492021-12-22 15:29:52 +0000331 func.member_func_metadata.as_ref().map(|meta| meta.find_record(ir)).transpose()?;
Devin Jeanpierre9c34da62022-03-31 04:57:16 -0700332 let has_pointer_params =
333 param_type_kinds.iter().any(|p| matches!(p, RsTypeKind::Pointer { .. }));
334 let impl_kind: ImplKind;
Lukasz Anforowiczab65e292022-01-14 23:04:21 +0000335 let func_name: syn::Ident;
Googlerd03d05b2022-01-07 10:10:57 +0000336 match &func.name {
Lukasz Anforowicz9c663ca2022-02-09 01:33:31 +0000337 UnqualifiedIdentifier::Operator(op) if op.name == "==" => {
Lukasz Anforowiczeb19ac62022-02-05 00:10:16 +0000338 if param_type_kinds.len() != 2 {
339 bail!("Unexpected number of parameters in operator==: {:?}", func);
340 }
341 match (&param_type_kinds[0], &param_type_kinds[1]) {
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +0000342 (
Lukasz Anforowiczeb19ac62022-02-05 00:10:16 +0000343 RsTypeKind::Reference { referent: lhs, mutability: Mutability::Const, .. },
344 RsTypeKind::Reference { referent: rhs, mutability: Mutability::Const, .. },
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +0000345 ) => match **lhs {
346 RsTypeKind::Record(lhs_record) => {
Lukasz Anforowicz4c3a2cc2022-03-11 00:24:49 +0000347 let lhs: Ident = make_rs_ident(&lhs_record.rs_name);
Devin Jeanpierred2b7a8f2022-04-01 04:37:16 -0700348 let rhs: TokenStream = rhs.format(ir);
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() {
Lukasz Anforowiczf9564622022-01-28 14:31:04 +0000380 let first_param = param_type_kinds.first().ok_or_else(|| {
381 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
435 match func.params.len() {
436 0 => bail!("Missing `__this` parameter in a constructor: {:?}", func),
437 2 => {
Devin Jeanpierred2b7a8f2022-04-01 04:37:16 -0700438 let param_type = param_type_kinds[1].format(ir);
Lukasz Anforowicz5e623782022-03-14 16:52:23 +0000439 impl_kind = ImplKind::new_generic_trait(
Devin Jeanpierre6f607372022-03-22 21:34:38 +0000440 TraitName::CtorNew(param_type.clone()),
Devin Jeanpierre1b8f4a12022-03-22 21:29:42 +0000441 record_name,
Devin Jeanpierred9a6e6c2022-03-29 02:55:41 -0700442 /* format_first_param_as_self= */ false,
Devin Jeanpierre1b8f4a12022-03-22 21:29:42 +0000443 );
Devin Jeanpierre6f607372022-03-22 21:34:38 +0000444 }
445 _ => {
446 // TODO(b/216648347): Support bindings for other constructors.
Devin Jeanpierred7b48102022-03-31 04:15:03 -0700447 bail!(
Devin Jeanpierre6f607372022-03-22 21:34:38 +0000448 "Only single-parameter constructors for T: !Unpin are supported for now",
Marcel Hlopko14ee3c82022-02-09 09:46:23 +0000449 );
Lukasz Anforowicz73326af2022-01-05 01:13:10 +0000450 }
451 }
Devin Jeanpierre6f607372022-03-22 21:34:38 +0000452 } else {
453 match func.params.len() {
454 0 => bail!("Missing `__this` parameter in a constructor: {:?}", func),
455 1 => {
456 impl_kind = ImplKind::new_trait(
457 TraitName::UnpinConstructor(quote! {Default}),
458 record_name,
Devin Jeanpierred9a6e6c2022-03-29 02:55:41 -0700459 /* format_first_param_as_self= */ false,
Devin Jeanpierre6f607372022-03-22 21:34:38 +0000460 );
461 func_name = make_rs_ident("default");
Devin Jeanpierre6f607372022-03-22 21:34:38 +0000462 }
463 2 => {
Devin Jeanpierre6f607372022-03-22 21:34:38 +0000464 if param_type_kinds[1].is_shared_ref_to(record) {
465 // Copy constructor
466 if should_derive_clone(record) {
Devin Jeanpierred7b48102022-03-31 04:15:03 -0700467 return Ok(None);
Devin Jeanpierre6f607372022-03-22 21:34:38 +0000468 } else {
469 impl_kind = ImplKind::new_trait(
470 TraitName::UnpinConstructor(quote! {Clone}),
471 record_name,
Devin Jeanpierred9a6e6c2022-03-29 02:55:41 -0700472 /* format_first_param_as_self= */ true,
Devin Jeanpierre6f607372022-03-22 21:34:38 +0000473 );
474 func_name = make_rs_ident("clone");
Devin Jeanpierre6f607372022-03-22 21:34:38 +0000475 }
476 } else if !instance_method_metadata.is_explicit_ctor {
Devin Jeanpierred2b7a8f2022-04-01 04:37:16 -0700477 let param_type = param_type_kinds[1].format(ir);
Devin Jeanpierre6f607372022-03-22 21:34:38 +0000478 impl_kind = ImplKind::new_generic_trait(
479 TraitName::UnpinConstructor(quote! {From< #param_type >}),
480 record_name,
Devin Jeanpierred9a6e6c2022-03-29 02:55:41 -0700481 /* format_first_param_as_self= */ false,
Devin Jeanpierre6f607372022-03-22 21:34:38 +0000482 );
483 func_name = make_rs_ident("from");
Devin Jeanpierre6f607372022-03-22 21:34:38 +0000484 } else {
Devin Jeanpierred7b48102022-03-31 04:15:03 -0700485 bail!("Not yet supported type of constructor parameter",);
Devin Jeanpierre6f607372022-03-22 21:34:38 +0000486 }
487 }
488 _ => {
489 // TODO(b/216648347): Support bindings for other constructors.
Devin Jeanpierred7b48102022-03-31 04:15:03 -0700490 bail!("More than 1 constructor parameter is not supported yet",);
Devin Jeanpierre6f607372022-03-22 21:34:38 +0000491 }
Lukasz Anforowicz73326af2022-01-05 01:13:10 +0000492 }
Lukasz Anforowiczab65e292022-01-14 23:04:21 +0000493 }
494 }
495 }
Devin Jeanpierre9c34da62022-03-31 04:57:16 -0700496 Ok(Some((func_name, impl_kind)))
497}
498
499/// Generates Rust source code for a given `Func`.
500///
501/// Returns:
502///
503/// * `Err(_)`: couldn't import the function, emit an `UnsupportedItem`.
504/// * `Ok(None)`: the function imported as "nothing". (For example, a defaulted
505/// destructor might be mapped to no `Drop` impl at all.)
506/// * `Ok((rs_api, rs_thunk, function_id))`: The Rust function definition,
507/// thunk FFI definition, and function ID.
508fn generate_func(func: &Func, ir: &IR) -> Result<Option<(RsSnippet, RsSnippet, FunctionId)>> {
Devin Jeanpierre9c34da62022-03-31 04:57:16 -0700509 let param_type_kinds = func
510 .params
511 .iter()
512 .map(|p| {
513 RsTypeKind::new(&p.type_.rs_type, ir).with_context(|| {
514 format!("Failed to process type of parameter {:?} on {:?}", p, func)
515 })
516 })
517 .collect::<Result<Vec<_>>>()?;
518
519 let (func_name, mut impl_kind) =
Devin Jeanpierre9886fb42022-04-01 04:31:20 -0700520 if let Some(values) = api_func_shape(func, ir, &param_type_kinds)? {
Devin Jeanpierre9c34da62022-03-31 04:57:16 -0700521 values
522 } else {
523 return Ok(None);
524 };
Lukasz Anforowiczab65e292022-01-14 23:04:21 +0000525
Devin Jeanpierred9cecff2022-03-29 02:53:58 -0700526 let return_type_fragment = RsTypeKind::new(&func.return_type.rs_type, ir)
Devin Jeanpierred2b7a8f2022-04-01 04:37:16 -0700527 .map(|t| t.format_as_return_type_fragment(ir))
Devin Jeanpierred9cecff2022-03-29 02:53:58 -0700528 .with_context(|| format!("Failed to format return type for {:?}", func))?;
529 let param_idents =
530 func.params.iter().map(|p| make_rs_ident(&p.identifier.identifier)).collect_vec();
Devin Jeanpierred2b7a8f2022-04-01 04:37:16 -0700531 let param_types: Vec<_> = param_type_kinds.iter().map(|t| t.format(ir)).collect();
Devin Jeanpierred9cecff2022-03-29 02:53:58 -0700532 let thunk_ident = thunk_ident(func);
533
Lukasz Anforowiczab65e292022-01-14 23:04:21 +0000534 let api_func_def = {
Lukasz Anforowicz95551272022-01-20 00:02:24 +0000535 // Clone params, return type, etc - we may need to mutate them in the
536 // API func, but we want to retain the originals for the thunk.
Lukasz Anforowiczab65e292022-01-14 23:04:21 +0000537 let mut return_type_fragment = return_type_fragment.clone();
538 let mut thunk_args = param_idents.iter().map(|id| quote! { #id}).collect_vec();
539 let mut api_params = param_idents
540 .iter()
541 .zip(param_types.iter())
542 .map(|(ident, type_)| quote! { #ident : #type_ })
543 .collect_vec();
Lukasz Anforowicz95551272022-01-20 00:02:24 +0000544 let mut lifetimes = func.lifetime_params.iter().collect_vec();
Lukasz Anforowiczf7bdd392022-01-21 00:33:39 +0000545 let mut maybe_first_api_param = param_type_kinds.get(0);
Lukasz Anforowiczab65e292022-01-14 23:04:21 +0000546
Devin Jeanpierre6f607372022-03-22 21:34:38 +0000547 if let ImplKind::Trait {
548 trait_name: TraitName::UnpinConstructor(..) | TraitName::CtorNew(..),
549 ..
550 } = impl_kind
551 {
Lukasz Anforowiczab65e292022-01-14 23:04:21 +0000552 return_type_fragment = quote! { -> Self };
553
Lukasz Anforowiczf9564622022-01-28 14:31:04 +0000554 // Drop `__this` parameter from the public Rust API. Presence of
555 // element #0 is indirectly verified by a `Constructor`-related
556 // `match` branch a little bit above.
557 api_params.remove(0);
558 thunk_args.remove(0);
Lukasz Anforowicz95551272022-01-20 00:02:24 +0000559
Lukasz Anforowicz326c4e42022-01-27 14:43:00 +0000560 // Remove the lifetime associated with `__this`.
Devin Jeanpierre1b8f4a12022-03-22 21:29:42 +0000561 ensure!(
562 func.return_type.rs_type.is_unit_type(),
563 "Unexpectedly non-void return type of a constructor: {:?}",
564 func
565 );
Lukasz Anforowiczf7bdd392022-01-21 00:33:39 +0000566 let maybe_first_lifetime = func.params[0].type_.rs_type.lifetime_args.first();
Lukasz Anforowicz55673c92022-01-27 19:37:26 +0000567 let no_longer_needed_lifetime_id = maybe_first_lifetime
568 .ok_or_else(|| anyhow!("Missing lifetime on `__this` parameter: {:?}", func))?;
569 lifetimes.retain(|l| l.id != *no_longer_needed_lifetime_id);
Lukasz Anforowicz55673c92022-01-27 19:37:26 +0000570 if let Some(type_still_dependent_on_removed_lifetime) = param_type_kinds
571 .iter()
572 .skip(1) // Skipping `__this`
Lukasz Anforowicz90bdb962022-02-14 21:07:45 +0000573 .flat_map(|t| t.lifetimes())
574 .find(|lifetime_id| *lifetime_id == *no_longer_needed_lifetime_id)
Lukasz Anforowicz55673c92022-01-27 19:37:26 +0000575 {
576 bail!(
577 "The lifetime of `__this` is unexpectedly also used by another \
578 parameter {:?} in function {:?}",
579 type_still_dependent_on_removed_lifetime,
580 func.name
581 );
Lukasz Anforowicz95551272022-01-20 00:02:24 +0000582 }
583
584 // Rebind `maybe_first_api_param` to the next param after `__this`.
Lukasz Anforowiczf7bdd392022-01-21 00:33:39 +0000585 maybe_first_api_param = param_type_kinds.get(1);
Lukasz Anforowiczab65e292022-01-14 23:04:21 +0000586 }
587
Devin Jeanpierre6f607372022-03-22 21:34:38 +0000588 if let ImplKind::Trait { trait_name: TraitName::CtorNew(..), .. } = impl_kind {
589 return_type_fragment = quote! { -> Self::CtorType };
590 }
591
Lukasz Anforowiczab65e292022-01-14 23:04:21 +0000592 // Change `__this: &'a SomeStruct` into `&'a self` if needed.
Devin Jeanpierred9a6e6c2022-03-29 02:55:41 -0700593 if impl_kind.format_first_param_as_self() {
Lukasz Anforowiczab65e292022-01-14 23:04:21 +0000594 let first_api_param = maybe_first_api_param
595 .ok_or_else(|| anyhow!("No parameter to format as 'self': {:?}", func))?;
Lukasz Anforowiczcde4b1b2022-02-03 21:20:55 +0000596 let self_decl =
Devin Jeanpierre9886fb42022-04-01 04:31:20 -0700597 first_api_param.format_as_self_param(func, ir).with_context(
Lukasz Anforowiczcde4b1b2022-02-03 21:20:55 +0000598 || format!("Failed to format as `self` param: {:?}", first_api_param),
599 )?;
Lukasz Anforowiczf9564622022-01-28 14:31:04 +0000600 // Presence of element #0 is verified by `ok_or_else` on
601 // `maybe_first_api_param` above.
602 api_params[0] = self_decl;
603 thunk_args[0] = quote! { self };
Lukasz Anforowiczab65e292022-01-14 23:04:21 +0000604 }
605
Devin Jeanpierre6f607372022-03-22 21:34:38 +0000606 // TODO(b/200067242): the Pin-wrapping code doesn't know to wrap &mut
607 // MaybeUninit<T> in Pin if T is !Unpin. It should understand
608 // 'structural pinning', so that we do not need into_inner_unchecked()
609 // here.
Devin Jeanpierre7bddfdb2022-03-14 11:04:40 +0000610 let func_body = match &impl_kind {
Devin Jeanpierre6f607372022-03-22 21:34:38 +0000611 ImplKind::Trait { trait_name: TraitName::CtorNew(..), .. } => {
Devin Jeanpierredeea7892022-03-29 02:13:31 -0700612 // TODO(b/226447239): check for copy here and instead use copies in that case?
Devin Jeanpierre6f607372022-03-22 21:34:38 +0000613 quote! {
Rosica Dejanovskaaccf00b2022-04-01 13:28:04 -0700614 ctor::FnCtor::new(move |dest: rust_std::pin::Pin<&mut rust_std::mem::MaybeUninit<Self>>| {
Devin Jeanpierre6f607372022-03-22 21:34:38 +0000615 unsafe {
Rosica Dejanovskaaccf00b2022-04-01 13:28:04 -0700616 crate::detail::#thunk_ident(rust_std::pin::Pin::into_inner_unchecked(dest) #( , #thunk_args )*);
Devin Jeanpierre6f607372022-03-22 21:34:38 +0000617 }
618 })
619 }
620 }
Devin Jeanpierre1b8f4a12022-03-22 21:29:42 +0000621 ImplKind::Trait { trait_name: TraitName::UnpinConstructor(..), .. } => {
Lukasz Anforowiczab65e292022-01-14 23:04:21 +0000622 // SAFETY: A user-defined constructor is not guaranteed to
623 // initialize all the fields. To make the `assume_init()` call
624 // below safe, the memory is zero-initialized first. This is a
625 // bit safer, because zero-initialized memory represents a valid
626 // value for the currently supported field types (this may
627 // change once the bindings generator starts supporting
628 // reference fields). TODO(b/213243309): Double-check if
629 // zero-initialization is desirable here.
630 quote! {
Rosica Dejanovskaaccf00b2022-04-01 13:28:04 -0700631 let mut tmp = rust_std::mem::MaybeUninit::<Self>::zeroed();
Lukasz Anforowiczab65e292022-01-14 23:04:21 +0000632 unsafe {
633 crate::detail::#thunk_ident( &mut tmp #( , #thunk_args )* );
634 tmp.assume_init()
Lukasz Anforowicz13cf7492021-12-22 15:29:52 +0000635 }
Lukasz Anforowicz13cf7492021-12-22 15:29:52 +0000636 }
Lukasz Anforowiczab65e292022-01-14 23:04:21 +0000637 }
Devin Jeanpierre7bddfdb2022-03-14 11:04:40 +0000638 _ => {
639 let mut body = quote! { crate::detail::#thunk_ident( #( #thunk_args ),* ) };
640 // Only need to wrap everything in an `unsafe { ... }` block if
641 // the *whole* api function is safe.
Devin Jeanpierre7c3d8ed2022-03-29 03:02:04 -0700642 if !impl_kind.is_unsafe() {
Devin Jeanpierre7bddfdb2022-03-14 11:04:40 +0000643 body = quote! { unsafe { #body } };
644 }
645 body
646 }
Lukasz Anforowiczab65e292022-01-14 23:04:21 +0000647 };
648
Devin Jeanpierre7c3d8ed2022-03-29 03:02:04 -0700649 let pub_ = match impl_kind {
650 ImplKind::None { .. } | ImplKind::Struct { .. } => quote! { pub },
651 ImplKind::Trait { .. } => quote! {},
652 };
653 let unsafe_ = if impl_kind.is_unsafe() {
654 quote! { unsafe }
655 } else {
656 quote! {}
Lukasz Anforowiczab65e292022-01-14 23:04:21 +0000657 };
658
Lukasz Anforowicz95551272022-01-20 00:02:24 +0000659 let lifetimes = lifetimes.into_iter().map(|l| format_lifetime_name(&l.name));
Lukasz Anforowicz5e623782022-03-14 16:52:23 +0000660 let fn_generic_params: TokenStream;
Devin Jeanpierre1b8f4a12022-03-22 21:29:42 +0000661 if let ImplKind::Trait { declare_lifetimes: true, trait_generic_params, .. } =
662 &mut impl_kind
663 {
Lukasz Anforowicz5e623782022-03-14 16:52:23 +0000664 *trait_generic_params = format_generic_params(lifetimes);
Devin Jeanpierre1b8f4a12022-03-22 21:29:42 +0000665 fn_generic_params = quote! {}
Lukasz Anforowicz5e623782022-03-14 16:52:23 +0000666 } else {
667 fn_generic_params = format_generic_params(lifetimes);
668 }
Lukasz Anforowicz95551272022-01-20 00:02:24 +0000669
Lukasz Anforowiczab65e292022-01-14 23:04:21 +0000670 quote! {
671 #[inline(always)]
Lukasz Anforowicz5e623782022-03-14 16:52:23 +0000672 #pub_ #unsafe_ fn #func_name #fn_generic_params(
673 #( #api_params ),* ) #return_type_fragment {
Lukasz Anforowiczab65e292022-01-14 23:04:21 +0000674 #func_body
675 }
Lukasz Anforowicz13cf7492021-12-22 15:29:52 +0000676 }
Michael Forstered642022021-10-04 09:48:25 +0000677 };
678
Devin Jeanpierred9cecff2022-03-29 02:53:58 -0700679 let doc_comment = generate_doc_comment(&func.doc_comment);
680 let mut features = BTreeSet::new();
Lukasz Anforowiczab65e292022-01-14 23:04:21 +0000681 let api_func: TokenStream;
682 let function_id: FunctionId;
Lukasz Anforowiczab65e292022-01-14 23:04:21 +0000683 match impl_kind {
Devin Jeanpierre7c3d8ed2022-03-29 03:02:04 -0700684 ImplKind::None { .. } => {
Lukasz Anforowiczab65e292022-01-14 23:04:21 +0000685 api_func = quote! { #doc_comment #api_func_def };
686 function_id = FunctionId { self_type: None, function_path: func_name.into() };
687 }
Devin Jeanpierre6784e5e2022-03-29 02:59:01 -0700688 ImplKind::Struct { record_name, .. } => {
Lukasz Anforowiczab65e292022-01-14 23:04:21 +0000689 api_func = quote! { impl #record_name { #doc_comment #api_func_def } };
690 function_id = FunctionId {
691 self_type: None,
692 function_path: syn::parse2(quote! { #record_name :: #func_name })?,
693 };
694 }
Lukasz Anforowicz5e623782022-03-14 16:52:23 +0000695 ImplKind::Trait { trait_name, record_name, trait_generic_params, .. } => {
Devin Jeanpierre6f607372022-03-22 21:34:38 +0000696 let extra_items;
697 match trait_name {
698 TraitName::CtorNew(..) => {
699 // This feature seems destined for stabilization, and makes the code
700 // simpler.
701 features.insert(make_rs_ident("type_alias_impl_trait"));
702 extra_items = quote! {type CtorType = impl ctor::Ctor<Output = Self>;};
703 }
704 _ => {
705 extra_items = quote! {};
706 }
707 };
Lukasz Anforowicz5e623782022-03-14 16:52:23 +0000708 api_func = quote! {
709 #doc_comment
710 impl #trait_generic_params #trait_name for #record_name {
Devin Jeanpierre6f607372022-03-22 21:34:38 +0000711 #extra_items
Lukasz Anforowicz5e623782022-03-14 16:52:23 +0000712 #api_func_def
713 }
714 };
Lukasz Anforowiczab65e292022-01-14 23:04:21 +0000715 function_id = FunctionId {
716 self_type: Some(record_name.into()),
717 function_path: syn::parse2(quote! { #trait_name :: #func_name })?,
718 };
719 }
720 }
721
Lukasz Anforowicz6d553632022-01-06 21:36:14 +0000722 let thunk = {
Devin Jeanpierre7e9a1de2021-12-03 08:04:22 +0000723 let thunk_attr = if can_skip_cc_thunk(func) {
Devin Jeanpierred9cecff2022-03-29 02:53:58 -0700724 let mangled_name = &func.mangled_name;
Devin Jeanpierre7e9a1de2021-12-03 08:04:22 +0000725 quote! {#[link_name = #mangled_name]}
726 } else {
727 quote! {}
728 };
729
Lukasz Anforowicz40c2eb82022-01-11 18:22:31 +0000730 // For constructors inject MaybeUninit into the type of `__this_` parameter.
731 let mut param_types = param_types;
732 if func.name == UnqualifiedIdentifier::Constructor {
733 if param_types.is_empty() || func.params.is_empty() {
734 bail!("Constructors should have at least one parameter (__this)");
735 }
Lukasz Anforowiczf7bdd392022-01-21 00:33:39 +0000736 param_types[0] = param_type_kinds[0]
Devin Jeanpierre9886fb42022-04-01 04:31:20 -0700737 .format_mut_ref_as_uninitialized(ir)
Lukasz Anforowicz231a3bb2022-01-12 14:05:59 +0000738 .with_context(|| {
Devin Jeanpierre1b8f4a12022-03-22 21:29:42 +0000739 format!(
740 "Failed to format `__this` param for a constructor thunk: {:?}",
741 func.params[0]
742 )
Devin Jeanpierre149950d2022-02-22 21:02:02 +0000743 })?;
744 } else if func.name == UnqualifiedIdentifier::Destructor {
745 if param_types.is_empty() || func.params.is_empty() {
746 bail!("Destructors should have at least one parameter (__this)");
747 }
748 param_types[0] = param_type_kinds[0]
Devin Jeanpierre9886fb42022-04-01 04:31:20 -0700749 .format_ref_as_raw_ptr(ir)
Devin Jeanpierre149950d2022-02-22 21:02:02 +0000750 .with_context(|| {
Devin Jeanpierre1b8f4a12022-03-22 21:29:42 +0000751 format!(
752 "Failed to format `__this` param for a destructor thunk: {:?}",
753 func.params[0]
754 )
Lukasz Anforowicz231a3bb2022-01-12 14:05:59 +0000755 })?;
Lukasz Anforowicz40c2eb82022-01-11 18:22:31 +0000756 }
757
Lukasz Anforowicz95551272022-01-20 00:02:24 +0000758 let lifetimes = func.lifetime_params.iter().map(|l| format_lifetime_name(&l.name));
759 let generic_params = format_generic_params(lifetimes);
760
Devin Jeanpierre7e9a1de2021-12-03 08:04:22 +0000761 quote! {
762 #thunk_attr
Lukasz Anforowicz4ad012b2021-12-15 18:13:40 +0000763 pub(crate) fn #thunk_ident #generic_params( #( #param_idents: #param_types ),*
764 ) #return_type_fragment ;
Devin Jeanpierre7e9a1de2021-12-03 08:04:22 +0000765 }
Michael Forstered642022021-10-04 09:48:25 +0000766 };
767
Devin Jeanpierred7b48102022-03-31 04:15:03 -0700768 Ok(Some((RsSnippet { features, tokens: api_func }, thunk.into(), function_id)))
Michael Forstered642022021-10-04 09:48:25 +0000769}
770
Michael Forstercc5941a2021-10-07 07:12:24 +0000771fn generate_doc_comment(comment: &Option<String>) -> TokenStream {
772 match comment {
Michael Forster028800b2021-10-05 12:39:59 +0000773 Some(text) => {
Marcel Hlopko89547752021-12-10 09:39:41 +0000774 // token_stream_printer (and rustfmt) don't put a space between /// and the doc
775 // comment, let's add it here so our comments are pretty.
Lukasz Anforowiczdd9ae0f2022-02-17 15:52:53 +0000776 let doc = format!(" {}", text.replace('\n', "\n "));
Michael Forster028800b2021-10-05 12:39:59 +0000777 quote! {#[doc=#doc]}
778 }
779 None => quote! {},
Michael Forstercc5941a2021-10-07 07:12:24 +0000780 }
781}
Lukasz Anforowiczdaff0402021-12-23 00:37:50 +0000782
783fn format_generic_params<T: quote::ToTokens>(params: impl IntoIterator<Item = T>) -> TokenStream {
784 let mut params = params.into_iter().peekable();
785 if params.peek().is_none() {
786 quote! {}
787 } else {
788 quote! { < #( #params ),* > }
789 }
790}
791
Lukasz Anforowicze57215c2022-01-12 14:54:16 +0000792fn should_implement_drop(record: &Record) -> bool {
793 match record.destructor.definition {
794 // TODO(b/202258760): Only omit destructor if `Copy` is specified.
795 SpecialMemberDefinition::Trivial => false,
796
797 // TODO(b/212690698): Avoid calling into the C++ destructor (e.g. let
798 // Rust drive `drop`-ing) to avoid (somewhat unergonomic) ManuallyDrop
799 // if we can ask Rust to preserve C++ field destruction order in
800 // NontrivialMembers case.
801 SpecialMemberDefinition::NontrivialMembers => true,
802
803 // The `impl Drop` for NontrivialUserDefined needs to call into the
804 // user-defined destructor on C++ side.
805 SpecialMemberDefinition::NontrivialUserDefined => true,
806
807 // TODO(b/213516512): Today the IR doesn't contain Func entries for
808 // deleted functions/destructors/etc. But, maybe we should generate
809 // `impl Drop` in this case? With `unreachable!`? With
810 // `std::mem::forget`?
811 SpecialMemberDefinition::Deleted => false,
812 }
813}
814
815/// Returns whether fields of type `ty` need to be wrapped in `ManuallyDrop<T>`
816/// to prevent the fields from being destructed twice (once by the C++
817/// destructor calkled from the `impl Drop` of the struct and once by `drop` on
818/// the Rust side).
819///
820/// A type is safe to destroy twice if it implements `Copy`. Fields of such
821/// don't need to be wrapped in `ManuallyDrop<T>` even if the struct
822/// containing the fields provides an `impl Drop` that calles into a C++
823/// destructor (in addition to dropping the fields on the Rust side).
824///
825/// Note that it is not enough to just be `!needs_drop<T>()`: Rust only
826/// guarantees that it is safe to use-after-destroy for `Copy` types. See
827/// e.g. the documentation for
828/// [`drop_in_place`](https://doc.rust-lang.org/std/ptr/fn.drop_in_place.html):
829///
830/// > if `T` is not `Copy`, using the pointed-to value after calling
831/// > `drop_in_place` can cause undefined behavior
Teddy Katzd2cd1422022-04-04 09:41:33 -0700832///
833/// For non-Copy union fields, failing to use `ManuallyDrop<T>` would additionally cause a
834/// compile-time error until https://github.com/rust-lang/rust/issues/55149 is stabilized.
Lukasz Anforowicze57215c2022-01-12 14:54:16 +0000835fn needs_manually_drop(ty: &ir::RsType, ir: &IR) -> Result<bool> {
836 let ty_implements_copy = RsTypeKind::new(ty, ir)?.implements_copy();
837 Ok(!ty_implements_copy)
838}
839
Michael Forsterbee84482021-10-13 08:35:38 +0000840/// Generates Rust source code for a given `Record` and associated assertions as
841/// a tuple.
Marcel Hlopkoc0956cf2021-11-29 08:31:28 +0000842fn generate_record(record: &Record, ir: &IR) -> Result<(RsSnippet, RsSnippet)> {
Lukasz Anforowicz4c3a2cc2022-03-11 00:24:49 +0000843 let ident = make_rs_ident(&record.rs_name);
Michael Forstercc5941a2021-10-07 07:12:24 +0000844 let doc_comment = generate_doc_comment(&record.doc_comment);
Marcel Hlopkob4b28742021-09-15 12:45:20 +0000845 let field_idents =
Marcel Hlopkoeaae9b72022-01-21 15:54:11 +0000846 record.fields.iter().map(|f| make_rs_ident(&f.identifier.identifier)).collect_vec();
Michael Forstercc5941a2021-10-07 07:12:24 +0000847 let field_doc_coments =
848 record.fields.iter().map(|f| generate_doc_comment(&f.doc_comment)).collect_vec();
Lukasz Anforowiczfed64e62022-03-22 22:39:04 +0000849
850 let mut field_copy_trait_assertions: Vec<TokenStream> = vec![];
Devin Jeanpierre09c6f452021-09-29 07:34:24 +0000851 let field_types = record
852 .fields
853 .iter()
Devin Jeanpierreb69bcae2022-02-03 09:45:50 +0000854 .enumerate()
855 .map(|(i, f)| {
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +0000856 // [[no_unique_address]] fields are replaced by an unaligned block of memory
857 // which fills space up to the next field.
Devin Jeanpierreb69bcae2022-02-03 09:45:50 +0000858 // See: docs/struct_layout
859 if f.is_no_unique_address {
860 let next_offset = if let Some(next) = record.fields.get(i + 1) {
861 next.offset
862 } else {
863 record.size * 8
864 };
865 let width = Literal::usize_unsuffixed((next_offset - f.offset) / 8);
Rosica Dejanovskaaccf00b2022-04-01 13:28:04 -0700866 return Ok(quote! {[rust_std::mem::MaybeUninit<u8>; #width]});
Devin Jeanpierreb69bcae2022-02-03 09:45:50 +0000867 }
Devin Jeanpierre9886fb42022-04-01 04:31:20 -0700868 let mut formatted = format_rs_type(&f.type_.rs_type, ir)
Lukasz Anforowicze57215c2022-01-12 14:54:16 +0000869 .with_context(|| {
Googlerb7e361d2022-01-04 14:02:59 +0000870 format!("Failed to format type for field {:?} on record {:?}", f, record)
871 })?;
Lukasz Anforowicze57215c2022-01-12 14:54:16 +0000872 // TODO(b/212696226): Verify cases where ManuallyDrop<T> is skipped
873 // via static asserts in the generated code.
Teddy Katzd2cd1422022-04-04 09:41:33 -0700874 if should_implement_drop(record) || record.is_union {
Lukasz Anforowiczfed64e62022-03-22 22:39:04 +0000875 if needs_manually_drop(&f.type_.rs_type, ir)? {
876 // TODO(b/212690698): Avoid (somewhat unergonomic) ManuallyDrop
877 // if we can ask Rust to preserve field destruction order if the
878 // destructor is the SpecialMemberDefinition::NontrivialMembers
879 // case.
Rosica Dejanovskaaccf00b2022-04-01 13:28:04 -0700880 formatted = quote! { rust_std::mem::ManuallyDrop<#formatted> }
Lukasz Anforowiczfed64e62022-03-22 22:39:04 +0000881 } else {
882 field_copy_trait_assertions.push(quote! {
883 const _: () = { assert_impl_all!(#formatted: Copy); };
884 });
885 }
Lukasz Anforowicz6d553632022-01-06 21:36:14 +0000886 };
Devin Jeanpierre7e9a1de2021-12-03 08:04:22 +0000887 Ok(formatted)
888 })
Devin Jeanpierre09c6f452021-09-29 07:34:24 +0000889 .collect::<Result<Vec<_>>>()?;
Lukasz Anforowiczfed64e62022-03-22 22:39:04 +0000890
Googlerec589eb2021-09-17 07:45:39 +0000891 let field_accesses = record
892 .fields
893 .iter()
894 .map(|f| {
Devin Jeanpierreb69bcae2022-02-03 09:45:50 +0000895 if f.access == AccessSpecifier::Public && !f.is_no_unique_address {
Googlerec589eb2021-09-17 07:45:39 +0000896 quote! { pub }
897 } else {
898 quote! {}
899 }
900 })
901 .collect_vec();
Googlerec648ff2021-09-23 07:19:53 +0000902 let size = record.size;
903 let alignment = record.alignment;
Lukasz Anforowiczfed64e62022-03-22 22:39:04 +0000904 let field_offset_assertions =
Googleraaa0a532021-10-01 09:11:27 +0000905 record.fields.iter().zip(field_idents.iter()).map(|(field, field_ident)| {
906 let offset = field.offset;
907 quote! {
908 // The IR contains the offset in bits, while offset_of!()
909 // returns the offset in bytes, so we need to convert.
Googler209b10a2021-12-06 09:11:57 +0000910 const _: () = assert!(offset_of!(#ident, #field_ident) * 8 == #offset);
Googleraaa0a532021-10-01 09:11:27 +0000911 }
912 });
Lukasz Anforowiczfed64e62022-03-22 22:39:04 +0000913 // TODO(b/212696226): Generate `assert_impl_all!` or `assert_not_impl_all!`
914 // assertions about the `Copy` trait - this trait should be implemented
915 // iff `should_implement_drop(record)` is false.
Michael Forsterdb8101a2021-10-08 06:56:03 +0000916 let mut record_features = BTreeSet::new();
917 let mut assertion_features = BTreeSet::new();
Devin Jeanpierre273eeae2021-10-06 13:29:35 +0000918
919 // TODO(mboehme): For the time being, we're using unstable features to
920 // be able to use offset_of!() in static assertions. This is fine for a
921 // prototype, but longer-term we want to either get those features
922 // stabilized or find an alternative. For more details, see
923 // b/200120034#comment15
Marcel Hlopkoeaae9b72022-01-21 15:54:11 +0000924 assertion_features.insert(make_rs_ident("const_ptr_offset_from"));
Devin Jeanpierre2ed14ec2021-10-06 11:32:19 +0000925
Lukasz Anforowicze57215c2022-01-12 14:54:16 +0000926 let derives = generate_derives(record);
Devin Jeanpierre9227d2c2021-10-06 12:26:05 +0000927 let derives = if derives.is_empty() {
928 quote! {}
929 } else {
930 quote! {#[derive( #(#derives),* )]}
931 };
Teddy Katzd2cd1422022-04-04 09:41:33 -0700932 let record_kind = if record.is_union {
933 quote! { union }
934 } else {
935 quote! { struct }
936 };
Lukasz Anforowiczdd9ae0f2022-02-17 15:52:53 +0000937 let unpin_impl = if record.is_unpin() {
938 quote! {}
Devin Jeanpierreea700d32021-10-06 11:33:56 +0000939 } else {
Michael Forsterbee84482021-10-13 08:35:38 +0000940 // negative_impls are necessary for universal initialization due to Rust's
941 // coherence rules: PhantomPinned isn't enough to prove to Rust that a
942 // blanket impl that requires Unpin doesn't apply. See http://<internal link>=h.f6jp8ifzgt3n
Marcel Hlopkoeaae9b72022-01-21 15:54:11 +0000943 record_features.insert(make_rs_ident("negative_impls"));
Lukasz Anforowiczdd9ae0f2022-02-17 15:52:53 +0000944 quote! {
Michael Forsterdb8101a2021-10-08 06:56:03 +0000945 __NEWLINE__ __NEWLINE__
946 impl !Unpin for #ident {}
Lukasz Anforowiczdd9ae0f2022-02-17 15:52:53 +0000947 }
948 };
Devin Jeanpierre273eeae2021-10-06 13:29:35 +0000949
Devin Jeanpierrec80e6242022-02-03 01:56:40 +0000950 let mut repr_attributes = vec![quote! {C}];
951 if record.override_alignment && record.alignment > 1 {
952 let alignment = Literal::usize_unsuffixed(record.alignment);
953 repr_attributes.push(quote! {align(#alignment)});
954 }
955
956 // Adjust the struct to also include base class subobjects. We use an opaque
957 // field because subobjects can live in the alignment of base class
958 // subobjects.
959 let base_subobjects_field = if let Some(base_size) = record.base_size {
960 let n = proc_macro2::Literal::usize_unsuffixed(base_size);
961 quote! {
Rosica Dejanovskaaccf00b2022-04-01 13:28:04 -0700962 __base_class_subobjects: [rust_std::mem::MaybeUninit<u8>; #n],
Devin Jeanpierrec80e6242022-02-03 01:56:40 +0000963 }
964 } else {
965 quote! {}
966 };
967
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +0000968 let empty_struct_placeholder_field =
969 if record.fields.is_empty() && record.base_size.unwrap_or(0) == 0 {
970 quote! {
971 /// Prevent empty C++ struct being zero-size in Rust.
Rosica Dejanovskaaccf00b2022-04-01 13:28:04 -0700972 placeholder: rust_std::mem::MaybeUninit<u8>,
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +0000973 }
974 } else {
975 quote! {}
976 };
Googlerf4792062021-10-20 07:21:21 +0000977
Devin Jeanpierre58181ac2022-02-14 21:30:05 +0000978 let no_unique_address_accessors = cc_struct_no_unique_address_impl(record, ir)?;
Devin Jeanpierre56777022022-02-03 01:57:15 +0000979 let base_class_into = cc_struct_upcast_impl(record, ir)?;
980
Michael Forsterdb8101a2021-10-08 06:56:03 +0000981 let record_tokens = quote! {
Michael Forster028800b2021-10-05 12:39:59 +0000982 #doc_comment
Devin Jeanpierre9227d2c2021-10-06 12:26:05 +0000983 #derives
Devin Jeanpierrec80e6242022-02-03 01:56:40 +0000984 #[repr(#( #repr_attributes ),*)]
Teddy Katzd2cd1422022-04-04 09:41:33 -0700985 pub #record_kind #ident {
Devin Jeanpierrec80e6242022-02-03 01:56:40 +0000986 #base_subobjects_field
Michael Forstercc5941a2021-10-07 07:12:24 +0000987 #( #field_doc_coments #field_accesses #field_idents: #field_types, )*
Googlerf4792062021-10-20 07:21:21 +0000988 #empty_struct_placeholder_field
Marcel Hlopkob4b28742021-09-15 12:45:20 +0000989 }
Googlerec648ff2021-09-23 07:19:53 +0000990
Devin Jeanpierre58181ac2022-02-14 21:30:05 +0000991 #no_unique_address_accessors
992
Devin Jeanpierre56777022022-02-03 01:57:15 +0000993 #base_class_into
994
Devin Jeanpierreea700d32021-10-06 11:33:56 +0000995 #unpin_impl
Devin Jeanpierre273eeae2021-10-06 13:29:35 +0000996 };
997
Lukasz Anforowicz95938f82022-03-24 13:51:54 +0000998 let record_trait_assertions = {
Devin Jeanpierre084ebbd2022-04-01 04:32:46 -0700999 let record_type_name = rs_type_name_for_record(record, ir);
Lukasz Anforowicz95938f82022-03-24 13:51:54 +00001000 let mut assertions: Vec<TokenStream> = vec![];
1001 let mut add_assertion = |assert_impl_macro: TokenStream, trait_name: TokenStream| {
1002 assertions.push(quote! {
1003 const _: () = { #assert_impl_macro (#record_type_name: #trait_name); };
1004 });
1005 };
1006 if should_derive_clone(record) {
1007 add_assertion(quote! { assert_impl_all! }, quote! { Clone });
1008 } else {
1009 // Can't `assert_not_impl_all!` here, because `Clone` may be
1010 // implemented rather than derived.
1011 }
1012 let mut add_conditional_assertion = |should_impl_trait: bool, trait_name: TokenStream| {
1013 let assert_impl_macro = if should_impl_trait {
1014 quote! { assert_impl_all! }
1015 } else {
1016 quote! { assert_not_impl_all! }
1017 };
1018 add_assertion(assert_impl_macro, trait_name);
1019 };
1020 add_conditional_assertion(should_derive_copy(record), quote! { Copy });
1021 add_conditional_assertion(should_implement_drop(record), quote! { Drop });
1022 assertions
1023 };
Michael Forsterdb8101a2021-10-08 06:56:03 +00001024 let assertion_tokens = quote! {
Rosica Dejanovskaaccf00b2022-04-01 13:28:04 -07001025 const _: () = assert!(rust_std::mem::size_of::<#ident>() == #size);
1026 const _: () = assert!(rust_std::mem::align_of::<#ident>() == #alignment);
Lukasz Anforowicz95938f82022-03-24 13:51:54 +00001027 #( #record_trait_assertions )*
Lukasz Anforowiczfed64e62022-03-22 22:39:04 +00001028 #( #field_offset_assertions )*
1029 #( #field_copy_trait_assertions )*
Michael Forsterdb8101a2021-10-08 06:56:03 +00001030 };
1031
1032 Ok((
1033 RsSnippet { features: record_features, tokens: record_tokens },
1034 RsSnippet { features: assertion_features, tokens: assertion_tokens },
1035 ))
Marcel Hlopkob4b28742021-09-15 12:45:20 +00001036}
1037
Lukasz Anforowicz2e41bb62022-01-11 18:23:07 +00001038fn should_derive_clone(record: &Record) -> bool {
Devin Jeanpierre88343c72022-01-15 01:10:23 +00001039 record.is_unpin()
Devin Jeanpierre2ed14ec2021-10-06 11:32:19 +00001040 && record.copy_constructor.access == ir::AccessSpecifier::Public
1041 && record.copy_constructor.definition == SpecialMemberDefinition::Trivial
Lukasz Anforowicz2e41bb62022-01-11 18:23:07 +00001042}
1043
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00001044fn should_derive_copy(record: &Record) -> bool {
1045 // TODO(b/202258760): Make `Copy` inclusion configurable.
1046 should_derive_clone(record)
1047}
1048
1049fn generate_derives(record: &Record) -> Vec<Ident> {
1050 let mut derives = vec![];
Lukasz Anforowicz2e41bb62022-01-11 18:23:07 +00001051 if should_derive_clone(record) {
Marcel Hlopkoeaae9b72022-01-21 15:54:11 +00001052 derives.push(make_rs_ident("Clone"));
Devin Jeanpierre2ed14ec2021-10-06 11:32:19 +00001053 }
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00001054 if should_derive_copy(record) {
Marcel Hlopkoeaae9b72022-01-21 15:54:11 +00001055 derives.push(make_rs_ident("Copy"));
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00001056 }
1057 derives
Devin Jeanpierre2ed14ec2021-10-06 11:32:19 +00001058}
1059
Teddy Katz76fa42b2022-02-23 01:22:56 +00001060fn generate_enum(enum_: &Enum, ir: &IR) -> Result<TokenStream> {
1061 let name = make_rs_ident(&enum_.identifier.identifier);
Devin Jeanpierre9886fb42022-04-01 04:31:20 -07001062 let underlying_type = format_rs_type(&enum_.underlying_type.rs_type, ir)?;
Teddy Katz76fa42b2022-02-23 01:22:56 +00001063 let enumerator_names =
1064 enum_.enumerators.iter().map(|enumerator| make_rs_ident(&enumerator.identifier.identifier));
1065 let enumerator_values = enum_.enumerators.iter().map(|enumerator| enumerator.value);
1066 Ok(quote! {
1067 #[repr(transparent)]
1068 #[derive(Debug, PartialEq, Eq, Copy, Clone, Hash, PartialOrd, Ord)]
1069 pub struct #name(#underlying_type);
1070 impl #name {
1071 #(pub const #enumerator_names: #name = #name(#enumerator_values);)*
1072 }
1073 impl From<#underlying_type> for #name {
1074 fn from(value: #underlying_type) -> #name {
1075 #name(v)
1076 }
1077 }
1078 impl From<#name> for #underlying_type {
1079 fn from(value: #name) -> #underlying_type {
1080 v.0
1081 }
1082 }
1083 })
1084}
1085
Googler6a0a5252022-01-11 14:08:09 +00001086fn generate_type_alias(type_alias: &TypeAlias, ir: &IR) -> Result<TokenStream> {
Marcel Hlopkoeaae9b72022-01-21 15:54:11 +00001087 let ident = make_rs_ident(&type_alias.identifier.identifier);
Lukasz Anforowiczc503af42022-03-04 23:18:15 +00001088 let doc_comment = generate_doc_comment(&type_alias.doc_comment);
Devin Jeanpierre9886fb42022-04-01 04:31:20 -07001089 let underlying_type = format_rs_type(&type_alias.underlying_type.rs_type, ir)
Googler6a0a5252022-01-11 14:08:09 +00001090 .with_context(|| format!("Failed to format underlying type for {:?}", type_alias))?;
Lukasz Anforowiczc503af42022-03-04 23:18:15 +00001091 Ok(quote! {
1092 #doc_comment
1093 pub type #ident = #underlying_type;
1094 })
Googler6a0a5252022-01-11 14:08:09 +00001095}
1096
Michael Forster523dbd42021-10-12 11:05:44 +00001097/// Generates Rust source code for a given `UnsupportedItem`.
1098fn generate_unsupported(item: &UnsupportedItem) -> Result<TokenStream> {
Googler48a74dd2021-10-25 07:31:53 +00001099 let location = if item.source_loc.filename.is_empty() {
1100 "<unknown location>".to_string()
1101 } else {
1102 // TODO(forster): The "google3" prefix should probably come from a command line
1103 // argument.
1104 // TODO(forster): Consider linking to the symbol instead of to the line number
1105 // to avoid wrong links while generated files have not caught up.
1106 format!("google3/{};l={}", &item.source_loc.filename, &item.source_loc.line)
1107 };
Michael Forster6a184ad2021-10-12 13:04:05 +00001108 let message = format!(
Googler48a74dd2021-10-25 07:31:53 +00001109 "{}\nError while generating bindings for item '{}':\n{}",
1110 &location, &item.name, &item.message
Michael Forster6a184ad2021-10-12 13:04:05 +00001111 );
Michael Forster523dbd42021-10-12 11:05:44 +00001112 Ok(quote! { __COMMENT__ #message })
1113}
1114
Michael Forsterf1dce422021-10-13 09:50:16 +00001115/// Generates Rust source code for a given `Comment`.
1116fn generate_comment(comment: &Comment) -> Result<TokenStream> {
1117 let text = &comment.text;
1118 Ok(quote! { __COMMENT__ #text })
1119}
1120
Rosica Dejanovska8da8f792022-03-29 02:02:22 -07001121#[derive(Clone, Debug, Default)]
1122struct GeneratedItem {
1123 item: TokenStream,
1124 thunks: TokenStream,
1125 assertions: TokenStream,
1126 features: BTreeSet<Ident>,
1127 has_record: bool,
1128}
1129
1130fn generate_item(
1131 item: &Item,
1132 ir: &IR,
1133 overloaded_funcs: &HashSet<FunctionId>,
1134) -> Result<GeneratedItem> {
1135 let generated_item = match item {
Devin Jeanpierred7b48102022-03-31 04:15:03 -07001136 Item::Func(func) => match generate_func(func, ir) {
1137 Err(e) => GeneratedItem {
1138 item: generate_unsupported(&make_unsupported_fn(func, ir, format!("{e}"))?)?,
1139 ..Default::default()
1140 },
1141 Ok(None) => GeneratedItem::default(),
1142 Ok(Some((api_func, thunk, function_id))) => {
Rosica Dejanovska8da8f792022-03-29 02:02:22 -07001143 if overloaded_funcs.contains(&function_id) {
1144 GeneratedItem {
1145 item: generate_unsupported(&make_unsupported_fn(
1146 func,
1147 ir,
1148 "Cannot generate bindings for overloaded function",
1149 )?)?,
1150 ..Default::default()
1151 }
1152 } else {
1153 GeneratedItem {
1154 item: api_func.tokens,
1155 thunks: thunk.tokens,
1156 features: api_func.features.union(&thunk.features).cloned().collect(),
1157 ..Default::default()
1158 }
1159 }
1160 }
1161 },
1162 Item::Record(record) => {
1163 if !ir.is_current_target(&record.owning_target)
1164 && !ir.is_stdlib_target(&record.owning_target)
1165 {
1166 GeneratedItem { ..Default::default() }
1167 } else {
1168 let (snippet, assertions_snippet) = generate_record(record, ir)?;
1169 GeneratedItem {
1170 item: snippet.tokens,
1171 assertions: assertions_snippet.tokens,
1172 features: snippet
1173 .features
1174 .union(&assertions_snippet.features)
1175 .cloned()
1176 .collect(),
1177 has_record: true,
1178 ..Default::default()
1179 }
1180 }
1181 }
1182 Item::Enum(enum_) => {
1183 if !ir.is_current_target(&enum_.owning_target)
1184 && !ir.is_stdlib_target(&enum_.owning_target)
1185 {
1186 GeneratedItem { ..Default::default() }
1187 } else {
1188 GeneratedItem { item: generate_enum(enum_, ir)?, ..Default::default() }
1189 }
1190 }
1191 Item::TypeAlias(type_alias) => {
1192 if !ir.is_current_target(&type_alias.owning_target)
1193 && !ir.is_stdlib_target(&type_alias.owning_target)
1194 {
1195 GeneratedItem { ..Default::default() }
1196 } else {
1197 GeneratedItem { item: generate_type_alias(type_alias, ir)?, ..Default::default() }
1198 }
1199 }
1200 Item::UnsupportedItem(unsupported) => {
1201 GeneratedItem { item: generate_unsupported(unsupported)?, ..Default::default() }
1202 }
1203 Item::Comment(comment) => {
1204 GeneratedItem { item: generate_comment(comment)?, ..Default::default() }
1205 }
1206 };
1207
1208 Ok(generated_item)
1209}
1210
Marcel Hlopko89547752021-12-10 09:39:41 +00001211fn generate_rs_api(ir: &IR) -> Result<TokenStream> {
Michael Forstered642022021-10-04 09:48:25 +00001212 let mut items = vec![];
Marcel Hlopko42abfc82021-08-09 07:03:17 +00001213 let mut thunks = vec![];
Michael Forsterdb8101a2021-10-08 06:56:03 +00001214 let mut assertions = vec![];
Marcel Hlopko42abfc82021-08-09 07:03:17 +00001215
Googler454f2652021-12-06 12:53:12 +00001216 // We import nullable pointers as an Option<&T> and assume that at the ABI
1217 // level, None is represented as a zero pointer value whereas Some is
1218 // represented as as non-zero pointer value. This seems like a pretty safe
1219 // assumption to make, but to provide some safeguard, assert that
1220 // `Option<&i32>` and `&i32` have the same size.
1221 assertions.push(quote! {
Rosica Dejanovskaaccf00b2022-04-01 13:28:04 -07001222 const _: () = assert!(rust_std::mem::size_of::<Option<&i32>>() == rust_std::mem::size_of::<&i32>());
Googler454f2652021-12-06 12:53:12 +00001223 });
1224
Michael Forsterbee84482021-10-13 08:35:38 +00001225 // TODO(jeanpierreda): Delete has_record, either in favor of using RsSnippet, or not
1226 // having uses. See https://chat.google.com/room/AAAAnQmj8Qs/6QbkSvWcfhA
Devin Jeanpierreea700d32021-10-06 11:33:56 +00001227 let mut has_record = false;
Devin Jeanpierre273eeae2021-10-06 13:29:35 +00001228 let mut features = BTreeSet::new();
Marcel Hlopko42abfc82021-08-09 07:03:17 +00001229
Lukasz Anforowicz72c4d222022-02-18 19:07:28 +00001230 // For #![rustfmt::skip].
1231 features.insert(make_rs_ident("custom_inner_attributes"));
1232
Googlerd03d05b2022-01-07 10:10:57 +00001233 // Identify all functions having overloads that we can't import (yet).
1234 // TODO(b/213280424): Implement support for overloaded functions.
1235 let mut seen_funcs = HashSet::new();
1236 let mut overloaded_funcs = HashSet::new();
1237 for func in ir.functions() {
Devin Jeanpierred7b48102022-03-31 04:15:03 -07001238 if let Ok(Some((.., function_id))) = generate_func(func, ir) {
Googlerd03d05b2022-01-07 10:10:57 +00001239 if !seen_funcs.insert(function_id.clone()) {
1240 overloaded_funcs.insert(function_id);
1241 }
1242 }
1243 }
1244
Marcel Hlopko3b9bf9e2021-11-29 08:25:14 +00001245 for item in ir.items() {
Rosica Dejanovska8da8f792022-03-29 02:02:22 -07001246 let generated = generate_item(item, ir, &overloaded_funcs)?;
1247 items.push(generated.item);
Devin Jeanpierreb5ba1402022-03-29 07:29:24 -07001248 if !generated.thunks.is_empty() {
Rosica Dejanovska8da8f792022-03-29 02:02:22 -07001249 thunks.push(generated.thunks);
Michael Forstered642022021-10-04 09:48:25 +00001250 }
Devin Jeanpierreb5ba1402022-03-29 07:29:24 -07001251 if !generated.assertions.is_empty() {
Rosica Dejanovska8da8f792022-03-29 02:02:22 -07001252 assertions.push(generated.assertions);
1253 }
1254 features.extend(generated.features);
1255 has_record = has_record || generated.has_record;
Marcel Hlopko42abfc82021-08-09 07:03:17 +00001256 }
1257
Marcel Hlopkob4b28742021-09-15 12:45:20 +00001258 let mod_detail = if thunks.is_empty() {
1259 quote! {}
1260 } else {
1261 quote! {
1262 mod detail {
Googler55647142022-01-11 12:37:39 +00001263 #[allow(unused_imports)]
Devin Jeanpierred4dde0e2021-10-13 20:48:25 +00001264 use super::*;
Marcel Hlopkob4b28742021-09-15 12:45:20 +00001265 extern "C" {
1266 #( #thunks )*
1267 }
Marcel Hlopko42abfc82021-08-09 07:03:17 +00001268 }
1269 }
1270 };
1271
Rosica Dejanovskaaccf00b2022-04-01 13:28:04 -07001272 // TODO(b/227790881): Replace usage of rust_std with ::std once the issue
1273 // is fixed.
Devin Jeanpierreea700d32021-10-06 11:33:56 +00001274 let imports = if has_record {
Googlerec648ff2021-09-23 07:19:53 +00001275 quote! {
Rosica Dejanovskabbed2012022-04-05 04:06:30 -07001276 use ::std as rust_std;
Googleraaa0a532021-10-01 09:11:27 +00001277 use memoffset_unstable_const::offset_of;
Lukasz Anforowiczfed64e62022-03-22 22:39:04 +00001278 use static_assertions::{assert_impl_all, assert_not_impl_all};
Googlerec648ff2021-09-23 07:19:53 +00001279 }
Michael Forstered642022021-10-04 09:48:25 +00001280 } else {
Rosica Dejanovskaaccf00b2022-04-01 13:28:04 -07001281 quote! {
Rosica Dejanovskabbed2012022-04-05 04:06:30 -07001282 use ::std as rust_std;
Rosica Dejanovskaaccf00b2022-04-01 13:28:04 -07001283 }
Googlerec648ff2021-09-23 07:19:53 +00001284 };
1285
Devin Jeanpierre273eeae2021-10-06 13:29:35 +00001286 let features = if features.is_empty() {
1287 quote! {}
1288 } else {
1289 quote! {
1290 #![feature( #(#features),* )]
1291 }
1292 };
1293
Marcel Hlopko89547752021-12-10 09:39:41 +00001294 Ok(quote! {
Googler55647142022-01-11 12:37:39 +00001295 #features __NEWLINE__
1296 #![allow(non_camel_case_types)] __NEWLINE__
1297 #![allow(non_snake_case)] __NEWLINE__ __NEWLINE__
1298
Michael Forsterdb8101a2021-10-08 06:56:03 +00001299 #imports __NEWLINE__ __NEWLINE__
Googlerec648ff2021-09-23 07:19:53 +00001300
Michael Forsterdb8101a2021-10-08 06:56:03 +00001301 #( #items __NEWLINE__ __NEWLINE__ )*
Marcel Hlopkob4b28742021-09-15 12:45:20 +00001302
Michael Forsterdb8101a2021-10-08 06:56:03 +00001303 #mod_detail __NEWLINE__ __NEWLINE__
1304
1305 #( #assertions __NEWLINE__ __NEWLINE__ )*
Marcel Hlopko89547752021-12-10 09:39:41 +00001306 })
Marcel Hlopko42abfc82021-08-09 07:03:17 +00001307}
1308
Marcel Hlopkoeaae9b72022-01-21 15:54:11 +00001309/// Makes an 'Ident' to be used in the Rust source code. Escapes Rust keywords.
1310fn make_rs_ident(ident: &str) -> Ident {
1311 // TODO(https://github.com/dtolnay/syn/pull/1098): Remove the hardcoded list once syn recognizes
1312 // 2018 and 2021 keywords.
1313 if ["async", "await", "try", "dyn"].contains(&ident) {
1314 return format_ident!("r#{}", ident);
1315 }
1316 match syn::parse_str::<syn::Ident>(ident) {
1317 Ok(_) => format_ident!("{}", ident),
1318 Err(_) => format_ident!("r#{}", ident),
1319 }
1320}
1321
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +00001322/// Formats a C++ identifier. Does not escape C++ keywords.
1323fn format_cc_ident(ident: &str) -> TokenStream {
1324 ident.parse().unwrap()
Marcel Hlopko42abfc82021-08-09 07:03:17 +00001325}
1326
Googler6a0a5252022-01-11 14:08:09 +00001327fn rs_type_name_for_target_and_identifier(
Googler6c3de122022-03-28 11:40:41 +00001328 owning_target: &BazelLabel,
Googler6a0a5252022-01-11 14:08:09 +00001329 identifier: &ir::Identifier,
1330 ir: &IR,
Devin Jeanpierre084ebbd2022-04-01 04:32:46 -07001331) -> TokenStream {
Marcel Hlopkoeaae9b72022-01-21 15:54:11 +00001332 let ident = make_rs_ident(identifier.identifier.as_str());
Googler6a0a5252022-01-11 14:08:09 +00001333
1334 if ir.is_current_target(owning_target) || ir.is_stdlib_target(owning_target) {
Devin Jeanpierre084ebbd2022-04-01 04:32:46 -07001335 quote! {#ident}
Googler6a0a5252022-01-11 14:08:09 +00001336 } else {
Devin Jeanpierre084ebbd2022-04-01 04:32:46 -07001337 let owning_crate_name = owning_target.target_name();
Marcel Hlopkod906b892022-01-27 08:52:36 +00001338 // TODO(b/216587072): Remove this hacky escaping and use the import! macro once
1339 // available
Lukasz Anforowiczdd9ae0f2022-02-17 15:52:53 +00001340 let escaped_owning_crate_name = owning_crate_name.replace('-', "_");
Marcel Hlopkod906b892022-01-27 08:52:36 +00001341 let owning_crate = make_rs_ident(&escaped_owning_crate_name);
Devin Jeanpierre084ebbd2022-04-01 04:32:46 -07001342 quote! {#owning_crate::#ident}
Googler6a0a5252022-01-11 14:08:09 +00001343 }
1344}
1345
Devin Jeanpierre084ebbd2022-04-01 04:32:46 -07001346fn rs_type_name_for_record(record: &Record, ir: &IR) -> TokenStream {
Lukasz Anforowicz95938f82022-03-24 13:51:54 +00001347 rs_type_name_for_target_and_identifier(
1348 &record.owning_target,
1349 &Identifier { identifier: record.rs_name.clone() },
1350 ir,
1351 )
1352}
1353
Lukasz Anforowicz40c2eb82022-01-11 18:22:31 +00001354#[derive(Debug, Eq, PartialEq)]
1355enum Mutability {
1356 Const,
1357 Mut,
1358}
1359
1360impl Mutability {
1361 fn format_for_pointer(&self) -> TokenStream {
1362 match self {
1363 Mutability::Mut => quote! {mut},
1364 Mutability::Const => quote! {const},
1365 }
1366 }
1367
1368 fn format_for_reference(&self) -> TokenStream {
1369 match self {
1370 Mutability::Mut => quote! {mut},
1371 Mutability::Const => quote! {},
1372 }
1373 }
1374}
1375
1376// TODO(b/213947473): Instead of having a separate RsTypeKind here, consider
1377// changing ir::RsType into a similar `enum`, with fields that contain
Rosica Dejanovskad638cf52022-03-23 15:45:01 +00001378// references (e.g. &'ir Record`) instead of ItemIds.
Lukasz Anforowicz40c2eb82022-01-11 18:22:31 +00001379#[derive(Debug)]
1380enum RsTypeKind<'ir> {
Devin Jeanpierred2b7a8f2022-04-01 04:37:16 -07001381 Pointer { pointee: Box<RsTypeKind<'ir>>, mutability: Mutability },
1382 Reference { referent: Box<RsTypeKind<'ir>>, mutability: Mutability, lifetime: Lifetime },
1383 RvalueReference { referent: Box<RsTypeKind<'ir>>, mutability: Mutability, lifetime: Lifetime },
1384 FuncPtr { abi: &'ir str, return_type: Box<RsTypeKind<'ir>>, param_types: Vec<RsTypeKind<'ir>> },
Lukasz Anforowicz40c2eb82022-01-11 18:22:31 +00001385 Record(&'ir Record),
Devin Jeanpierred2b7a8f2022-04-01 04:37:16 -07001386 TypeAlias { type_alias: &'ir TypeAlias, underlying_type: Box<RsTypeKind<'ir>> },
Lukasz Anforowicz40c2eb82022-01-11 18:22:31 +00001387 Unit,
Devin Jeanpierred2b7a8f2022-04-01 04:37:16 -07001388 Other { name: &'ir str, type_args: Vec<RsTypeKind<'ir>> },
Lukasz Anforowicz40c2eb82022-01-11 18:22:31 +00001389}
1390
1391impl<'ir> RsTypeKind<'ir> {
1392 pub fn new(ty: &'ir ir::RsType, ir: &'ir IR) -> Result<Self> {
1393 // The lambdas deduplicate code needed by multiple `match` branches.
1394 let get_type_args = || -> Result<Vec<RsTypeKind<'ir>>> {
1395 ty.type_args.iter().map(|type_arg| RsTypeKind::<'ir>::new(type_arg, ir)).collect()
1396 };
1397 let get_pointee = || -> Result<Box<RsTypeKind<'ir>>> {
1398 if ty.type_args.len() != 1 {
1399 bail!("Missing pointee/referent type (need exactly 1 type argument): {:?}", ty);
1400 }
1401 Ok(Box::new(get_type_args()?.remove(0)))
1402 };
Devin Jeanpierred2b7a8f2022-04-01 04:37:16 -07001403 let get_lifetime = || -> Result<Lifetime> {
Lukasz Anforowicz40c2eb82022-01-11 18:22:31 +00001404 if ty.lifetime_args.len() != 1 {
1405 bail!("Missing reference lifetime (need exactly 1 lifetime argument): {:?}", ty);
1406 }
Devin Jeanpierred2b7a8f2022-04-01 04:37:16 -07001407 let lifetime_id = ty.lifetime_args[0];
1408 ir.get_lifetime(lifetime_id)
1409 .ok_or_else(|| anyhow!("no known lifetime with id {lifetime_id:?}"))
1410 .cloned()
Lukasz Anforowicz40c2eb82022-01-11 18:22:31 +00001411 };
1412
1413 let result = match ty.name.as_deref() {
1414 None => {
1415 ensure!(
1416 ty.type_args.is_empty(),
1417 "Type arguments on records nor type aliases are not yet supported: {:?}",
1418 ty
1419 );
1420 match ir.item_for_type(ty)? {
1421 Item::Record(record) => RsTypeKind::Record(record),
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00001422 Item::TypeAlias(type_alias) => RsTypeKind::TypeAlias {
1423 type_alias,
1424 underlying_type: Box::new(RsTypeKind::new(
1425 &type_alias.underlying_type.rs_type,
1426 ir,
1427 )?),
1428 },
Lukasz Anforowicz40c2eb82022-01-11 18:22:31 +00001429 other_item => bail!("Item does not define a type: {:?}", other_item),
1430 }
1431 }
1432 Some(name) => match name {
1433 "()" => {
1434 if !ty.type_args.is_empty() {
1435 bail!("Unit type must not have type arguments: {:?}", ty);
1436 }
1437 RsTypeKind::Unit
1438 }
1439 "*mut" => {
1440 RsTypeKind::Pointer { pointee: get_pointee()?, mutability: Mutability::Mut }
1441 }
1442 "*const" => {
1443 RsTypeKind::Pointer { pointee: get_pointee()?, mutability: Mutability::Const }
1444 }
1445 "&mut" => RsTypeKind::Reference {
1446 referent: get_pointee()?,
1447 mutability: Mutability::Mut,
Devin Jeanpierred2b7a8f2022-04-01 04:37:16 -07001448 lifetime: get_lifetime()?,
Lukasz Anforowicz40c2eb82022-01-11 18:22:31 +00001449 },
1450 "&" => RsTypeKind::Reference {
1451 referent: get_pointee()?,
1452 mutability: Mutability::Const,
Devin Jeanpierred2b7a8f2022-04-01 04:37:16 -07001453 lifetime: get_lifetime()?,
Lukasz Anforowicz40c2eb82022-01-11 18:22:31 +00001454 },
Devin Jeanpierredeea7892022-03-29 02:13:31 -07001455 "#RvalueReference mut" => RsTypeKind::RvalueReference {
1456 referent: get_pointee()?,
1457 mutability: Mutability::Mut,
Devin Jeanpierred2b7a8f2022-04-01 04:37:16 -07001458 lifetime: get_lifetime()?,
Devin Jeanpierredeea7892022-03-29 02:13:31 -07001459 },
1460 "#RvalueReference const" => RsTypeKind::RvalueReference {
1461 referent: get_pointee()?,
1462 mutability: Mutability::Const,
Devin Jeanpierred2b7a8f2022-04-01 04:37:16 -07001463 lifetime: get_lifetime()?,
Devin Jeanpierredeea7892022-03-29 02:13:31 -07001464 },
Lukasz Anforowiczcf230fd2022-02-18 19:20:39 +00001465 name => {
1466 let mut type_args = get_type_args()?;
1467 match name.strip_prefix("#funcPtr ") {
1468 None => RsTypeKind::Other { name, type_args },
1469 Some(abi) => {
1470 // TODO(b/217419782): Consider enforcing `'static` lifetime.
1471 ensure!(!type_args.is_empty(), "No return type in fn type: {:?}", ty);
1472 RsTypeKind::FuncPtr {
1473 abi,
1474 return_type: Box::new(type_args.remove(type_args.len() - 1)),
1475 param_types: type_args,
1476 }
Devin Jeanpierre1b8f4a12022-03-22 21:29:42 +00001477 }
Lukasz Anforowiczcf230fd2022-02-18 19:20:39 +00001478 }
Devin Jeanpierre1b8f4a12022-03-22 21:29:42 +00001479 }
Lukasz Anforowicz40c2eb82022-01-11 18:22:31 +00001480 },
1481 };
1482 Ok(result)
1483 }
1484
Devin Jeanpierre149950d2022-02-22 21:02:02 +00001485 /// Returns true if the type is known to be `Unpin`, false otherwise.
Devin Jeanpierref85ae592022-04-01 04:33:57 -07001486 pub fn is_unpin(&self) -> bool {
Devin Jeanpierre149950d2022-02-22 21:02:02 +00001487 match self {
1488 RsTypeKind::Record(record) => record.is_unpin(),
Devin Jeanpierref85ae592022-04-01 04:33:57 -07001489 RsTypeKind::TypeAlias { underlying_type, .. } => underlying_type.is_unpin(),
Devin Jeanpierre149950d2022-02-22 21:02:02 +00001490 _ => true,
1491 }
1492 }
1493
Devin Jeanpierred2b7a8f2022-04-01 04:37:16 -07001494 pub fn format(&self, ir: &IR) -> TokenStream {
1495 match self {
Lukasz Anforowicz40c2eb82022-01-11 18:22:31 +00001496 RsTypeKind::Pointer { pointee, mutability } => {
1497 let mutability = mutability.format_for_pointer();
Devin Jeanpierred2b7a8f2022-04-01 04:37:16 -07001498 let nested_type = pointee.format(ir);
Lukasz Anforowicz40c2eb82022-01-11 18:22:31 +00001499 quote! {* #mutability #nested_type}
1500 }
Devin Jeanpierred2b7a8f2022-04-01 04:37:16 -07001501 RsTypeKind::Reference { referent, mutability, lifetime } => {
Devin Jeanpierre149950d2022-02-22 21:02:02 +00001502 let mut_ = mutability.format_for_reference();
Devin Jeanpierred2b7a8f2022-04-01 04:37:16 -07001503 let lifetime = format_lifetime_name(&lifetime.name);
1504 let nested_type = referent.format(ir);
Devin Jeanpierre149950d2022-02-22 21:02:02 +00001505 let reference = quote! {& #lifetime #mut_ #nested_type};
Devin Jeanpierref85ae592022-04-01 04:33:57 -07001506 if mutability == &Mutability::Mut && !referent.is_unpin() {
Rosica Dejanovskaaccf00b2022-04-01 13:28:04 -07001507 // TODO(b/200067242): Add a `use rust_std::pin::Pin` to the crate, and use `Pin`.
Devin Jeanpierre149950d2022-02-22 21:02:02 +00001508 // Probably format needs to return an RsSnippet, and RsSnippet needs a `uses`
1509 // field.
Rosica Dejanovskaaccf00b2022-04-01 13:28:04 -07001510 quote! {rust_std::pin::Pin< #reference >}
Devin Jeanpierre149950d2022-02-22 21:02:02 +00001511 } else {
1512 reference
1513 }
Lukasz Anforowicz40c2eb82022-01-11 18:22:31 +00001514 }
Devin Jeanpierred2b7a8f2022-04-01 04:37:16 -07001515 RsTypeKind::RvalueReference { referent, mutability, lifetime } => {
1516 let lifetime = format_lifetime_name(&lifetime.name);
1517 let nested_type = referent.format(ir);
Devin Jeanpierredeea7892022-03-29 02:13:31 -07001518 // TODO(b/200067242): Add a `use ctor::RvalueReference` (etc.) to the crate.
1519 if mutability == &Mutability::Mut {
1520 quote! {ctor::RvalueReference<#lifetime, #nested_type>}
1521 } else {
1522 quote! {ctor::ConstRvalueReference<#lifetime, #nested_type>}
1523 }
1524 }
Lukasz Anforowiczcf230fd2022-02-18 19:20:39 +00001525 RsTypeKind::FuncPtr { abi, return_type, param_types } => {
Devin Jeanpierre1b8f4a12022-03-22 21:29:42 +00001526 let return_frag =
Devin Jeanpierred2b7a8f2022-04-01 04:37:16 -07001527 return_type.format_as_return_type_fragment(ir);
Lukasz Anforowiczcf230fd2022-02-18 19:20:39 +00001528 let param_types = param_types
Devin Jeanpierre1b8f4a12022-03-22 21:29:42 +00001529 .iter()
Devin Jeanpierred2b7a8f2022-04-01 04:37:16 -07001530 .map(|t| t.format(ir));
Devin Jeanpierre1b8f4a12022-03-22 21:29:42 +00001531 quote! { extern #abi fn( #( #param_types ),* ) #return_frag }
1532 }
Devin Jeanpierre084ebbd2022-04-01 04:32:46 -07001533 RsTypeKind::Record(record) => rs_type_name_for_record(record, ir),
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00001534 RsTypeKind::TypeAlias { type_alias, .. } => rs_type_name_for_target_and_identifier(
Lukasz Anforowicz40c2eb82022-01-11 18:22:31 +00001535 &type_alias.owning_target,
1536 &type_alias.identifier,
1537 ir,
Devin Jeanpierre084ebbd2022-04-01 04:32:46 -07001538 ),
Lukasz Anforowicz40c2eb82022-01-11 18:22:31 +00001539 RsTypeKind::Unit => quote! {()},
1540 RsTypeKind::Other { name, type_args } => {
Marcel Hlopkoeaae9b72022-01-21 15:54:11 +00001541 let ident = make_rs_ident(name);
Lukasz Anforowicz40c2eb82022-01-11 18:22:31 +00001542 let generic_params = format_generic_params(
1543 type_args
1544 .iter()
Devin Jeanpierred2b7a8f2022-04-01 04:37:16 -07001545 .map(|type_arg| type_arg.format(ir)),
Lukasz Anforowicz40c2eb82022-01-11 18:22:31 +00001546 );
1547 quote! {#ident #generic_params}
1548 }
Devin Jeanpierred2b7a8f2022-04-01 04:37:16 -07001549 }
Lukasz Anforowicz40c2eb82022-01-11 18:22:31 +00001550 }
1551
Devin Jeanpierred2b7a8f2022-04-01 04:37:16 -07001552 pub fn format_as_return_type_fragment(&self, ir: &IR) -> TokenStream {
Lukasz Anforowiczcf230fd2022-02-18 19:20:39 +00001553 match self {
Devin Jeanpierred2b7a8f2022-04-01 04:37:16 -07001554 RsTypeKind::Unit => quote! {},
Lukasz Anforowiczcf230fd2022-02-18 19:20:39 +00001555 other_type => {
Devin Jeanpierred2b7a8f2022-04-01 04:37:16 -07001556 let return_type = other_type.format(ir);
1557 quote! { -> #return_type }
Lukasz Anforowiczcf230fd2022-02-18 19:20:39 +00001558 }
1559 }
1560 }
1561
Lukasz Anforowiczcde4b1b2022-02-03 21:20:55 +00001562 /// Formats this RsTypeKind as `&'a mut MaybeUninit<SomeStruct>`. This is
1563 /// used to format `__this` parameter in a constructor thunk.
Devin Jeanpierre9886fb42022-04-01 04:31:20 -07001564 pub fn format_mut_ref_as_uninitialized(&self, ir: &IR) -> Result<TokenStream> {
Lukasz Anforowiczcde4b1b2022-02-03 21:20:55 +00001565 match self {
Devin Jeanpierred2b7a8f2022-04-01 04:37:16 -07001566 RsTypeKind::Reference { referent, lifetime, mutability: Mutability::Mut } => {
1567 let nested_type = referent.format(ir);
1568 let lifetime = format_lifetime_name(&lifetime.name);
Rosica Dejanovskaaccf00b2022-04-01 13:28:04 -07001569 Ok(quote! { & #lifetime mut rust_std::mem::MaybeUninit< #nested_type > })
Lukasz Anforowicz40c2eb82022-01-11 18:22:31 +00001570 }
Devin Jeanpierre149950d2022-02-22 21:02:02 +00001571 _ => bail!("Expected reference to format as MaybeUninit, got: {:?}", self),
Lukasz Anforowiczcde4b1b2022-02-03 21:20:55 +00001572 }
Lukasz Anforowicz40c2eb82022-01-11 18:22:31 +00001573 }
1574
Devin Jeanpierre149950d2022-02-22 21:02:02 +00001575 /// Formats a reference or pointer as a raw pointer.
Devin Jeanpierre9886fb42022-04-01 04:31:20 -07001576 pub fn format_ref_as_raw_ptr(&self, ir: &IR) -> Result<TokenStream> {
Devin Jeanpierre149950d2022-02-22 21:02:02 +00001577 match self {
1578 RsTypeKind::Reference { referent: pointee, mutability, .. }
1579 | RsTypeKind::Pointer { pointee, mutability } => {
Devin Jeanpierred2b7a8f2022-04-01 04:37:16 -07001580 let nested_type = pointee.format(ir);
Devin Jeanpierre149950d2022-02-22 21:02:02 +00001581 let mut_ = mutability.format_for_pointer();
1582 Ok(quote! { * #mut_ #nested_type })
1583 }
1584 _ => bail!("Expected reference to format as raw ptr, got: {:?}", self),
1585 }
1586 }
1587
1588 /// Formats this RsTypeKind as the `self` parameter: usually, `&'a self` or
1589 /// `&'a mut self`.
1590 ///
1591 /// If this is !Unpin, however, it uses `self: Pin<&mut Self>` instead.
Devin Jeanpierre9886fb42022-04-01 04:31:20 -07001592 pub fn format_as_self_param(&self, func: &Func, ir: &IR) -> Result<TokenStream> {
Lukasz Anforowicz732ca642022-02-03 20:58:38 +00001593 if func.name == UnqualifiedIdentifier::Destructor {
1594 let record = func
1595 .member_func_metadata
1596 .as_ref()
1597 .ok_or_else(|| anyhow!("Destructors must be member functions: {:?}", func))?
1598 .find_record(ir)?;
1599 if self.is_mut_ptr_to(record) {
1600 // Even in C++ it is UB to retain `this` pointer and dereference it
1601 // after a destructor runs. Therefore it is safe to use `&self` or
1602 // `&mut self` in Rust even if IR represents `__this` as a Rust
1603 // pointer (e.g. when lifetime annotations are missing - lifetime
1604 // annotations are required to represent it as a Rust reference).
1605 return Ok(quote! { &mut self });
1606 }
Lukasz Anforowicz231a3bb2022-01-12 14:05:59 +00001607 }
1608
1609 match self {
Devin Jeanpierred2b7a8f2022-04-01 04:37:16 -07001610 RsTypeKind::Reference { referent, lifetime, mutability } => {
Devin Jeanpierre149950d2022-02-22 21:02:02 +00001611 let mut_ = mutability.format_for_reference();
Devin Jeanpierred2b7a8f2022-04-01 04:37:16 -07001612 let lifetime = format_lifetime_name(&lifetime.name);
Devin Jeanpierre1b8f4a12022-03-22 21:29:42 +00001613 if mutability == &Mutability::Mut
Devin Jeanpierref85ae592022-04-01 04:33:57 -07001614 && !referent.is_unpin()
Devin Jeanpierre1b8f4a12022-03-22 21:29:42 +00001615 && func.name != UnqualifiedIdentifier::Destructor
1616 {
Rosica Dejanovskaaccf00b2022-04-01 13:28:04 -07001617 // TODO(b/200067242): Add a `use rust_std::pin::Pin` to the crate, and use `Pin`.
1618 Ok(quote! {self: rust_std::pin::Pin< & #lifetime #mut_ Self>})
Devin Jeanpierre149950d2022-02-22 21:02:02 +00001619 } else {
1620 Ok(quote! { & #lifetime #mut_ self })
1621 }
Lukasz Anforowicz231a3bb2022-01-12 14:05:59 +00001622 }
Lukasz Anforowicz732ca642022-02-03 20:58:38 +00001623 _ => bail!("Unexpected type of `self` parameter: {:?}", self),
Lukasz Anforowicz231a3bb2022-01-12 14:05:59 +00001624 }
1625 }
1626
Lukasz Anforowicz90bdb962022-02-14 21:07:45 +00001627 /// Returns whether the type represented by `self` implements the `Copy`
1628 /// trait.
Lukasz Anforowicza94ab702022-01-14 22:40:25 +00001629 pub fn implements_copy(&self) -> bool {
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00001630 // TODO(b/212696226): Verify results of `implements_copy` via static
1631 // assertions in the generated Rust code (because incorrect results
1632 // can silently lead to unsafe behavior).
1633 match self {
1634 RsTypeKind::Unit => true,
1635 RsTypeKind::Pointer { .. } => true,
Lukasz Anforowiczcf230fd2022-02-18 19:20:39 +00001636 RsTypeKind::FuncPtr { .. } => true,
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00001637 RsTypeKind::Reference { mutability: Mutability::Const, .. } => true,
1638 RsTypeKind::Reference { mutability: Mutability::Mut, .. } => false,
Devin Jeanpierredeea7892022-03-29 02:13:31 -07001639 RsTypeKind::RvalueReference { .. } => false,
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00001640 RsTypeKind::Record(record) => should_derive_copy(record),
1641 RsTypeKind::TypeAlias { underlying_type, .. } => underlying_type.implements_copy(),
Lukasz Anforowiczd81bea92022-02-11 08:57:58 +00001642 RsTypeKind::Other { type_args, .. } => {
Lukasz Anforowicz20651e32022-02-10 14:52:15 +00001643 // All types that may appear here without `type_args` (e.g.
1644 // primitive types like `i32`) implement `Copy`. Generic types
1645 // that may be present here (e.g. Option<...>) are `Copy` if all
1646 // of their `type_args` are `Copy`.
1647 type_args.iter().all(|t| t.implements_copy())
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00001648 }
1649 }
1650 }
Lukasz Anforowicza94ab702022-01-14 22:40:25 +00001651
Lukasz Anforowiczf9564622022-01-28 14:31:04 +00001652 pub fn is_mut_ptr_to(&self, expected_record: &Record) -> bool {
1653 match self {
1654 RsTypeKind::Pointer { pointee, mutability: Mutability::Mut, .. } => {
1655 pointee.is_record(expected_record)
1656 }
1657 _ => false,
1658 }
1659 }
1660
1661 pub fn is_ref_to(&self, expected_record: &Record) -> bool {
1662 match self {
1663 RsTypeKind::Reference { referent, .. } => referent.is_record(expected_record),
1664 _ => false,
1665 }
1666 }
1667
Lukasz Anforowicza94ab702022-01-14 22:40:25 +00001668 pub fn is_shared_ref_to(&self, expected_record: &Record) -> bool {
1669 match self {
1670 RsTypeKind::Reference { referent, mutability: Mutability::Const, .. } => {
Lukasz Anforowiczf9564622022-01-28 14:31:04 +00001671 referent.is_record(expected_record)
Lukasz Anforowicza94ab702022-01-14 22:40:25 +00001672 }
1673 _ => false,
1674 }
1675 }
Lukasz Anforowiczf9564622022-01-28 14:31:04 +00001676
1677 pub fn is_record(&self, expected_record: &Record) -> bool {
1678 match self {
1679 RsTypeKind::Record(actual_record) => actual_record.id == expected_record.id,
1680 _ => false,
1681 }
1682 }
Lukasz Anforowicz90bdb962022-02-14 21:07:45 +00001683
1684 /// Iterates over `self` and all the nested types (e.g. pointees, generic
1685 /// type args, etc.) in DFS order.
1686 pub fn dfs_iter<'ty>(&'ty self) -> impl Iterator<Item = &'ty RsTypeKind<'ir>> + '_ {
1687 RsTypeKindIter::new(self)
1688 }
1689
1690 /// Iterates over all `LifetimeId`s in `self` and in all the nested types.
1691 /// Note that the results might contain duplicate LifetimeId values (e.g.
1692 /// if the same LifetimeId is used in two `type_args`).
1693 pub fn lifetimes(&self) -> impl Iterator<Item = LifetimeId> + '_ {
1694 self.dfs_iter().filter_map(|t| match t {
Devin Jeanpierred2b7a8f2022-04-01 04:37:16 -07001695 RsTypeKind::Reference { lifetime, .. } => Some(lifetime.id),
Lukasz Anforowicz90bdb962022-02-14 21:07:45 +00001696 _ => None,
1697 })
1698 }
1699}
1700
1701struct RsTypeKindIter<'ty, 'ir> {
1702 todo: Vec<&'ty RsTypeKind<'ir>>,
1703}
1704
1705impl<'ty, 'ir> RsTypeKindIter<'ty, 'ir> {
1706 pub fn new(ty: &'ty RsTypeKind<'ir>) -> Self {
1707 Self { todo: vec![ty] }
1708 }
1709}
1710
1711impl<'ty, 'ir> Iterator for RsTypeKindIter<'ty, 'ir> {
1712 type Item = &'ty RsTypeKind<'ir>;
1713
1714 fn next(&mut self) -> Option<Self::Item> {
1715 match self.todo.pop() {
1716 None => None,
1717 Some(curr) => {
1718 match curr {
1719 RsTypeKind::Unit | RsTypeKind::Record(_) => (),
1720 RsTypeKind::Pointer { pointee, .. } => self.todo.push(pointee),
1721 RsTypeKind::Reference { referent, .. } => self.todo.push(referent),
Devin Jeanpierredeea7892022-03-29 02:13:31 -07001722 RsTypeKind::RvalueReference { referent, .. } => self.todo.push(referent),
Lukasz Anforowicz90bdb962022-02-14 21:07:45 +00001723 RsTypeKind::TypeAlias { underlying_type: t, .. } => self.todo.push(t),
Lukasz Anforowiczcf230fd2022-02-18 19:20:39 +00001724 RsTypeKind::FuncPtr { return_type, param_types, .. } => {
1725 self.todo.push(return_type);
1726 self.todo.extend(param_types.iter().rev());
Devin Jeanpierre1b8f4a12022-03-22 21:29:42 +00001727 }
Lukasz Anforowicz90bdb962022-02-14 21:07:45 +00001728 RsTypeKind::Other { type_args, .. } => self.todo.extend(type_args.iter().rev()),
1729 };
1730 Some(curr)
1731 }
1732 }
1733 }
Lukasz Anforowicz40c2eb82022-01-11 18:22:31 +00001734}
1735
Lukasz Anforowicz95551272022-01-20 00:02:24 +00001736fn format_lifetime_name(lifetime_name: &str) -> TokenStream {
1737 let lifetime =
1738 syn::Lifetime::new(&format!("'{}", lifetime_name), proc_macro2::Span::call_site());
1739 quote! { #lifetime }
1740}
1741
Devin Jeanpierre9886fb42022-04-01 04:31:20 -07001742fn format_rs_type(ty: &ir::RsType, ir: &IR) -> Result<TokenStream> {
Lukasz Anforowicz40c2eb82022-01-11 18:22:31 +00001743 RsTypeKind::new(ty, ir)
Devin Jeanpierred2b7a8f2022-04-01 04:37:16 -07001744 .map(|kind| kind.format(ir))
Lukasz Anforowicz40c2eb82022-01-11 18:22:31 +00001745 .with_context(|| format!("Failed to format Rust type {:?}", ty))
Devin Jeanpierre7a7328e2021-09-17 07:10:08 +00001746}
1747
Googler6a0a5252022-01-11 14:08:09 +00001748fn cc_type_name_for_item(item: &ir::Item) -> Result<TokenStream> {
Devin Jeanpierre1b8f4a12022-03-22 21:29:42 +00001749 Ok(match item {
Lukasz Anforowicz4c3a2cc2022-03-11 00:24:49 +00001750 Item::Record(record) => {
1751 let ident = format_cc_ident(&record.cc_name);
1752 quote! { class #ident }
Devin Jeanpierre1b8f4a12022-03-22 21:29:42 +00001753 }
Lukasz Anforowicz4c3a2cc2022-03-11 00:24:49 +00001754 Item::TypeAlias(type_alias) => {
1755 let ident = format_cc_ident(&type_alias.identifier.identifier);
1756 quote! { #ident }
Devin Jeanpierre1b8f4a12022-03-22 21:29:42 +00001757 }
Googler6a0a5252022-01-11 14:08:09 +00001758 _ => bail!("Item does not define a type: {:?}", item),
Lukasz Anforowicz4c3a2cc2022-03-11 00:24:49 +00001759 })
Googler6a0a5252022-01-11 14:08:09 +00001760}
1761
Lukasz Anforowicz71e9b592022-03-04 19:03:02 +00001762// Maps a Rust ABI [1] into a Clang attribute. See also
1763// `ConvertCcCallConvIntoRsApi` in importer.cc.
1764// [1]
1765// https://doc.rust-lang.org/reference/items/functions.html#extern-function-qualifier
1766fn format_cc_call_conv_as_clang_attribute(rs_abi: &str) -> Result<TokenStream> {
1767 match rs_abi {
1768 "cdecl" => Ok(quote! {}),
1769 "fastcall" => Ok(quote! { __attribute__((fastcall)) }),
1770 "stdcall" => Ok(quote! { __attribute__((stdcall)) }),
1771 "thiscall" => Ok(quote! { __attribute__((thiscall)) }),
1772 "vectorcall" => Ok(quote! { __attribute__((vectorcall)) }),
1773 _ => bail!("Unsupported ABI: {}", rs_abi),
1774 }
1775}
1776
Marcel Hlopkoc0956cf2021-11-29 08:31:28 +00001777fn format_cc_type(ty: &ir::CcType, ir: &IR) -> Result<TokenStream> {
Devin Jeanpierre09c6f452021-09-29 07:34:24 +00001778 let const_fragment = if ty.is_const {
Devin Jeanpierre184f9ac2021-09-17 13:47:03 +00001779 quote! {const}
1780 } else {
1781 quote! {}
1782 };
Marcel Hlopkoc0956cf2021-11-29 08:31:28 +00001783 if let Some(ref name) = ty.name {
1784 match name.as_str() {
1785 "*" => {
Googlerff7fc232021-12-02 09:43:00 +00001786 if ty.type_args.len() != 1 {
1787 bail!("Invalid pointer type (need exactly 1 type argument): {:?}", ty);
Marcel Hlopkoc0956cf2021-11-29 08:31:28 +00001788 }
Googlerff7fc232021-12-02 09:43:00 +00001789 assert_eq!(ty.type_args.len(), 1);
1790 let nested_type = format_cc_type(&ty.type_args[0], ir)?;
Marcel Hlopkoc0956cf2021-11-29 08:31:28 +00001791 Ok(quote! {#nested_type * #const_fragment})
Devin Jeanpierre7a7328e2021-09-17 07:10:08 +00001792 }
Lukasz Anforowicz275fa922022-01-05 16:13:20 +00001793 "&" => {
1794 if ty.type_args.len() != 1 {
1795 bail!("Invalid reference type (need exactly 1 type argument): {:?}", ty);
1796 }
1797 let nested_type = format_cc_type(&ty.type_args[0], ir)?;
1798 Ok(quote! {#nested_type &})
1799 }
Devin Jeanpierredeea7892022-03-29 02:13:31 -07001800 "&&" => {
1801 if ty.type_args.len() != 1 {
1802 bail!("Invalid rvalue reference type (need exactly 1 type argument): {:?}", ty);
1803 }
1804 let nested_type = format_cc_type(&ty.type_args[0], ir)?;
1805 Ok(quote! {#nested_type &&})
1806 }
Lukasz Anforowicz71e9b592022-03-04 19:03:02 +00001807 cc_type_name => match cc_type_name.strip_prefix("#funcValue ") {
1808 None => {
1809 if !ty.type_args.is_empty() {
1810 bail!("Type not yet supported: {:?}", ty);
1811 }
1812 let idents = cc_type_name.split_whitespace().map(format_cc_ident);
1813 Ok(quote! {#( #idents )* #const_fragment})
Devin Jeanpierre1b8f4a12022-03-22 21:29:42 +00001814 }
Lukasz Anforowicz71e9b592022-03-04 19:03:02 +00001815 Some(abi) => match ty.type_args.split_last() {
1816 None => bail!("funcValue type without a return type: {:?}", ty),
1817 Some((ret_type, param_types)) => {
1818 let ret_type = format_cc_type(ret_type, ir)?;
1819 let param_types = param_types
1820 .iter()
1821 .map(|t| format_cc_type(t, ir))
1822 .collect::<Result<Vec<_>>>()?;
1823 let attr = format_cc_call_conv_as_clang_attribute(abi)?;
1824 // `type_identity_t` is used below to avoid having to
1825 // emit spiral-like syntax where some syntax elements of
1826 // an inner type (e.g. function type as below) can
1827 // surround syntax elements of an outer type (e.g. a
1828 // pointer type). Compare: `int (*foo)(int, int)` VS
1829 // `type_identity_t<int(int, int)>* foo`.
1830 Ok(quote! { rs_api_impl_support::type_identity_t<
Devin Jeanpierre1b8f4a12022-03-22 21:29:42 +00001831 #ret_type ( #( #param_types ),* ) #attr
1832 > })
1833 }
1834 },
1835 },
Devin Jeanpierre7a7328e2021-09-17 07:10:08 +00001836 }
Marcel Hlopkoc0956cf2021-11-29 08:31:28 +00001837 } else {
Googler6a0a5252022-01-11 14:08:09 +00001838 let item = ir.item_for_type(ty)?;
1839 let type_name = cc_type_name_for_item(item)?;
1840 Ok(quote! {#const_fragment #type_name})
Devin Jeanpierre7a7328e2021-09-17 07:10:08 +00001841 }
1842}
1843
Marcel Hlopkoa0f38662021-12-03 08:45:26 +00001844fn cc_struct_layout_assertion(record: &Record, ir: &IR) -> TokenStream {
Googler6a0a5252022-01-11 14:08:09 +00001845 if !ir.is_current_target(&record.owning_target) && !ir.is_stdlib_target(&record.owning_target) {
Marcel Hlopkoa0f38662021-12-03 08:45:26 +00001846 return quote! {};
1847 }
Lukasz Anforowicz4c3a2cc2022-03-11 00:24:49 +00001848 let record_ident = format_cc_ident(&record.cc_name);
Googler5ea88642021-09-29 08:05:59 +00001849 let size = Literal::usize_unsuffixed(record.size);
1850 let alignment = Literal::usize_unsuffixed(record.alignment);
Lukasz Anforowicz74704712021-12-22 15:30:31 +00001851 let field_assertions =
1852 record.fields.iter().filter(|f| f.access == AccessSpecifier::Public).map(|field| {
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +00001853 let field_ident = format_cc_ident(&field.identifier.identifier);
Lukasz Anforowicz74704712021-12-22 15:30:31 +00001854 let offset = Literal::usize_unsuffixed(field.offset);
1855 // The IR contains the offset in bits, while C++'s offsetof()
1856 // returns the offset in bytes, so we need to convert.
1857 quote! {
Googler972d3582022-01-11 10:17:22 +00001858 static_assert(offsetof(class #record_ident, #field_ident) * 8 == #offset);
Lukasz Anforowicz74704712021-12-22 15:30:31 +00001859 }
1860 });
Googler5ea88642021-09-29 08:05:59 +00001861 quote! {
Googler972d3582022-01-11 10:17:22 +00001862 static_assert(sizeof(class #record_ident) == #size);
1863 static_assert(alignof(class #record_ident) == #alignment);
Googler5ea88642021-09-29 08:05:59 +00001864 #( #field_assertions )*
1865 }
1866}
1867
Devin Jeanpierre58181ac2022-02-14 21:30:05 +00001868// Returns the accessor functions for no_unique_address member variables.
1869fn cc_struct_no_unique_address_impl(record: &Record, ir: &IR) -> Result<TokenStream> {
1870 let mut fields = vec![];
1871 let mut types = vec![];
1872 for field in &record.fields {
1873 if field.access != AccessSpecifier::Public || !field.is_no_unique_address {
1874 continue;
1875 }
1876 fields.push(make_rs_ident(&field.identifier.identifier));
Devin Jeanpierre9886fb42022-04-01 04:31:20 -07001877 types.push(format_rs_type(&field.type_.rs_type, ir).with_context(
Devin Jeanpierre58181ac2022-02-14 21:30:05 +00001878 || format!("Failed to format type for field {:?} on record {:?}", field, record),
1879 )?)
1880 }
1881
1882 if fields.is_empty() {
1883 return Ok(quote! {});
1884 }
1885
Lukasz Anforowicz4c3a2cc2022-03-11 00:24:49 +00001886 let ident = make_rs_ident(&record.rs_name);
Devin Jeanpierre58181ac2022-02-14 21:30:05 +00001887 Ok(quote! {
1888 impl #ident {
1889 #(
1890 pub fn #fields(&self) -> &#types {
1891 unsafe {&* (&self.#fields as *const _ as *const #types)}
1892 }
1893 )*
1894 }
1895 })
1896}
1897
Devin Jeanpierre56777022022-02-03 01:57:15 +00001898/// Returns the implementation of base class conversions, for converting a type
1899/// to its unambiguous public base classes.
1900///
1901/// TODO(b/216195042): Implement this in terms of a supporting trait which casts
1902/// raw pointers. Then, we would have blanket impls for reference, pinned mut
1903/// reference, etc. conversion. The current version is just enough to test the
1904/// logic in importer.
1905//
1906// TODO(b/216195042): Should this use, like, AsRef/AsMut (and some equivalent
1907// for Pin)?
1908fn cc_struct_upcast_impl(record: &Record, ir: &IR) -> Result<TokenStream> {
1909 let mut impls = Vec::with_capacity(record.unambiguous_public_bases.len());
1910 for base in &record.unambiguous_public_bases {
Lukasz Anforowicz44047252022-03-23 13:04:48 +00001911 let base_record: &Record = ir
1912 .find_decl(base.base_record_id)
1913 .with_context(|| format!("Can't find a base record of {:?}", record))?;
Devin Jeanpierre56777022022-02-03 01:57:15 +00001914 if let Some(offset) = base.offset {
1915 let offset = Literal::i64_unsuffixed(offset);
1916 // TODO(b/216195042): Correctly handle imported records, lifetimes.
Lukasz Anforowicz4c3a2cc2022-03-11 00:24:49 +00001917 let base_name = make_rs_ident(&base_record.rs_name);
1918 let derived_name = make_rs_ident(&record.rs_name);
Devin Jeanpierre56777022022-02-03 01:57:15 +00001919 impls.push(quote! {
1920 impl<'a> From<&'a #derived_name> for &'a #base_name {
1921 fn from(x: &'a #derived_name) -> Self {
1922 unsafe {
1923 &*((x as *const _ as *const u8).offset(#offset) as *const #base_name)
1924 }
1925 }
1926 }
1927 });
1928 } else {
1929 // TODO(b/216195042): determine offset dynamically / use a dynamic
1930 // cast. This requires a new C++ function to be
1931 // generated, so that we have something to call.
1932 }
1933 }
1934
1935 Ok(quote! {
1936 #(#impls)*
1937 })
1938}
1939
Googlera675ae02021-12-07 08:04:59 +00001940fn thunk_ident(func: &Func) -> Ident {
1941 format_ident!("__rust_thunk__{}", func.mangled_name)
Devin Jeanpierref2ec8712021-10-13 20:47:16 +00001942}
1943
Marcel Hlopko89547752021-12-10 09:39:41 +00001944fn generate_rs_api_impl(ir: &IR) -> Result<TokenStream> {
Michael Forsterbee84482021-10-13 08:35:38 +00001945 // This function uses quote! to generate C++ source code out of convenience.
1946 // This is a bold idea so we have to continously evaluate if it still makes
1947 // sense or the cost of working around differences in Rust and C++ tokens is
1948 // greather than the value added.
Marcel Hlopko3164eee2021-08-24 20:09:22 +00001949 //
Michael Forsterbee84482021-10-13 08:35:38 +00001950 // See rs_bindings_from_cc/
1951 // token_stream_printer.rs for a list of supported placeholders.
Marcel Hlopko3164eee2021-08-24 20:09:22 +00001952 let mut thunks = vec![];
Michael Forster7ef80732021-10-01 18:12:19 +00001953 for func in ir.functions() {
Lukasz Anforowiczdd9ae0f2022-02-17 15:52:53 +00001954 if can_skip_cc_thunk(func) {
Marcel Hlopko3164eee2021-08-24 20:09:22 +00001955 continue;
1956 }
1957
Googlera675ae02021-12-07 08:04:59 +00001958 let thunk_ident = thunk_ident(func);
Devin Jeanpierref2ec8712021-10-13 20:47:16 +00001959 let implementation_function = match &func.name {
Lukasz Anforowicz9c663ca2022-02-09 01:33:31 +00001960 UnqualifiedIdentifier::Operator(op) => {
1961 let name = syn::parse_str::<TokenStream>(&op.name)?;
1962 quote! { operator #name }
1963 }
Devin Jeanpierref2ec8712021-10-13 20:47:16 +00001964 UnqualifiedIdentifier::Identifier(id) => {
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +00001965 let fn_ident = format_cc_ident(&id.identifier);
Lukasz Anforowiczaab8ad22021-12-19 20:29:26 +00001966 let static_method_metadata = func
1967 .member_func_metadata
1968 .as_ref()
1969 .filter(|meta| meta.instance_method_metadata.is_none());
1970 match static_method_metadata {
1971 None => quote! {#fn_ident},
1972 Some(meta) => {
Devin Jeanpierre1b8f4a12022-03-22 21:29:42 +00001973 let record_ident = format_cc_ident(&meta.find_record(ir)?.cc_name);
Lukasz Anforowiczaab8ad22021-12-19 20:29:26 +00001974 quote! { #record_ident :: #fn_ident }
1975 }
1976 }
Devin Jeanpierref2ec8712021-10-13 20:47:16 +00001977 }
Lukasz Anforowicz7b0042d2022-01-06 23:00:19 +00001978 // Use `destroy_at` to avoid needing to spell out the class name. Destructor identiifers
Devin Jeanpierrecc6cf092021-12-16 04:31:14 +00001979 // use the name of the type itself, without namespace qualification, template
1980 // parameters, or aliases. We do not need to use that naming scheme anywhere else in
1981 // the bindings, and it can be difficult (impossible?) to spell in the general case. By
1982 // using destroy_at, we avoid needing to determine or remember what the correct spelling
Lukasz Anforowicz7b0042d2022-01-06 23:00:19 +00001983 // is. Similar arguments apply to `construct_at`.
Lukasz Anforowicze643ec92021-12-22 15:45:15 +00001984 UnqualifiedIdentifier::Constructor => {
Lukasz Anforowicz7b0042d2022-01-06 23:00:19 +00001985 quote! { rs_api_impl_support::construct_at }
Lukasz Anforowicze643ec92021-12-22 15:45:15 +00001986 }
Devin Jeanpierref2ec8712021-10-13 20:47:16 +00001987 UnqualifiedIdentifier::Destructor => quote! {std::destroy_at},
Devin Jeanpierref2ec8712021-10-13 20:47:16 +00001988 };
Marcel Hlopkoc0956cf2021-11-29 08:31:28 +00001989 let return_type_name = format_cc_type(&func.return_type.cc_type, ir)?;
Lukasz Anforowicze643ec92021-12-22 15:45:15 +00001990 let return_stmt = if func.return_type.cc_type.is_void() {
1991 quote! {}
1992 } else {
1993 quote! { return }
1994 };
Marcel Hlopko3164eee2021-08-24 20:09:22 +00001995
1996 let param_idents =
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +00001997 func.params.iter().map(|p| format_cc_ident(&p.identifier.identifier)).collect_vec();
Marcel Hlopko3164eee2021-08-24 20:09:22 +00001998
Devin Jeanpierre09c6f452021-09-29 07:34:24 +00001999 let param_types = func
2000 .params
2001 .iter()
Marcel Hlopkoc0956cf2021-11-29 08:31:28 +00002002 .map(|p| format_cc_type(&p.type_.cc_type, ir))
Devin Jeanpierre09c6f452021-09-29 07:34:24 +00002003 .collect::<Result<Vec<_>>>()?;
Marcel Hlopko3164eee2021-08-24 20:09:22 +00002004
Lukasz Anforowiczb3d89aa2022-01-12 14:35:52 +00002005 let needs_this_deref = match &func.member_func_metadata {
2006 None => false,
2007 Some(meta) => match &func.name {
2008 UnqualifiedIdentifier::Constructor | UnqualifiedIdentifier::Destructor => false,
Marcel Hlopko14ee3c82022-02-09 09:46:23 +00002009 UnqualifiedIdentifier::Identifier(_) | UnqualifiedIdentifier::Operator(_) => {
2010 meta.instance_method_metadata.is_some()
2011 }
Lukasz Anforowiczb3d89aa2022-01-12 14:35:52 +00002012 },
2013 };
Devin Jeanpierredeea7892022-03-29 02:13:31 -07002014
2015 let arg_expressions: Vec<_> = param_idents
2016 .iter()
2017 .map(
2018 // Forward references along. (If the parameter is a value, not a reference, this
2019 // will create an lvalue reference, and still do the right thing.)
2020 |ident| quote! {std::forward<decltype(#ident)>(#ident)},
2021 )
2022 .collect();
Lukasz Anforowiczb3d89aa2022-01-12 14:35:52 +00002023 let (implementation_function, arg_expressions) = if !needs_this_deref {
Devin Jeanpierredeea7892022-03-29 02:13:31 -07002024 (implementation_function, arg_expressions.clone())
Lukasz Anforowiczb3d89aa2022-01-12 14:35:52 +00002025 } else {
2026 let this_param = func
2027 .params
2028 .first()
2029 .ok_or_else(|| anyhow!("Instance methods must have `__this` param."))?;
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +00002030 let this_arg = format_cc_ident(&this_param.identifier.identifier);
Lukasz Anforowiczb3d89aa2022-01-12 14:35:52 +00002031 (
2032 quote! { #this_arg -> #implementation_function},
Devin Jeanpierredeea7892022-03-29 02:13:31 -07002033 arg_expressions.iter().skip(1).cloned().collect_vec(),
Lukasz Anforowiczb3d89aa2022-01-12 14:35:52 +00002034 )
2035 };
2036
Marcel Hlopko3164eee2021-08-24 20:09:22 +00002037 thunks.push(quote! {
2038 extern "C" #return_type_name #thunk_ident( #( #param_types #param_idents ),* ) {
Lukasz Anforowiczb3d89aa2022-01-12 14:35:52 +00002039 #return_stmt #implementation_function( #( #arg_expressions ),* );
Marcel Hlopko3164eee2021-08-24 20:09:22 +00002040 }
2041 });
2042 }
2043
Marcel Hlopkoa0f38662021-12-03 08:45:26 +00002044 let layout_assertions = ir.records().map(|record| cc_struct_layout_assertion(record, ir));
Googler5ea88642021-09-29 08:05:59 +00002045
Devin Jeanpierre231ef8d2021-10-27 10:50:44 +00002046 let mut standard_headers = <BTreeSet<Ident>>::new();
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +00002047 standard_headers.insert(format_ident!("memory")); // ubiquitous.
Devin Jeanpierre231ef8d2021-10-27 10:50:44 +00002048 if ir.records().next().is_some() {
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +00002049 standard_headers.insert(format_ident!("cstddef"));
Devin Jeanpierre231ef8d2021-10-27 10:50:44 +00002050 };
Googler5ea88642021-09-29 08:05:59 +00002051
Devin Jeanpierre1b8f4a12022-03-22 21:29:42 +00002052 let mut includes = vec!["rs_bindings_from_cc/support/cxx20_backports.h"];
Lukasz Anforowicz4457baf2021-12-23 17:24:04 +00002053
Michael Forsterbee84482021-10-13 08:35:38 +00002054 // In order to generate C++ thunk in all the cases Clang needs to be able to
2055 // access declarations from public headers of the C++ library.
Lukasz Anforowicz4457baf2021-12-23 17:24:04 +00002056 includes.extend(ir.used_headers().map(|i| &i.name as &str));
Marcel Hlopko3164eee2021-08-24 20:09:22 +00002057
Marcel Hlopko89547752021-12-10 09:39:41 +00002058 Ok(quote! {
Googler5ea88642021-09-29 08:05:59 +00002059 #( __HASH_TOKEN__ include <#standard_headers> __NEWLINE__)*
Devin Jeanpierre7c74f842022-02-03 07:08:06 +00002060 __NEWLINE__
Michael Forsterdb8101a2021-10-08 06:56:03 +00002061 #( __HASH_TOKEN__ include #includes __NEWLINE__)* __NEWLINE__
Marcel Hlopkob8069ae2022-02-19 09:31:00 +00002062 __HASH_TOKEN__ pragma clang diagnostic push __NEWLINE__
2063 // Disable Clang thread-safety-analysis warnings that would otherwise
2064 // complain about thunks that call mutex locking functions in an unpaired way.
2065 __HASH_TOKEN__ pragma clang diagnostic ignored "-Wthread-safety-analysis" __NEWLINE__
Marcel Hlopko3164eee2021-08-24 20:09:22 +00002066
Michael Forsterdb8101a2021-10-08 06:56:03 +00002067 #( #thunks )* __NEWLINE__ __NEWLINE__
Googler5ea88642021-09-29 08:05:59 +00002068
Michael Forsterdb8101a2021-10-08 06:56:03 +00002069 #( #layout_assertions __NEWLINE__ __NEWLINE__ )*
Marcel Hlopkoc6b726c2021-10-07 06:53:09 +00002070
Marcel Hlopkob8069ae2022-02-19 09:31:00 +00002071 __NEWLINE__
2072 __HASH_TOKEN__ pragma clang diagnostic pop __NEWLINE__
Marcel Hlopkoc6b726c2021-10-07 06:53:09 +00002073 // To satisfy http://cs/symbol:devtools.metadata.Presubmit.CheckTerminatingNewline check.
2074 __NEWLINE__
Marcel Hlopko89547752021-12-10 09:39:41 +00002075 })
Marcel Hlopko3164eee2021-08-24 20:09:22 +00002076}
2077
Marcel Hlopko42abfc82021-08-09 07:03:17 +00002078#[cfg(test)]
2079mod tests {
Devin Jeanpierre45cb1162021-10-27 10:54:28 +00002080 use super::*;
Michael Forstered642022021-10-04 09:48:25 +00002081 use anyhow::anyhow;
Dmitri Gribenko67cbfd22022-03-24 13:39:34 +00002082 use ir_testing::{ir_from_cc, ir_from_cc_dependency, ir_record, retrieve_func};
Lukasz Anforowicz282bfd72022-03-22 22:38:17 +00002083 use static_assertions::{assert_impl_all, assert_not_impl_all};
Marcel Hlopko89547752021-12-10 09:39:41 +00002084 use token_stream_matchers::{
Lukasz Anforowicz71e9b592022-03-04 19:03:02 +00002085 assert_cc_matches, assert_cc_not_matches, assert_ir_matches, assert_rs_matches,
2086 assert_rs_not_matches,
Devin Jeanpierre7e9a1de2021-12-03 08:04:22 +00002087 };
Michael Forsterdb8101a2021-10-08 06:56:03 +00002088 use token_stream_printer::tokens_to_string;
Marcel Hlopko42abfc82021-08-09 07:03:17 +00002089
2090 #[test]
Marcel Hlopkob8069ae2022-02-19 09:31:00 +00002091 fn test_disable_thread_safety_warnings() -> Result<()> {
2092 let ir = ir_from_cc("inline void foo() {}")?;
2093 let rs_api_impl = generate_rs_api_impl(&ir)?;
2094 assert_cc_matches!(
2095 rs_api_impl,
2096 quote! {
2097 ...
2098 __HASH_TOKEN__ pragma clang diagnostic push
2099 __HASH_TOKEN__ pragma clang diagnostic ignored "-Wthread-safety-analysis"
2100 ...
2101
2102 __HASH_TOKEN__ pragma clang diagnostic pop
2103 ...
2104 }
2105 );
2106 Ok(())
2107 }
2108
2109 #[test]
Marcel Hlopko3b9bf9e2021-11-29 08:25:14 +00002110 // TODO(hlopko): Move this test to a more principled place where it can access
2111 // `ir_testing`.
2112 fn test_duplicate_decl_ids_err() {
2113 let mut r1 = ir_record("R1");
Rosica Dejanovskad638cf52022-03-23 15:45:01 +00002114 r1.id = ItemId(42);
Marcel Hlopko3b9bf9e2021-11-29 08:25:14 +00002115 let mut r2 = ir_record("R2");
Rosica Dejanovskad638cf52022-03-23 15:45:01 +00002116 r2.id = ItemId(42);
Marcel Hlopko3b9bf9e2021-11-29 08:25:14 +00002117 let result = make_ir_from_items([r1.into(), r2.into()]);
2118 assert!(result.is_err());
2119 assert!(result.unwrap_err().to_string().contains("Duplicate decl_id found in"));
2120 }
2121
2122 #[test]
Marcel Hlopko45fba972021-08-23 19:52:20 +00002123 fn test_simple_function() -> Result<()> {
Marcel Hlopko89547752021-12-10 09:39:41 +00002124 let ir = ir_from_cc("int Add(int a, int b);")?;
2125 let rs_api = generate_rs_api(&ir)?;
2126 assert_rs_matches!(
2127 rs_api,
2128 quote! {
Michael Forsterdb8101a2021-10-08 06:56:03 +00002129 #[inline(always)]
Marcel Hlopko89547752021-12-10 09:39:41 +00002130 pub fn Add(a: i32, b: i32) -> i32 {
Googlera675ae02021-12-07 08:04:59 +00002131 unsafe { crate::detail::__rust_thunk___Z3Addii(a, b) }
Marcel Hlopko89547752021-12-10 09:39:41 +00002132 }
2133 }
2134 );
2135 assert_rs_matches!(
2136 rs_api,
2137 quote! {
Michael Forsterdb8101a2021-10-08 06:56:03 +00002138 mod detail {
Googler55647142022-01-11 12:37:39 +00002139 #[allow(unused_imports)]
Devin Jeanpierred4dde0e2021-10-13 20:48:25 +00002140 use super::*;
Michael Forsterdb8101a2021-10-08 06:56:03 +00002141 extern "C" {
2142 #[link_name = "_Z3Addii"]
Googlera675ae02021-12-07 08:04:59 +00002143 pub(crate) fn __rust_thunk___Z3Addii(a: i32, b: i32) -> i32;
Marcel Hlopko89547752021-12-10 09:39:41 +00002144 }
2145 }
2146 }
Marcel Hlopko42abfc82021-08-09 07:03:17 +00002147 );
Michael Forsterdb8101a2021-10-08 06:56:03 +00002148
Marcel Hlopko89547752021-12-10 09:39:41 +00002149 assert_cc_not_matches!(generate_rs_api_impl(&ir)?, quote! {__rust_thunk___Z3Addii});
Michael Forsterdb8101a2021-10-08 06:56:03 +00002150
Marcel Hlopko3164eee2021-08-24 20:09:22 +00002151 Ok(())
2152 }
2153
2154 #[test]
2155 fn test_inline_function() -> Result<()> {
Marcel Hlopko89547752021-12-10 09:39:41 +00002156 let ir = ir_from_cc("inline int Add(int a, int b);")?;
2157 let rs_api = generate_rs_api(&ir)?;
2158 assert_rs_matches!(
2159 rs_api,
2160 quote! {
Michael Forsterdb8101a2021-10-08 06:56:03 +00002161 #[inline(always)]
Marcel Hlopko89547752021-12-10 09:39:41 +00002162 pub fn Add(a: i32, b: i32) -> i32 {
Googlera675ae02021-12-07 08:04:59 +00002163 unsafe { crate::detail::__rust_thunk___Z3Addii(a, b) }
Marcel Hlopko89547752021-12-10 09:39:41 +00002164 }
2165 }
2166 );
2167 assert_rs_matches!(
2168 rs_api,
2169 quote! {
Michael Forsterdb8101a2021-10-08 06:56:03 +00002170 mod detail {
Googler55647142022-01-11 12:37:39 +00002171 #[allow(unused_imports)]
Devin Jeanpierred4dde0e2021-10-13 20:48:25 +00002172 use super::*;
Michael Forsterdb8101a2021-10-08 06:56:03 +00002173 extern "C" {
Googlera675ae02021-12-07 08:04:59 +00002174 pub(crate) fn __rust_thunk___Z3Addii(a: i32, b: i32) -> i32;
Marcel Hlopko89547752021-12-10 09:39:41 +00002175 }
2176 }
2177 }
Marcel Hlopko3164eee2021-08-24 20:09:22 +00002178 );
2179
Marcel Hlopko89547752021-12-10 09:39:41 +00002180 assert_cc_matches!(
2181 generate_rs_api_impl(&ir)?,
2182 quote! {
Googlera675ae02021-12-07 08:04:59 +00002183 extern "C" int __rust_thunk___Z3Addii(int a, int b) {
Devin Jeanpierredeea7892022-03-29 02:13:31 -07002184 return Add(std::forward<decltype(a)>(a), std::forward<decltype(b)>(b));
Marcel Hlopko3164eee2021-08-24 20:09:22 +00002185 }
Marcel Hlopko89547752021-12-10 09:39:41 +00002186 }
Marcel Hlopko3164eee2021-08-24 20:09:22 +00002187 );
Marcel Hlopko42abfc82021-08-09 07:03:17 +00002188 Ok(())
2189 }
Marcel Hlopkob4b28742021-09-15 12:45:20 +00002190
2191 #[test]
Marcel Hlopkoa0f38662021-12-03 08:45:26 +00002192 fn test_simple_function_with_types_from_other_target() -> Result<()> {
2193 let ir = ir_from_cc_dependency(
2194 "inline ReturnStruct DoSomething(ParamStruct param);",
2195 "struct ReturnStruct {}; struct ParamStruct {};",
2196 )?;
2197
Marcel Hlopko89547752021-12-10 09:39:41 +00002198 let rs_api = generate_rs_api(&ir)?;
2199 assert_rs_matches!(
2200 rs_api,
2201 quote! {
Marcel Hlopkoa0f38662021-12-03 08:45:26 +00002202 #[inline(always)]
2203 pub fn DoSomething(param: dependency::ParamStruct)
Marcel Hlopko89547752021-12-10 09:39:41 +00002204 -> dependency::ReturnStruct {
Googlera675ae02021-12-07 08:04:59 +00002205 unsafe { crate::detail::__rust_thunk___Z11DoSomething11ParamStruct(param) }
Marcel Hlopko89547752021-12-10 09:39:41 +00002206 }
2207 }
2208 );
2209 assert_rs_matches!(
2210 rs_api,
2211 quote! {
2212 mod detail {
Googler55647142022-01-11 12:37:39 +00002213 #[allow(unused_imports)]
Marcel Hlopko89547752021-12-10 09:39:41 +00002214 use super::*;
2215 extern "C" {
2216 pub(crate) fn __rust_thunk___Z11DoSomething11ParamStruct(param: dependency::ParamStruct)
2217 -> dependency::ReturnStruct;
2218 }
2219 }}
Marcel Hlopkoa0f38662021-12-03 08:45:26 +00002220 );
2221
Marcel Hlopko89547752021-12-10 09:39:41 +00002222 assert_cc_matches!(
2223 generate_rs_api_impl(&ir)?,
2224 quote! {
Googler972d3582022-01-11 10:17:22 +00002225 extern "C" class ReturnStruct __rust_thunk___Z11DoSomething11ParamStruct(class ParamStruct param) {
Devin Jeanpierredeea7892022-03-29 02:13:31 -07002226 return DoSomething(std::forward<decltype(param)>(param));
Marcel Hlopkoa0f38662021-12-03 08:45:26 +00002227 }
Marcel Hlopko89547752021-12-10 09:39:41 +00002228 }
Marcel Hlopkoa0f38662021-12-03 08:45:26 +00002229 );
2230 Ok(())
2231 }
2232
2233 #[test]
Marcel Hlopkob4b28742021-09-15 12:45:20 +00002234 fn test_simple_struct() -> Result<()> {
Marcel Hlopko89547752021-12-10 09:39:41 +00002235 let ir = ir_from_cc(&tokens_to_string(quote! {
Devin Jeanpierre88343c72022-01-15 01:10:23 +00002236 struct SomeStruct final {
Marcel Hlopko89547752021-12-10 09:39:41 +00002237 int public_int;
2238 protected:
2239 int protected_int;
2240 private:
2241 int private_int;
2242 };
2243 })?)?;
Michael Forster028800b2021-10-05 12:39:59 +00002244
Marcel Hlopko89547752021-12-10 09:39:41 +00002245 let rs_api = generate_rs_api(&ir)?;
2246 assert_rs_matches!(
2247 rs_api,
2248 quote! {
Michael Forsterdb8101a2021-10-08 06:56:03 +00002249 #[derive(Clone, Copy)]
2250 #[repr(C)]
2251 pub struct SomeStruct {
2252 pub public_int: i32,
2253 protected_int: i32,
2254 private_int: i32,
Marcel Hlopko89547752021-12-10 09:39:41 +00002255 }
2256 }
2257 );
2258 assert_rs_matches!(
2259 rs_api,
2260 quote! {
Rosica Dejanovskaaccf00b2022-04-01 13:28:04 -07002261 const _: () = assert!(rust_std::mem::size_of::<Option<&i32>>() == rust_std::mem::size_of::<&i32>());
2262 const _: () = assert!(rust_std::mem::size_of::<SomeStruct>() == 12usize);
2263 const _: () = assert!(rust_std::mem::align_of::<SomeStruct>() == 4usize);
Lukasz Anforowicz95938f82022-03-24 13:51:54 +00002264 const _: () = { assert_impl_all!(SomeStruct: Clone); };
2265 const _: () = { assert_impl_all!(SomeStruct: Copy); };
2266 const _: () = { assert_not_impl_all!(SomeStruct: Drop); };
Googler209b10a2021-12-06 09:11:57 +00002267 const _: () = assert!(offset_of!(SomeStruct, public_int) * 8 == 0usize);
2268 const _: () = assert!(offset_of!(SomeStruct, protected_int) * 8 == 32usize);
2269 const _: () = assert!(offset_of!(SomeStruct, private_int) * 8 == 64usize);
Marcel Hlopko89547752021-12-10 09:39:41 +00002270 }
Marcel Hlopkob4b28742021-09-15 12:45:20 +00002271 );
Marcel Hlopko89547752021-12-10 09:39:41 +00002272 let rs_api_impl = generate_rs_api_impl(&ir)?;
2273 assert_cc_matches!(
2274 rs_api_impl,
2275 quote! {
Googler972d3582022-01-11 10:17:22 +00002276 extern "C" void __rust_thunk___ZN10SomeStructD1Ev(class SomeStruct * __this) {
Devin Jeanpierredeea7892022-03-29 02:13:31 -07002277 std :: destroy_at (std::forward<decltype(__this)>(__this)) ;
Marcel Hlopko89547752021-12-10 09:39:41 +00002278 }
2279 }
2280 );
2281 assert_cc_matches!(
2282 rs_api_impl,
2283 quote! {
Googler972d3582022-01-11 10:17:22 +00002284 static_assert(sizeof(class SomeStruct) == 12);
2285 static_assert(alignof(class SomeStruct) == 4);
2286 static_assert(offsetof(class SomeStruct, public_int) * 8 == 0);
Marcel Hlopko89547752021-12-10 09:39:41 +00002287 }
Googler5ea88642021-09-29 08:05:59 +00002288 );
Marcel Hlopkob4b28742021-09-15 12:45:20 +00002289 Ok(())
2290 }
Devin Jeanpierre7a7328e2021-09-17 07:10:08 +00002291
Devin Jeanpierre2ed14ec2021-10-06 11:32:19 +00002292 #[test]
Lukasz Anforowicz275fa922022-01-05 16:13:20 +00002293 fn test_ref_to_struct_in_thunk_impls() -> Result<()> {
Googler972d3582022-01-11 10:17:22 +00002294 let ir = ir_from_cc("struct S{}; inline void foo(class S& s) {} ")?;
Lukasz Anforowicz275fa922022-01-05 16:13:20 +00002295 let rs_api_impl = generate_rs_api_impl(&ir)?;
2296 assert_cc_matches!(
2297 rs_api_impl,
2298 quote! {
Googler972d3582022-01-11 10:17:22 +00002299 extern "C" void __rust_thunk___Z3fooR1S(class S& s) {
Devin Jeanpierredeea7892022-03-29 02:13:31 -07002300 foo(std::forward<decltype(s)>(s));
Lukasz Anforowicz275fa922022-01-05 16:13:20 +00002301 }
2302 }
2303 );
2304 Ok(())
2305 }
2306
2307 #[test]
2308 fn test_const_ref_to_struct_in_thunk_impls() -> Result<()> {
Googler972d3582022-01-11 10:17:22 +00002309 let ir = ir_from_cc("struct S{}; inline void foo(const class S& s) {} ")?;
Lukasz Anforowicz275fa922022-01-05 16:13:20 +00002310 let rs_api_impl = generate_rs_api_impl(&ir)?;
2311 assert_cc_matches!(
2312 rs_api_impl,
2313 quote! {
Googler972d3582022-01-11 10:17:22 +00002314 extern "C" void __rust_thunk___Z3fooRK1S(const class S& s) {
Devin Jeanpierredeea7892022-03-29 02:13:31 -07002315 foo(std::forward<decltype(s)>(s));
Lukasz Anforowicz275fa922022-01-05 16:13:20 +00002316 }
2317 }
2318 );
2319 Ok(())
2320 }
2321
2322 #[test]
Lukasz Anforowicz957cbf22022-01-05 16:14:05 +00002323 fn test_unsigned_int_in_thunk_impls() -> Result<()> {
2324 let ir = ir_from_cc("inline void foo(unsigned int i) {} ")?;
2325 let rs_api_impl = generate_rs_api_impl(&ir)?;
2326 assert_cc_matches!(
2327 rs_api_impl,
2328 quote! {
2329 extern "C" void __rust_thunk___Z3fooj(unsigned int i) {
Devin Jeanpierredeea7892022-03-29 02:13:31 -07002330 foo(std::forward<decltype(i)>(i));
Lukasz Anforowicz957cbf22022-01-05 16:14:05 +00002331 }
2332 }
2333 );
2334 Ok(())
2335 }
2336
2337 #[test]
Marcel Hlopkodd1fcb12021-12-22 14:13:59 +00002338 fn test_record_static_methods_qualify_call_in_thunk() -> Result<()> {
2339 let ir = ir_from_cc(&tokens_to_string(quote! {
2340 struct SomeStruct {
2341 static inline int some_func() { return 42; }
2342 };
2343 })?)?;
2344
2345 assert_cc_matches!(
2346 generate_rs_api_impl(&ir)?,
2347 quote! {
2348 extern "C" int __rust_thunk___ZN10SomeStruct9some_funcEv() {
2349 return SomeStruct::some_func();
2350 }
2351 }
2352 );
2353 Ok(())
2354 }
2355
2356 #[test]
Lukasz Anforowiczb3d89aa2022-01-12 14:35:52 +00002357 fn test_record_instance_methods_deref_this_in_thunk() -> Result<()> {
2358 let ir = ir_from_cc(&tokens_to_string(quote! {
2359 struct SomeStruct {
2360 inline int some_func(int arg) const { return 42 + arg; }
2361 };
2362 })?)?;
2363
2364 assert_cc_matches!(
2365 generate_rs_api_impl(&ir)?,
2366 quote! {
2367 extern "C" int __rust_thunk___ZNK10SomeStruct9some_funcEi(
2368 const class SomeStruct* __this, int arg) {
Devin Jeanpierredeea7892022-03-29 02:13:31 -07002369 return __this->some_func(std::forward<decltype(arg)>(arg));
Lukasz Anforowiczb3d89aa2022-01-12 14:35:52 +00002370 }
2371 }
2372 );
2373 Ok(())
2374 }
2375
2376 #[test]
Marcel Hlopkoa0f38662021-12-03 08:45:26 +00002377 fn test_struct_from_other_target() -> Result<()> {
2378 let ir = ir_from_cc_dependency("// intentionally empty", "struct SomeStruct {};")?;
Marcel Hlopko89547752021-12-10 09:39:41 +00002379 assert_rs_not_matches!(generate_rs_api(&ir)?, quote! { SomeStruct });
2380 assert_cc_not_matches!(generate_rs_api_impl(&ir)?, quote! { SomeStruct });
Marcel Hlopkoa0f38662021-12-03 08:45:26 +00002381 Ok(())
2382 }
2383
2384 #[test]
Devin Jeanpierre2ed14ec2021-10-06 11:32:19 +00002385 fn test_copy_derives() {
Devin Jeanpierreccfefc82021-10-27 10:54:00 +00002386 let record = ir_record("S");
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00002387 assert_eq!(generate_derives(&record), &["Clone", "Copy"]);
Devin Jeanpierre2ed14ec2021-10-06 11:32:19 +00002388 }
2389
2390 #[test]
2391 fn test_copy_derives_not_is_trivial_abi() {
Devin Jeanpierreccfefc82021-10-27 10:54:00 +00002392 let mut record = ir_record("S");
Devin Jeanpierre2ed14ec2021-10-06 11:32:19 +00002393 record.is_trivial_abi = false;
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00002394 assert_eq!(generate_derives(&record), &[""; 0]);
Devin Jeanpierre2ed14ec2021-10-06 11:32:19 +00002395 }
2396
Devin Jeanpierre88343c72022-01-15 01:10:23 +00002397 /// Even if it's trivially relocatable, !Unpin C++ type cannot be
2398 /// cloned/copied or otherwise used by value, because values would allow
2399 /// assignment into the Pin.
2400 ///
2401 /// All !Unpin C++ types, not just non trivially relocatable ones, are
2402 /// unsafe to assign in the Rust sense.
Devin Jeanpierree6e16652021-12-22 15:54:46 +00002403 #[test]
Devin Jeanpierre88343c72022-01-15 01:10:23 +00002404 fn test_copy_derives_not_final() {
Devin Jeanpierree6e16652021-12-22 15:54:46 +00002405 let mut record = ir_record("S");
Teddy Katzd2cd1422022-04-04 09:41:33 -07002406 record.is_inheritable = true;
Devin Jeanpierre88343c72022-01-15 01:10:23 +00002407 assert_eq!(generate_derives(&record), &[""; 0]);
Devin Jeanpierree6e16652021-12-22 15:54:46 +00002408 }
2409
Devin Jeanpierre2ed14ec2021-10-06 11:32:19 +00002410 #[test]
2411 fn test_copy_derives_ctor_nonpublic() {
Devin Jeanpierreccfefc82021-10-27 10:54:00 +00002412 let mut record = ir_record("S");
Devin Jeanpierre2ed14ec2021-10-06 11:32:19 +00002413 for access in [ir::AccessSpecifier::Protected, ir::AccessSpecifier::Private] {
2414 record.copy_constructor.access = access;
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00002415 assert_eq!(generate_derives(&record), &[""; 0]);
Devin Jeanpierre2ed14ec2021-10-06 11:32:19 +00002416 }
2417 }
2418
2419 #[test]
2420 fn test_copy_derives_ctor_deleted() {
Devin Jeanpierreccfefc82021-10-27 10:54:00 +00002421 let mut record = ir_record("S");
Devin Jeanpierre2ed14ec2021-10-06 11:32:19 +00002422 record.copy_constructor.definition = ir::SpecialMemberDefinition::Deleted;
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00002423 assert_eq!(generate_derives(&record), &[""; 0]);
Devin Jeanpierre2ed14ec2021-10-06 11:32:19 +00002424 }
2425
2426 #[test]
Devin Jeanpierrebe2f33b2021-10-21 12:54:19 +00002427 fn test_copy_derives_ctor_nontrivial_members() {
Devin Jeanpierreccfefc82021-10-27 10:54:00 +00002428 let mut record = ir_record("S");
Devin Jeanpierrebe2f33b2021-10-21 12:54:19 +00002429 record.copy_constructor.definition = ir::SpecialMemberDefinition::NontrivialMembers;
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00002430 assert_eq!(generate_derives(&record), &[""; 0]);
Devin Jeanpierrebe2f33b2021-10-21 12:54:19 +00002431 }
2432
2433 #[test]
2434 fn test_copy_derives_ctor_nontrivial_self() {
Devin Jeanpierreccfefc82021-10-27 10:54:00 +00002435 let mut record = ir_record("S");
Devin Jeanpierre7b62e952021-12-08 21:43:30 +00002436 record.copy_constructor.definition = ir::SpecialMemberDefinition::NontrivialUserDefined;
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00002437 assert_eq!(generate_derives(&record), &[""; 0]);
Devin Jeanpierre2ed14ec2021-10-06 11:32:19 +00002438 }
2439
Devin Jeanpierre7a7328e2021-09-17 07:10:08 +00002440 #[test]
2441 fn test_ptr_func() -> Result<()> {
Marcel Hlopko89547752021-12-10 09:39:41 +00002442 let ir = ir_from_cc(&tokens_to_string(quote! {
2443 inline int* Deref(int*const* p);
2444 })?)?;
Devin Jeanpierred6da7002021-10-21 12:55:20 +00002445
Marcel Hlopko89547752021-12-10 09:39:41 +00002446 let rs_api = generate_rs_api(&ir)?;
2447 assert_rs_matches!(
2448 rs_api,
2449 quote! {
Michael Forsterdb8101a2021-10-08 06:56:03 +00002450 #[inline(always)]
Lukasz Anforowiczf7bdd392022-01-21 00:33:39 +00002451 pub unsafe fn Deref(p: *const *mut i32) -> *mut i32 {
2452 crate::detail::__rust_thunk___Z5DerefPKPi(p)
Marcel Hlopko89547752021-12-10 09:39:41 +00002453 }
2454 }
2455 );
2456 assert_rs_matches!(
2457 rs_api,
2458 quote! {
Michael Forsterdb8101a2021-10-08 06:56:03 +00002459 mod detail {
Googler55647142022-01-11 12:37:39 +00002460 #[allow(unused_imports)]
Devin Jeanpierred4dde0e2021-10-13 20:48:25 +00002461 use super::*;
Michael Forsterdb8101a2021-10-08 06:56:03 +00002462 extern "C" {
Googlera675ae02021-12-07 08:04:59 +00002463 pub(crate) fn __rust_thunk___Z5DerefPKPi(p: *const *mut i32) -> *mut i32;
Marcel Hlopko89547752021-12-10 09:39:41 +00002464 }
2465 }
2466 }
Devin Jeanpierre7a7328e2021-09-17 07:10:08 +00002467 );
Devin Jeanpierre184f9ac2021-09-17 13:47:03 +00002468
Marcel Hlopko89547752021-12-10 09:39:41 +00002469 assert_cc_matches!(
2470 generate_rs_api_impl(&ir)?,
2471 quote! {
Googlera675ae02021-12-07 08:04:59 +00002472 extern "C" int* __rust_thunk___Z5DerefPKPi(int* const * p) {
Devin Jeanpierredeea7892022-03-29 02:13:31 -07002473 return Deref(std::forward<decltype(p)>(p));
Devin Jeanpierre184f9ac2021-09-17 13:47:03 +00002474 }
Marcel Hlopko89547752021-12-10 09:39:41 +00002475 }
Devin Jeanpierre184f9ac2021-09-17 13:47:03 +00002476 );
Devin Jeanpierre7a7328e2021-09-17 07:10:08 +00002477 Ok(())
2478 }
Michael Forstered642022021-10-04 09:48:25 +00002479
2480 #[test]
Googlerdb111532022-01-05 06:12:13 +00002481 fn test_const_char_ptr_func() -> Result<()> {
2482 // This is a regression test: We used to include the "const" in the name
2483 // of the CcType, which caused a panic in the code generator
2484 // ('"const char" is not a valid Ident').
2485 // It's therefore important that f() is inline so that we need to
2486 // generate a thunk for it (where we then process the CcType).
2487 let ir = ir_from_cc(&tokens_to_string(quote! {
2488 inline void f(const char *str);
2489 })?)?;
2490
2491 let rs_api = generate_rs_api(&ir)?;
2492 assert_rs_matches!(
2493 rs_api,
2494 quote! {
2495 #[inline(always)]
Lukasz Anforowiczf7bdd392022-01-21 00:33:39 +00002496 pub unsafe fn f(str: *const i8) {
2497 crate::detail::__rust_thunk___Z1fPKc(str)
Googlerdb111532022-01-05 06:12:13 +00002498 }
2499 }
2500 );
2501 assert_rs_matches!(
2502 rs_api,
2503 quote! {
2504 extern "C" {
2505 pub(crate) fn __rust_thunk___Z1fPKc(str: *const i8);
2506 }
2507 }
2508 );
2509
2510 assert_cc_matches!(
2511 generate_rs_api_impl(&ir)?,
2512 quote! {
Devin Jeanpierredeea7892022-03-29 02:13:31 -07002513 extern "C" void __rust_thunk___Z1fPKc(char const * str){ f(std::forward<decltype(str)>(str)) ; }
Googlerdb111532022-01-05 06:12:13 +00002514 }
2515 );
2516 Ok(())
2517 }
2518
2519 #[test]
Lukasz Anforowiczcf230fd2022-02-18 19:20:39 +00002520 fn test_func_ptr_where_params_are_primitive_types() -> Result<()> {
2521 let ir = ir_from_cc(r#" int (*get_ptr_to_func())(float, double); "#)?;
2522 let rs_api = generate_rs_api(&ir)?;
2523 let rs_api_impl = generate_rs_api_impl(&ir)?;
2524 assert_rs_matches!(
2525 rs_api,
2526 quote! {
2527 #[inline(always)]
2528 pub fn get_ptr_to_func() -> Option<extern "C" fn (f32, f64) -> i32> {
2529 unsafe { crate::detail::__rust_thunk___Z15get_ptr_to_funcv() }
2530 }
2531 }
2532 );
2533 assert_rs_matches!(
2534 rs_api,
2535 quote! {
2536 mod detail {
2537 #[allow(unused_imports)]
2538 use super::*;
2539 extern "C" {
2540 #[link_name = "_Z15get_ptr_to_funcv"]
2541 pub(crate) fn __rust_thunk___Z15get_ptr_to_funcv()
2542 -> Option<extern "C" fn(f32, f64) -> i32>;
2543 }
2544 }
2545 }
2546 );
2547 // Verify that no C++ thunk got generated.
2548 assert_cc_not_matches!(rs_api_impl, quote! { __rust_thunk___Z15get_ptr_to_funcv });
2549
2550 // TODO(b/217419782): Add another test for more exotic calling conventions /
2551 // abis.
2552
2553 // TODO(b/217419782): Add another test for pointer to a function that
2554 // takes/returns non-trivially-movable types by value. See also
2555 // <internal link>
2556
2557 Ok(())
2558 }
2559
2560 #[test]
Lukasz Anforowicz92c81c32022-03-04 19:03:56 +00002561 fn test_func_ref() -> Result<()> {
2562 let ir = ir_from_cc(r#" int (&get_ref_to_func())(float, double); "#)?;
2563 let rs_api = generate_rs_api(&ir)?;
2564 assert_rs_matches!(
2565 rs_api,
2566 quote! {
2567 #[inline(always)]
2568 pub fn get_ref_to_func() -> extern "C" fn (f32, f64) -> i32 {
2569 unsafe { crate::detail::__rust_thunk___Z15get_ref_to_funcv() }
2570 }
2571 }
2572 );
2573 Ok(())
2574 }
2575
2576 #[test]
Lukasz Anforowiczcf230fd2022-02-18 19:20:39 +00002577 fn test_func_ptr_with_non_static_lifetime() -> Result<()> {
2578 let ir = ir_from_cc(
2579 r#"
Googler53f65942022-02-23 11:23:30 +00002580 [[clang::annotate("lifetimes", "-> a")]]
Lukasz Anforowiczcf230fd2022-02-18 19:20:39 +00002581 int (*get_ptr_to_func())(float, double); "#,
2582 )?;
2583 let rs_api = generate_rs_api(&ir)?;
2584 assert_rs_matches!(
2585 rs_api,
2586 quote! {
2587 // Error while generating bindings for item 'get_ptr_to_func':
2588 // Return type is not supported: Function pointers with non-'static lifetimes are not supported: int (*)(float, double)
2589 }
2590 );
2591 Ok(())
2592 }
2593
2594 #[test]
2595 fn test_func_ptr_where_params_are_raw_ptrs() -> Result<()> {
2596 let ir = ir_from_cc(r#" const int* (*get_ptr_to_func())(const int*); "#)?;
2597 let rs_api = generate_rs_api(&ir)?;
2598 let rs_api_impl = generate_rs_api_impl(&ir)?;
2599 assert_rs_matches!(
2600 rs_api,
2601 quote! {
2602 #[inline(always)]
2603 pub fn get_ptr_to_func() -> Option<extern "C" fn (*const i32) -> *const i32> {
2604 unsafe { crate::detail::__rust_thunk___Z15get_ptr_to_funcv() }
2605 }
2606 }
2607 );
2608 assert_rs_matches!(
2609 rs_api,
2610 quote! {
2611 mod detail {
2612 #[allow(unused_imports)]
2613 use super::*;
2614 extern "C" {
2615 #[link_name = "_Z15get_ptr_to_funcv"]
2616 pub(crate) fn __rust_thunk___Z15get_ptr_to_funcv()
2617 -> Option<extern "C" fn(*const i32) -> *const i32>;
2618 }
2619 }
2620 }
2621 );
2622 // Verify that no C++ thunk got generated.
2623 assert_cc_not_matches!(rs_api_impl, quote! { __rust_thunk___Z15get_ptr_to_funcv });
2624
2625 // TODO(b/217419782): Add another test where params (and the return
2626 // type) are references with lifetimes. Something like this:
2627 // #pragma clang lifetime_elision
2628 // const int& (*get_ptr_to_func())(const int&, const int&); "#)?;
2629 // 1) Need to investigate why this fails - seeing raw pointers in Rust
2630 // seems to indicate that no lifetimes are present at the `importer.cc`
2631 // level. Maybe lifetime elision doesn't support this scenario? Unclear
Googler53f65942022-02-23 11:23:30 +00002632 // how to explicitly apply [[clang::annotate("lifetimes", "a, b -> a")]]
Lukasz Anforowiczcf230fd2022-02-18 19:20:39 +00002633 // to the _inner_ function.
2634 // 2) It is important to have 2 reference parameters, so see if the problem
2635 // of passing `lifetimes` by value would have been caught - see:
2636 // cl/428079010/depot/rs_bindings_from_cc/
2637 // importer.cc?version=s6#823
2638
2639 // TODO(b/217419782): Decide what to do if the C++ pointer is *not*
2640 // annotated with a lifetime - emit `unsafe fn(...) -> ...` in that
2641 // case?
2642
2643 Ok(())
2644 }
2645
2646 #[test]
Lukasz Anforowicz71e9b592022-03-04 19:03:02 +00002647 fn test_func_ptr_with_custom_abi() -> Result<()> {
2648 let ir = ir_from_cc(r#" int (*get_ptr_to_func())(float, double) [[clang::vectorcall]]; "#)?;
2649
2650 // Verify that the test input correctly represents what we intend to
2651 // test - we want [[clang::vectorcall]] to apply to the returned
2652 // function pointer, but *not* apply to the `get_ptr_to_func` function.
Lukasz Anforowicz71e9b592022-03-04 19:03:02 +00002653 assert_ir_matches!(
2654 ir,
2655 quote! {
2656 Func(Func {
2657 name: "get_ptr_to_func", ...
2658 return_type: MappedType {
2659 rs_type: RsType {
2660 name: Some("Option"), ...
2661 type_args: [RsType { name: Some("#funcPtr vectorcall"), ... }], ...
2662 },
2663 cc_type: CcType {
2664 name: Some("*"), ...
2665 type_args: [CcType { name: Some("#funcValue vectorcall"), ... }], ...
2666 },
Lukasz Anforowicz0b6a6ac2022-03-22 22:32:23 +00002667 }, ...
2668 has_c_calling_convention: true, ...
Lukasz Anforowicz71e9b592022-03-04 19:03:02 +00002669 }),
2670 }
2671 );
2672
2673 // Check that the custom "vectorcall" ABI gets propagated into the
2674 // return type (i.e. into `extern "vectorcall" fn`).
2675 let rs_api = generate_rs_api(&ir)?;
2676 assert_rs_matches!(
2677 rs_api,
2678 quote! {
2679 #[inline(always)]
2680 pub fn get_ptr_to_func() -> Option<extern "vectorcall" fn (f32, f64) -> i32> {
2681 unsafe { crate::detail::__rust_thunk___Z15get_ptr_to_funcv() }
2682 }
2683 }
2684 );
2685
2686 // The usual `extern "C"` ABI should be used for "get_ptr_to_func".
2687 assert_rs_matches!(
2688 rs_api,
2689 quote! {
2690 mod detail {
2691 #[allow(unused_imports)]
2692 use super::*;
2693 extern "C" {
2694 #[link_name = "_Z15get_ptr_to_funcv"]
2695 pub(crate) fn __rust_thunk___Z15get_ptr_to_funcv()
2696 -> Option<extern "vectorcall" fn(f32, f64) -> i32>;
2697 }
2698 }
2699 }
2700 );
2701
2702 // Verify that no C++ thunk got generated.
2703 let rs_api_impl = generate_rs_api_impl(&ir)?;
2704 assert_cc_not_matches!(rs_api_impl, quote! { __rust_thunk___Z15get_ptr_to_funcv });
2705 Ok(())
2706 }
2707
2708 #[test]
2709 fn test_func_ptr_thunk() -> Result<()> {
2710 // Using an `inline` keyword forces generation of a C++ thunk in
2711 // `rs_api_impl` (i.e. exercises `format_cc_type` and similar code).
2712 let ir = ir_from_cc(
2713 r#"
2714 int multiply(int x, int y);
2715 inline int (*inline_get_pointer_to_function())(int, int) {
2716 return multiply;
2717 }
2718 "#,
2719 )?;
2720 let rs_api_impl = generate_rs_api_impl(&ir)?;
2721 assert_cc_matches!(
2722 rs_api_impl,
2723 quote! {
2724 extern "C" rs_api_impl_support::type_identity_t<int(int , int)>*
2725 __rust_thunk___Z30inline_get_pointer_to_functionv() {
2726 return inline_get_pointer_to_function();
2727 }
2728 }
2729 );
2730 Ok(())
2731 }
2732
2733 #[test]
Lukasz Anforowicz0b6a6ac2022-03-22 22:32:23 +00002734 fn test_func_ptr_with_custom_abi_thunk() -> Result<()> {
2735 // Using an `inline` keyword forces generation of a C++ thunk in
2736 // `rs_api_impl` (i.e. exercises `format_cc_type`,
2737 // `format_cc_call_conv_as_clang_attribute` and similar code).
2738 let ir = ir_from_cc(
2739 r#"
2740 inline int (*inline_get_ptr_to_func())(float, double) [[clang::vectorcall]];
2741 "#,
2742 )?;
2743
2744 // Verify that the test input correctly represents what we intend to
2745 // test - we want [[clang::vectorcall]] to apply to the returned
2746 // function pointer, but *not* apply to the `get_ptr_to_func` function.
2747 assert_ir_matches!(
2748 ir,
2749 quote! {
2750 Func(Func {
2751 name: "inline_get_ptr_to_func", ...
2752 return_type: MappedType {
2753 rs_type: RsType {
2754 name: Some("Option"), ...
2755 type_args: [RsType { name: Some("#funcPtr vectorcall"), ... }], ...
2756 },
2757 cc_type: CcType {
2758 name: Some("*"), ...
2759 type_args: [CcType { name: Some("#funcValue vectorcall"), ... }], ...
2760 },
2761 }, ...
2762 has_c_calling_convention: true, ...
2763 }),
2764 }
2765 );
2766
2767 // This test is quite similar to `test_func_ptr_thunk` - the main
2768 // difference is verification of the `__attribute__((vectorcall))` in
2769 // the expected signature of the generated thunk below.
2770 let rs_api_impl = generate_rs_api_impl(&ir)?;
2771 assert_cc_matches!(
2772 rs_api_impl,
2773 quote! {
2774 extern "C" rs_api_impl_support::type_identity_t<
2775 int(float , double) __attribute__((vectorcall))
2776 >* __rust_thunk___Z22inline_get_ptr_to_funcv() {
2777 return inline_get_ptr_to_func();
2778 }
2779 }
2780 );
2781 Ok(())
2782 }
2783
2784 #[test]
Michael Forstered642022021-10-04 09:48:25 +00002785 fn test_item_order() -> Result<()> {
Marcel Hlopko89547752021-12-10 09:39:41 +00002786 let ir = ir_from_cc(
2787 "int first_func();
2788 struct FirstStruct {};
2789 int second_func();
2790 struct SecondStruct {};",
2791 )?;
Michael Forstered642022021-10-04 09:48:25 +00002792
Marcel Hlopko89547752021-12-10 09:39:41 +00002793 let rs_api = rs_tokens_to_formatted_string(generate_rs_api(&ir)?)?;
2794
Lukasz Anforowiczdd9ae0f2022-02-17 15:52:53 +00002795 let idx = |s: &str| rs_api.find(s).ok_or_else(|| anyhow!("'{}' missing", s));
Michael Forstered642022021-10-04 09:48:25 +00002796
2797 let f1 = idx("fn first_func")?;
2798 let f2 = idx("fn second_func")?;
2799 let s1 = idx("struct FirstStruct")?;
2800 let s2 = idx("struct SecondStruct")?;
Googlera675ae02021-12-07 08:04:59 +00002801 let t1 = idx("fn __rust_thunk___Z10first_funcv")?;
2802 let t2 = idx("fn __rust_thunk___Z11second_funcv")?;
Michael Forstered642022021-10-04 09:48:25 +00002803
2804 assert!(f1 < s1);
2805 assert!(s1 < f2);
2806 assert!(f2 < s2);
2807 assert!(s2 < t1);
2808 assert!(t1 < t2);
2809
2810 Ok(())
2811 }
Michael Forster028800b2021-10-05 12:39:59 +00002812
2813 #[test]
Devin Jeanpierrec80e6242022-02-03 01:56:40 +00002814 fn test_base_class_subobject_layout() -> Result<()> {
2815 let ir = ir_from_cc(
2816 r#"
2817 // We use a class here to force `Derived::z` to live inside the tail padding of `Base`.
2818 // On the Itanium ABI, this would not happen if `Base` were a POD type.
Devin Jeanpierre56777022022-02-03 01:57:15 +00002819 class Base {__INT64_TYPE__ x; char y;};
2820 struct Derived final : Base {__INT16_TYPE__ z;};
Devin Jeanpierrec80e6242022-02-03 01:56:40 +00002821 "#,
2822 )?;
2823 let rs_api = generate_rs_api(&ir)?;
2824 assert_rs_matches!(
2825 rs_api,
2826 quote! {
2827 #[repr(C, align(8))]
2828 pub struct Derived {
Rosica Dejanovskaaccf00b2022-04-01 13:28:04 -07002829 __base_class_subobjects: [rust_std::mem::MaybeUninit<u8>; 10],
Devin Jeanpierrec80e6242022-02-03 01:56:40 +00002830 pub z: i16,
2831 }
2832 }
2833 );
2834 Ok(())
2835 }
2836
2837 /// The same as test_base_class_subobject_layout, but with multiple
2838 /// inheritance.
2839 #[test]
2840 fn test_base_class_multiple_inheritance_subobject_layout() -> Result<()> {
2841 let ir = ir_from_cc(
2842 r#"
Devin Jeanpierre56777022022-02-03 01:57:15 +00002843 class Base1 {__INT64_TYPE__ x;};
Devin Jeanpierrec80e6242022-02-03 01:56:40 +00002844 class Base2 {char y;};
Devin Jeanpierre56777022022-02-03 01:57:15 +00002845 struct Derived final : Base1, Base2 {__INT16_TYPE__ z;};
Devin Jeanpierrec80e6242022-02-03 01:56:40 +00002846 "#,
2847 )?;
2848 let rs_api = generate_rs_api(&ir)?;
2849 assert_rs_matches!(
2850 rs_api,
2851 quote! {
2852 #[repr(C, align(8))]
2853 pub struct Derived {
Rosica Dejanovskaaccf00b2022-04-01 13:28:04 -07002854 __base_class_subobjects: [rust_std::mem::MaybeUninit<u8>; 10],
Devin Jeanpierrec80e6242022-02-03 01:56:40 +00002855 pub z: i16,
2856 }
2857 }
2858 );
2859 Ok(())
2860 }
2861
2862 /// The same as test_base_class_subobject_layout, but with a chain of
2863 /// inheritance.
2864 #[test]
2865 fn test_base_class_deep_inheritance_subobject_layout() -> Result<()> {
2866 let ir = ir_from_cc(
2867 r#"
Devin Jeanpierre56777022022-02-03 01:57:15 +00002868 class Base1 {__INT64_TYPE__ x;};
Devin Jeanpierrec80e6242022-02-03 01:56:40 +00002869 class Base2 : Base1 {char y;};
Devin Jeanpierre56777022022-02-03 01:57:15 +00002870 struct Derived final : Base2 {__INT16_TYPE__ z;};
Devin Jeanpierrec80e6242022-02-03 01:56:40 +00002871 "#,
2872 )?;
2873 let rs_api = generate_rs_api(&ir)?;
2874 assert_rs_matches!(
2875 rs_api,
2876 quote! {
2877 #[repr(C, align(8))]
2878 pub struct Derived {
Rosica Dejanovskaaccf00b2022-04-01 13:28:04 -07002879 __base_class_subobjects: [rust_std::mem::MaybeUninit<u8>; 10],
Devin Jeanpierrec80e6242022-02-03 01:56:40 +00002880 pub z: i16,
2881 }
2882 }
2883 );
2884 Ok(())
2885 }
2886
2887 /// For derived classes with no data members, we can't use the offset of the
2888 /// first member to determine the size of the base class subobjects.
2889 #[test]
2890 fn test_base_class_subobject_fieldless_layout() -> Result<()> {
2891 let ir = ir_from_cc(
2892 r#"
Devin Jeanpierre56777022022-02-03 01:57:15 +00002893 class Base {__INT64_TYPE__ x; char y;};
Devin Jeanpierrec80e6242022-02-03 01:56:40 +00002894 struct Derived final : Base {};
2895 "#,
2896 )?;
2897 let rs_api = generate_rs_api(&ir)?;
2898 assert_rs_matches!(
2899 rs_api,
2900 quote! {
2901 #[repr(C, align(8))]
2902 pub struct Derived {
Rosica Dejanovskaaccf00b2022-04-01 13:28:04 -07002903 __base_class_subobjects: [rust_std::mem::MaybeUninit<u8>; 9],
Devin Jeanpierrec80e6242022-02-03 01:56:40 +00002904 }
2905 }
2906 );
2907 Ok(())
2908 }
2909
2910 #[test]
2911 fn test_base_class_subobject_empty_fieldless() -> Result<()> {
2912 let ir = ir_from_cc(
2913 r#"
2914 class Base {};
2915 struct Derived final : Base {};
2916 "#,
2917 )?;
2918 let rs_api = generate_rs_api(&ir)?;
2919 assert_rs_matches!(
2920 rs_api,
2921 quote! {
2922 #[repr(C)]
2923 pub struct Derived {
Rosica Dejanovskaaccf00b2022-04-01 13:28:04 -07002924 __base_class_subobjects: [rust_std::mem::MaybeUninit<u8>; 0],
Devin Jeanpierrec80e6242022-02-03 01:56:40 +00002925 /// Prevent empty C++ struct being zero-size in Rust.
Rosica Dejanovskaaccf00b2022-04-01 13:28:04 -07002926 placeholder: rust_std::mem::MaybeUninit<u8>,
Devin Jeanpierrec80e6242022-02-03 01:56:40 +00002927 }
2928 }
2929 );
2930 Ok(())
2931 }
2932
2933 #[test]
2934 fn test_base_class_subobject_empty() -> Result<()> {
2935 let ir = ir_from_cc(
2936 r#"
2937 class Base {};
2938 struct Derived final : Base {};
2939 "#,
2940 )?;
2941 let rs_api = generate_rs_api(&ir)?;
2942 assert_rs_matches!(
2943 rs_api,
2944 quote! {
2945 #[repr(C)]
2946 pub struct Derived {
Rosica Dejanovskaaccf00b2022-04-01 13:28:04 -07002947 __base_class_subobjects: [rust_std::mem::MaybeUninit<u8>; 0],
Devin Jeanpierrec80e6242022-02-03 01:56:40 +00002948 /// Prevent empty C++ struct being zero-size in Rust.
Rosica Dejanovskaaccf00b2022-04-01 13:28:04 -07002949 placeholder: rust_std::mem::MaybeUninit<u8>,
Devin Jeanpierrec80e6242022-02-03 01:56:40 +00002950 }
2951 }
2952 );
2953 Ok(())
2954 }
2955
Devin Jeanpierreb69bcae2022-02-03 09:45:50 +00002956 /// When a field is [[no_unique_address]], it occupies the space up to the
2957 /// next field.
2958 #[test]
2959 fn test_no_unique_address() -> Result<()> {
2960 let ir = ir_from_cc(
2961 r#"
2962 class Field1 {__INT64_TYPE__ x;};
2963 class Field2 {char y;};
2964 struct Struct final {
2965 [[no_unique_address]] Field1 field1;
2966 [[no_unique_address]] Field2 field2;
2967 __INT16_TYPE__ z;
2968 };
2969 "#,
2970 )?;
2971 let rs_api = generate_rs_api(&ir)?;
2972 assert_rs_matches!(
2973 rs_api,
2974 quote! {
2975 #[derive(Clone, Copy)]
2976 #[repr(C, align(8))]
2977 pub struct Struct {
Rosica Dejanovskaaccf00b2022-04-01 13:28:04 -07002978 field1: [rust_std::mem::MaybeUninit<u8>; 8],
2979 field2: [rust_std::mem::MaybeUninit<u8>; 2],
Devin Jeanpierreb69bcae2022-02-03 09:45:50 +00002980 pub z: i16,
2981 }
Devin Jeanpierre58181ac2022-02-14 21:30:05 +00002982
2983 impl Struct {
2984 pub fn field1(&self) -> &Field1 {
2985 unsafe {&* (&self.field1 as *const _ as *const Field1)}
2986 }
2987 pub fn field2(&self) -> &Field2 {
2988 unsafe {&* (&self.field2 as *const _ as *const Field2)}
2989 }
2990 }
Devin Jeanpierreb69bcae2022-02-03 09:45:50 +00002991 }
2992 );
2993 Ok(())
2994 }
2995
2996 /// When a [[no_unique_address]] field is the last one, it occupies the rest
2997 /// of the object.
2998 #[test]
2999 fn test_no_unique_address_last_field() -> Result<()> {
3000 let ir = ir_from_cc(
3001 r#"
3002 class Field1 {__INT64_TYPE__ x;};
3003 class Field2 {char y;};
3004 struct Struct final {
3005 [[no_unique_address]] Field1 field1;
3006 [[no_unique_address]] Field2 field2;
3007 };
3008 "#,
3009 )?;
3010 let rs_api = generate_rs_api(&ir)?;
3011 assert_rs_matches!(
3012 rs_api,
3013 quote! {
3014 #[derive(Clone, Copy)]
3015 #[repr(C, align(8))]
3016 pub struct Struct {
Rosica Dejanovskaaccf00b2022-04-01 13:28:04 -07003017 field1: [rust_std::mem::MaybeUninit<u8>; 8],
3018 field2: [rust_std::mem::MaybeUninit<u8>; 8],
Devin Jeanpierreb69bcae2022-02-03 09:45:50 +00003019 }
3020 }
3021 );
3022 Ok(())
3023 }
3024
3025 #[test]
3026 fn test_no_unique_address_empty() -> Result<()> {
3027 let ir = ir_from_cc(
3028 r#"
3029 class Field {};
3030 struct Struct final {
3031 [[no_unique_address]] Field field;
3032 int x;
3033 };
3034 "#,
3035 )?;
3036 let rs_api = generate_rs_api(&ir)?;
3037 assert_rs_matches!(
3038 rs_api,
3039 quote! {
3040 #[repr(C, align(4))]
3041 pub struct Struct {
Rosica Dejanovskaaccf00b2022-04-01 13:28:04 -07003042 field: [rust_std::mem::MaybeUninit<u8>; 0],
Devin Jeanpierreb69bcae2022-02-03 09:45:50 +00003043 pub x: i32,
3044 }
3045 }
3046 );
3047 Ok(())
3048 }
3049
3050 #[test]
3051 fn test_base_class_subobject_empty_last_field() -> Result<()> {
3052 let ir = ir_from_cc(
3053 r#"
3054 class Field {};
3055 struct Struct final {
3056 [[no_unique_address]] Field field;
3057 };
3058 "#,
3059 )?;
3060 let rs_api = generate_rs_api(&ir)?;
3061 assert_rs_matches!(
3062 rs_api,
3063 quote! {
3064 #[repr(C)]
3065 pub struct Struct {
Rosica Dejanovskaaccf00b2022-04-01 13:28:04 -07003066 field: [rust_std::mem::MaybeUninit<u8>; 1],
Devin Jeanpierreb69bcae2022-02-03 09:45:50 +00003067 }
3068 }
3069 );
3070 Ok(())
3071 }
3072
Devin Jeanpierrec80e6242022-02-03 01:56:40 +00003073 #[test]
Teddy Katz76fa42b2022-02-23 01:22:56 +00003074 fn test_generate_enum_basic() -> Result<()> {
3075 let ir = ir_from_cc("enum Color { kRed = 5, kBlue };")?;
3076 let rs_api = generate_rs_api(&ir)?;
3077 assert_rs_matches!(
3078 rs_api,
3079 quote! {
3080 #[repr(transparent)]
3081 #[derive(Debug, PartialEq, Eq, Copy, Clone, Hash, PartialOrd, Ord)]
3082 pub struct Color(u32);
3083 impl Color {
3084 pub const kRed: Color = Color(5);
3085 pub const kBlue: Color = Color(6);
3086 }
3087 impl From<u32> for Color {
3088 fn from(value: u32) -> Color {
3089 Color(v)
3090 }
3091 }
3092 impl From<Color> for u32 {
3093 fn from(value: Color) -> u32 {
3094 v.0
3095 }
3096 }
3097 }
3098 );
3099 Ok(())
3100 }
3101
3102 #[test]
3103 fn test_generate_scoped_enum_basic() -> Result<()> {
3104 let ir = ir_from_cc("enum class Color { kRed = -5, kBlue };")?;
3105 let rs_api = generate_rs_api(&ir)?;
3106 assert_rs_matches!(
3107 rs_api,
3108 quote! {
3109 #[repr(transparent)]
3110 #[derive(Debug, PartialEq, Eq, Copy, Clone, Hash, PartialOrd, Ord)]
3111 pub struct Color(i32);
3112 impl Color {
3113 pub const kRed: Color = Color(-5);
3114 pub const kBlue: Color = Color(-4);
3115 }
3116 impl From<i32> for Color {
3117 fn from(value: i32) -> Color {
3118 Color(v)
3119 }
3120 }
3121 impl From<Color> for i32 {
3122 fn from(value: Color) -> i32 {
3123 v.0
3124 }
3125 }
3126 }
3127 );
3128 Ok(())
3129 }
3130
3131 #[test]
3132 fn test_generate_enum_with_64_bit_signed_vals() -> Result<()> {
3133 let ir = ir_from_cc(
3134 "enum Color : long { kViolet = -9223372036854775807 - 1LL, kRed = -5, kBlue, kGreen = 3, kMagenta = 9223372036854775807 };",
3135 )?;
3136 let rs_api = generate_rs_api(&ir)?;
3137 assert_rs_matches!(
3138 rs_api,
3139 quote! {
3140 #[repr(transparent)]
3141 #[derive(Debug, PartialEq, Eq, Copy, Clone, Hash, PartialOrd, Ord)]
3142 pub struct Color(i64);
3143 impl Color {
3144 pub const kViolet: Color = Color(-9223372036854775808);
3145 pub const kRed: Color = Color(-5);
3146 pub const kBlue: Color = Color(-4);
3147 pub const kGreen: Color = Color(3);
3148 pub const kMagenta: Color = Color(9223372036854775807);
3149 }
3150 impl From<i64> for Color {
3151 fn from(value: i64) -> Color {
3152 Color(v)
3153 }
3154 }
3155 impl From<Color> for i64 {
3156 fn from(value: Color) -> i64 {
3157 v.0
3158 }
3159 }
3160 }
3161 );
3162 Ok(())
3163 }
3164
3165 #[test]
3166 fn test_generate_enum_with_64_bit_unsigned_vals() -> Result<()> {
3167 let ir = ir_from_cc(
3168 "enum Color: unsigned long { kRed, kBlue, kLimeGreen = 18446744073709551615 };",
3169 )?;
3170 let rs_api = generate_rs_api(&ir)?;
3171 assert_rs_matches!(
3172 rs_api,
3173 quote! {
3174 #[repr(transparent)]
3175 #[derive(Debug, PartialEq, Eq, Copy, Clone, Hash, PartialOrd, Ord)]
3176 pub struct Color(u64);
3177 impl Color {
3178 pub const kRed: Color = Color(0);
3179 pub const kBlue: Color = Color(1);
3180 pub const kLimeGreen: Color = Color(18446744073709551615);
3181 }
3182 impl From<u64> for Color {
3183 fn from(value: u64) -> Color {
3184 Color(v)
3185 }
3186 }
3187 impl From<Color> for u64 {
3188 fn from(value: Color) -> u64 {
3189 v.0
3190 }
3191 }
3192 }
3193 );
3194 Ok(())
3195 }
3196
3197 #[test]
3198 fn test_generate_enum_with_32_bit_signed_vals() -> Result<()> {
3199 let ir = ir_from_cc(
3200 "enum Color { kViolet = -2147483647 - 1, kRed = -5, kBlue, kGreen = 3, kMagenta = 2147483647 };",
3201 )?;
3202 let rs_api = generate_rs_api(&ir)?;
3203 assert_rs_matches!(
3204 rs_api,
3205 quote! {
3206 #[repr(transparent)]
3207 #[derive(Debug, PartialEq, Eq, Copy, Clone, Hash, PartialOrd, Ord)]
3208 pub struct Color(i32);
3209 impl Color {
3210 pub const kViolet: Color = Color(-2147483648);
3211 pub const kRed: Color = Color(-5);
3212 pub const kBlue: Color = Color(-4);
3213 pub const kGreen: Color = Color(3);
3214 pub const kMagenta: Color = Color(2147483647);
3215 }
3216 impl From<i32> for Color {
3217 fn from(value: i32) -> Color {
3218 Color(v)
3219 }
3220 }
3221 impl From<Color> for i32 {
3222 fn from(value: Color) -> i32 {
3223 v.0
3224 }
3225 }
3226 }
3227 );
3228 Ok(())
3229 }
3230
3231 #[test]
3232 fn test_generate_enum_with_32_bit_unsigned_vals() -> Result<()> {
3233 let ir = ir_from_cc("enum Color: unsigned int { kRed, kBlue, kLimeGreen = 4294967295 };")?;
3234 let rs_api = generate_rs_api(&ir)?;
3235 assert_rs_matches!(
3236 rs_api,
3237 quote! {
3238 #[repr(transparent)]
3239 #[derive(Debug, PartialEq, Eq, Copy, Clone, Hash, PartialOrd, Ord)]
3240 pub struct Color(u32);
3241 impl Color {
3242 pub const kRed: Color = Color(0);
3243 pub const kBlue: Color = Color(1);
3244 pub const kLimeGreen: Color = Color(4294967295);
3245 }
3246 impl From<u32> for Color {
3247 fn from(value: u32) -> Color {
3248 Color(v)
3249 }
3250 }
3251 impl From<Color> for u32 {
3252 fn from(value: Color) -> u32 {
3253 v.0
3254 }
3255 }
3256 }
3257 );
3258 Ok(())
3259 }
3260
3261 #[test]
Michael Forster409d9412021-10-07 08:35:29 +00003262 fn test_doc_comment_func() -> Result<()> {
Marcel Hlopko89547752021-12-10 09:39:41 +00003263 let ir = ir_from_cc(
3264 "
3265 // Doc Comment
3266 // with two lines
3267 int func();",
3268 )?;
Michael Forster409d9412021-10-07 08:35:29 +00003269
Marcel Hlopko89547752021-12-10 09:39:41 +00003270 assert_rs_matches!(
3271 generate_rs_api(&ir)?,
3272 // leading space is intentional so there is a space between /// and the text of the
3273 // comment
3274 quote! {
3275 #[doc = " Doc Comment\n with two lines"]
3276 #[inline(always)]
3277 pub fn func
3278 }
Michael Forster409d9412021-10-07 08:35:29 +00003279 );
3280
3281 Ok(())
3282 }
3283
3284 #[test]
3285 fn test_doc_comment_record() -> Result<()> {
Marcel Hlopko89547752021-12-10 09:39:41 +00003286 let ir = ir_from_cc(
3287 "// Doc Comment\n\
3288 //\n\
3289 // * with bullet\n\
Devin Jeanpierre88343c72022-01-15 01:10:23 +00003290 struct SomeStruct final {\n\
Marcel Hlopko89547752021-12-10 09:39:41 +00003291 // Field doc\n\
3292 int field;\
3293 };",
3294 )?;
Michael Forster028800b2021-10-05 12:39:59 +00003295
Marcel Hlopko89547752021-12-10 09:39:41 +00003296 assert_rs_matches!(
3297 generate_rs_api(&ir)?,
3298 quote! {
3299 #[doc = " Doc Comment\n \n * with bullet"]
3300 #[derive(Clone, Copy)]
3301 #[repr(C)]
3302 pub struct SomeStruct {
3303 # [doc = " Field doc"]
3304 pub field: i32,
3305 }
3306 }
Michael Forstercc5941a2021-10-07 07:12:24 +00003307 );
Michael Forster028800b2021-10-05 12:39:59 +00003308 Ok(())
3309 }
Devin Jeanpierre91de7012021-10-21 12:53:51 +00003310
Devin Jeanpierre96839c12021-12-14 00:27:38 +00003311 #[test]
Teddy Katzd2cd1422022-04-04 09:41:33 -07003312 fn test_basic_union() -> Result<()> {
3313 let ir = ir_from_cc(
3314 r#"
3315 union SomeUnion {
3316 int some_field;
3317 long long some_bigger_field;
3318 };
3319 "#,
3320 )?;
3321 let rs_api = generate_rs_api(&ir)?;
3322
3323 assert_rs_matches!(
3324 rs_api,
3325 quote! {
3326 #[derive(Clone, Copy)]
3327 #[repr(C)]
3328 pub union SomeUnion {
3329 pub some_field: i32,
3330 pub some_bigger_field: i64,
3331 }
3332 }
3333 );
3334 Ok(())
3335 }
3336
3337 #[test]
3338 fn test_union_with_private_fields() -> Result<()> {
3339 let ir = ir_from_cc(
3340 r#"
3341 union SomeUnionWithPrivateFields {
3342 public:
3343 int public_field;
3344 private:
3345 long long private_field;
3346 };
3347 "#,
3348 )?;
3349 let rs_api = generate_rs_api(&ir)?;
3350
3351 assert_rs_matches!(
3352 rs_api,
3353 quote! {
3354 #[derive(Clone, Copy)]
3355 #[repr(C)]
3356 pub union SomeUnionWithPrivateFields {
3357 pub public_field: i32,
3358 private_field: i64,
3359 }
3360 }
3361 );
3362
3363 assert_rs_matches!(
3364 rs_api,
3365 quote! {
3366 const _: () = assert!(rust_std::mem::size_of::<SomeUnionWithPrivateFields>() == 8usize);
3367 const _: () = assert!(rust_std::mem::align_of::<SomeUnionWithPrivateFields>() == 8usize);
3368 const _: () = {
3369 assert_impl_all!(SomeUnionWithPrivateFields: Clone);
3370 };
3371 const _: () = {
3372 assert_impl_all!(SomeUnionWithPrivateFields: Copy);
3373 };
3374 const _: () = {
3375 assert_not_impl_all!(SomeUnionWithPrivateFields: Drop);
3376 };
3377 const _: () = assert!(offset_of!(SomeUnionWithPrivateFields, public_field) * 8 == 0usize);
3378 const _: () = assert!(offset_of!(SomeUnionWithPrivateFields, private_field) * 8 == 0usize);
3379 }
3380 );
3381 Ok(())
3382 }
3383
3384 #[test]
3385 fn test_empty_union() -> Result<()> {
3386 let ir = ir_from_cc(
3387 r#"
3388 union EmptyUnion {};
3389 "#,
3390 )?;
3391 let rs_api = generate_rs_api(&ir)?;
3392
3393 assert_rs_matches!(
3394 rs_api,
3395 quote! {
3396 #[derive(Clone, Copy)]
3397 #[repr(C)]
3398 pub union EmptyUnion {
3399 /// Prevent empty C++ struct being zero-size in Rust.
3400 placeholder: rust_std::mem::MaybeUninit<u8>,
3401 }
3402 }
3403 );
3404
3405 assert_rs_matches!(
3406 rs_api,
3407 quote! {
3408 const _: () = assert!(rust_std::mem::size_of::<EmptyUnion>() == 1usize);
3409 const _: () = assert!(rust_std::mem::align_of::<EmptyUnion>() == 1usize);
3410 }
3411 );
3412
3413 Ok(())
3414 }
3415
3416 #[test]
3417 fn test_union_field_with_nontrivial_destructor() -> Result<()> {
3418 let ir = ir_from_cc(
3419 r#"
3420 struct NontrivialStruct { ~NontrivialStruct(); };
3421 union UnionWithNontrivialField {
3422 int trivial_field;
3423 NontrivialStruct nontrivial_field;
3424 };
3425 "#,
3426 )?;
3427 let rs_api = generate_rs_api(&ir)?;
3428
3429 assert_rs_matches!(
3430 rs_api,
3431 quote! {
3432 #[derive(Clone, Copy)]
3433 #[repr(C)]
3434 pub union UnionWithNontrivialField {
3435 pub trivial_field: i32,
3436 pub nontrivial_field: rust_std::mem::ManuallyDrop<NontrivialStruct>,
3437 }
3438 }
3439 );
3440
3441 assert_rs_matches!(
3442 rs_api,
3443 quote! {
3444 const _: () = assert!(rust_std::mem::size_of::<UnionWithNontrivialField>() == 4usize);
3445 const _: () = assert!(rust_std::mem::align_of::<UnionWithNontrivialField>() == 4usize);
3446 }
3447 );
3448 Ok(())
3449 }
3450
3451 #[test]
3452 fn test_union_with_constructors() -> Result<()> {
3453 let ir = ir_from_cc(
3454 r#"
3455 #pragma clang lifetime_elision
3456 union UnionWithDefaultConstructors {
3457 int a;
3458 };
3459 "#,
3460 )?;
3461 let rs_api = generate_rs_api(&ir)?;
3462
3463 assert_rs_matches!(
3464 rs_api,
3465 quote! {
3466 #[derive(Clone, Copy)]
3467 #[repr(C)]
3468 pub union UnionWithDefaultConstructors {
3469 pub a: i32,
3470 }
3471 }
3472 );
3473
3474 assert_rs_matches!(
3475 rs_api,
3476 quote! {
3477 impl Default for UnionWithDefaultConstructors {
3478 #[inline(always)]
3479 fn default() -> Self {
3480 let mut tmp = rust_std::mem::MaybeUninit::<Self>::zeroed();
3481 unsafe {
3482 crate::detail::__rust_thunk___ZN28UnionWithDefaultConstructorsC1Ev(&mut tmp);
3483 tmp.assume_init()
3484 }
3485 }
3486 }
3487 }
3488 );
3489
3490 assert_rs_matches!(
3491 rs_api,
3492 quote! {
3493 impl<'b> From<ctor::RvalueReference<'b, UnionWithDefaultConstructors>> for UnionWithDefaultConstructors {
3494 #[inline(always)]
3495 fn from(__param_0: ctor::RvalueReference<'b, UnionWithDefaultConstructors>) -> Self {
3496 let mut tmp = rust_std::mem::MaybeUninit::<Self>::zeroed();
3497 unsafe {
3498 crate::detail::__rust_thunk___ZN28UnionWithDefaultConstructorsC1EOS_(&mut tmp, __param_0);
3499 tmp.assume_init()
3500 }
3501 }
3502 }
3503 }
3504 );
3505
3506 Ok(())
3507 }
3508
3509 #[test]
Devin Jeanpierre56777022022-02-03 01:57:15 +00003510 fn test_unambiguous_public_bases() -> Result<()> {
3511 let ir = ir_from_cc_dependency(
3512 "
3513 struct VirtualBase {};
3514 struct PrivateBase {};
3515 struct ProtectedBase {};
3516 struct UnambiguousPublicBase {};
3517 struct AmbiguousPublicBase {};
3518 struct MultipleInheritance : UnambiguousPublicBase, AmbiguousPublicBase {};
3519 struct Derived : private PrivateBase, protected ProtectedBase, MultipleInheritance, AmbiguousPublicBase, virtual VirtualBase {};
3520 ",
3521 "",
3522 )?;
3523 let rs_api = generate_rs_api(&ir)?;
3524 // TODO(b/216195042): virtual bases.
3525 assert_rs_not_matches!(rs_api, quote! { From<&'a Derived> for &'a VirtualBase });
3526 assert_rs_matches!(rs_api, quote! { From<&'a Derived> for &'a UnambiguousPublicBase });
3527 assert_rs_matches!(rs_api, quote! { From<&'a Derived> for &'a MultipleInheritance });
3528 assert_rs_not_matches!(rs_api, quote! {From<&'a Derived> for &'a PrivateBase});
3529 assert_rs_not_matches!(rs_api, quote! {From<&'a Derived> for &'a ProtectedBase});
3530 assert_rs_not_matches!(rs_api, quote! {From<&'a Derived> for &'a AmbiguousPublicBase});
3531 Ok(())
3532 }
3533
3534 /// Contrary to intuitions: a base class conversion is ambiguous even if the
3535 /// ambiguity is from a private base class cast that you can't even
3536 /// perform.
3537 ///
3538 /// Explanation (courtesy James Dennett):
3539 ///
3540 /// > Once upon a time, there was a rule in C++ that changing all access
3541 /// > specifiers to "public" would not change the meaning of code.
3542 /// > That's no longer true, but some of its effects can still be seen.
3543 ///
3544 /// So, we need to be sure to not allow casting to privately-ambiguous
3545 /// bases.
3546 #[test]
3547 fn test_unambiguous_public_bases_private_ambiguity() -> Result<()> {
3548 let ir = ir_from_cc_dependency(
3549 "
3550 struct Base {};
3551 struct Intermediate : public Base {};
3552 struct Derived : Base, private Intermediate {};
3553 ",
3554 "",
3555 )?;
3556 let rs_api = generate_rs_api(&ir)?;
3557 assert_rs_not_matches!(rs_api, quote! { From<&'a Derived> for &'a Base });
3558 Ok(())
3559 }
3560
3561 #[test]
Devin Jeanpierre96839c12021-12-14 00:27:38 +00003562 fn test_virtual_thunk() -> Result<()> {
3563 let ir = ir_from_cc("struct Polymorphic { virtual void Foo(); };")?;
3564
3565 assert_cc_matches!(
3566 generate_rs_api_impl(&ir)?,
3567 quote! {
Googler972d3582022-01-11 10:17:22 +00003568 extern "C" void __rust_thunk___ZN11Polymorphic3FooEv(class Polymorphic * __this)
Devin Jeanpierre96839c12021-12-14 00:27:38 +00003569 }
3570 );
3571 Ok(())
3572 }
3573
Lukasz Anforowicz0b6a6ac2022-03-22 22:32:23 +00003574 #[test]
3575 fn test_custom_abi_thunk() -> Result<()> {
3576 let ir = ir_from_cc(
3577 r#"
3578 float f_vectorcall_calling_convention(float p1, float p2) [[clang::vectorcall]];
3579 double f_c_calling_convention(double p1, double p2);
3580 "#,
3581 )?;
3582 let rs_api = generate_rs_api(&ir)?;
3583 let rs_api_impl = generate_rs_api_impl(&ir)?;
3584 assert_rs_matches!(
3585 rs_api,
3586 quote! {
3587 #[inline(always)]
3588 pub fn f_vectorcall_calling_convention(p1: f32, p2: f32) -> f32 {
3589 unsafe {
3590 crate::detail::__rust_thunk___Z31f_vectorcall_calling_conventionff(p1, p2)
3591 }
3592 }
3593 }
3594 );
3595 assert_rs_matches!(
3596 rs_api,
3597 quote! {
3598 #[inline(always)]
3599 pub fn f_c_calling_convention(p1: f64, p2: f64) -> f64 {
3600 unsafe { crate::detail::__rust_thunk___Z22f_c_calling_conventiondd(p1, p2) }
3601 }
3602 }
3603 );
3604 // `link_name` (i.e. no thunk) for `f_c_calling_convention`. No
3605 // `link_name` (i.e. indicates presence of a thunk) for
3606 // `f_vectorcall_calling_convention`.
3607 assert_rs_matches!(
3608 rs_api,
3609 quote! {
3610 mod detail {
3611 #[allow(unused_imports)]
3612 use super::*;
3613 extern "C" {
3614 pub(crate) fn __rust_thunk___Z31f_vectorcall_calling_conventionff(
3615 p1: f32, p2: f32) -> f32;
3616 #[link_name = "_Z22f_c_calling_conventiondd"]
3617 pub(crate) fn __rust_thunk___Z22f_c_calling_conventiondd(
3618 p1: f64, p2: f64) -> f64;
3619 }
3620 }
3621 }
3622 );
3623 // C++ thunk needed for `f_vectorcall_calling_convention`.
3624 assert_cc_matches!(
3625 rs_api_impl,
3626 quote! {
3627 extern "C" float __rust_thunk___Z31f_vectorcall_calling_conventionff(
3628 float p1, float p2) {
Devin Jeanpierredeea7892022-03-29 02:13:31 -07003629 return f_vectorcall_calling_convention (std::forward<decltype(p1)>(p1), std::forward<decltype(p2)>(p2));
Lukasz Anforowicz0b6a6ac2022-03-22 22:32:23 +00003630 }
3631 }
3632 );
3633 // No C++ thunk expected for `f_c_calling_convention`.
3634 assert_cc_not_matches!(rs_api_impl, quote! { f_c_calling_convention });
3635 Ok(())
3636 }
3637
Devin Jeanpierree6e16652021-12-22 15:54:46 +00003638 /// A trivially relocatable final struct is safe to use in Rust as normal,
3639 /// and is Unpin.
3640 #[test]
3641 fn test_no_negative_impl_unpin() -> Result<()> {
3642 let ir = ir_from_cc("struct Trivial final {};")?;
3643 let rs_api = generate_rs_api(&ir)?;
3644 assert_rs_not_matches!(rs_api, quote! {impl !Unpin});
3645 Ok(())
3646 }
3647
3648 /// A non-final struct, even if it's trivial, is not usable by mut
3649 /// reference, and so is !Unpin.
3650 #[test]
3651 fn test_negative_impl_unpin_nonfinal() -> Result<()> {
3652 let ir = ir_from_cc("struct Nonfinal {};")?;
3653 let rs_api = generate_rs_api(&ir)?;
3654 assert_rs_matches!(rs_api, quote! {impl !Unpin for Nonfinal {}});
3655 Ok(())
3656 }
3657
Devin Jeanpierre91de7012021-10-21 12:53:51 +00003658 /// At the least, a trivial type should have no drop impl if or until we add
3659 /// empty drop impls.
3660 #[test]
3661 fn test_no_impl_drop() -> Result<()> {
Googler7cced422021-12-06 11:58:39 +00003662 let ir = ir_from_cc("struct Trivial {};")?;
Marcel Hlopko89547752021-12-10 09:39:41 +00003663 let rs_api = rs_tokens_to_formatted_string(generate_rs_api(&ir)?)?;
Devin Jeanpierre91de7012021-10-21 12:53:51 +00003664 assert!(!rs_api.contains("impl Drop"));
3665 Ok(())
3666 }
3667
Devin Jeanpierre7e9a1de2021-12-03 08:04:22 +00003668 /// User-defined destructors *must* become Drop impls with ManuallyDrop
3669 /// fields
Devin Jeanpierre91de7012021-10-21 12:53:51 +00003670 #[test]
Devin Jeanpierre7e9a1de2021-12-03 08:04:22 +00003671 fn test_impl_drop_user_defined_destructor() -> Result<()> {
Googler7cced422021-12-06 11:58:39 +00003672 let ir = ir_from_cc(
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00003673 r#" struct NontrivialStruct { ~NontrivialStruct(); };
3674 struct UserDefinedDestructor {
Devin Jeanpierre91de7012021-10-21 12:53:51 +00003675 ~UserDefinedDestructor();
Devin Jeanpierre7e9a1de2021-12-03 08:04:22 +00003676 int x;
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00003677 NontrivialStruct nts;
Devin Jeanpierre91de7012021-10-21 12:53:51 +00003678 };"#,
3679 )?;
3680 let rs_api = generate_rs_api(&ir)?;
Lukasz Anforowicz6d553632022-01-06 21:36:14 +00003681 assert_rs_matches!(
3682 rs_api,
3683 quote! {
3684 impl Drop for UserDefinedDestructor {
3685 #[inline(always)]
3686 fn drop(&mut self) {
3687 unsafe { crate::detail::__rust_thunk___ZN21UserDefinedDestructorD1Ev(self) }
3688 }
3689 }
3690 }
3691 );
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00003692 assert_rs_matches!(rs_api, quote! {pub x: i32,});
Rosica Dejanovskaaccf00b2022-04-01 13:28:04 -07003693 assert_rs_matches!(
3694 rs_api,
3695 quote! {pub nts: rust_std::mem::ManuallyDrop<NontrivialStruct>,}
3696 );
Devin Jeanpierre7e9a1de2021-12-03 08:04:22 +00003697 Ok(())
3698 }
3699
Lukasz Anforowicz6d553632022-01-06 21:36:14 +00003700 /// nontrivial types without user-defined destructors should invoke
3701 /// the C++ destructor to preserve the order of field destructions.
Devin Jeanpierre7e9a1de2021-12-03 08:04:22 +00003702 #[test]
3703 fn test_impl_drop_nontrivial_member_destructor() -> Result<()> {
3704 // TODO(jeanpierreda): This would be cleaner if the UserDefinedDestructor code were
3705 // omitted. For example, we simulate it so that UserDefinedDestructor
3706 // comes from another library.
Googler7cced422021-12-06 11:58:39 +00003707 let ir = ir_from_cc(
Devin Jeanpierre88343c72022-01-15 01:10:23 +00003708 r#"struct UserDefinedDestructor final {
Devin Jeanpierre7e9a1de2021-12-03 08:04:22 +00003709 ~UserDefinedDestructor();
3710 };
Devin Jeanpierre88343c72022-01-15 01:10:23 +00003711 struct TrivialStruct final { int i; };
3712 struct NontrivialMembers final {
Devin Jeanpierre7e9a1de2021-12-03 08:04:22 +00003713 UserDefinedDestructor udd;
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00003714 TrivialStruct ts;
Devin Jeanpierre7e9a1de2021-12-03 08:04:22 +00003715 int x;
3716 };"#,
3717 )?;
3718 let rs_api = generate_rs_api(&ir)?;
Lukasz Anforowicz6d553632022-01-06 21:36:14 +00003719 assert_rs_matches!(
3720 rs_api,
3721 quote! {
3722 impl Drop for NontrivialMembers {
3723 #[inline(always)]
3724 fn drop(&mut self) {
3725 unsafe { crate::detail::__rust_thunk___ZN17NontrivialMembersD1Ev(self) }
3726 }
3727 }
3728 }
3729 );
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00003730 assert_rs_matches!(rs_api, quote! {pub x: i32,});
3731 assert_rs_matches!(rs_api, quote! {pub ts: TrivialStruct,});
Lukasz Anforowicz6d553632022-01-06 21:36:14 +00003732 assert_rs_matches!(
3733 rs_api,
Rosica Dejanovskaaccf00b2022-04-01 13:28:04 -07003734 quote! {pub udd: rust_std::mem::ManuallyDrop<UserDefinedDestructor>,}
Lukasz Anforowicz6d553632022-01-06 21:36:14 +00003735 );
Devin Jeanpierre7e9a1de2021-12-03 08:04:22 +00003736 Ok(())
3737 }
3738
3739 /// Trivial types (at least those that are mapped to Copy rust types) do not
3740 /// get a Drop impl.
3741 #[test]
3742 fn test_impl_drop_trivial() -> Result<()> {
Googler7cced422021-12-06 11:58:39 +00003743 let ir = ir_from_cc(
Devin Jeanpierre88343c72022-01-15 01:10:23 +00003744 r#"struct Trivial final {
Devin Jeanpierre7e9a1de2021-12-03 08:04:22 +00003745 ~Trivial() = default;
3746 int x;
3747 };"#,
3748 )?;
3749 let rs_api = generate_rs_api(&ir)?;
Marcel Hlopko89547752021-12-10 09:39:41 +00003750 assert_rs_not_matches!(rs_api, quote! {impl Drop});
3751 assert_rs_matches!(rs_api, quote! {pub x: i32});
Lukasz Anforowicz2f074162022-01-06 22:50:51 +00003752 let rs_api_impl = generate_rs_api_impl(&ir)?;
3753 // TODO(b/213326125): Avoid generating thunk impls that are never called.
3754 // (The test assertion below should be reversed once this bug is fixed.)
3755 assert_cc_matches!(rs_api_impl, quote! { std::destroy_at });
Devin Jeanpierre91de7012021-10-21 12:53:51 +00003756 Ok(())
3757 }
Devin Jeanpierre45cb1162021-10-27 10:54:28 +00003758
3759 #[test]
Lukasz Anforowicze643ec92021-12-22 15:45:15 +00003760 fn test_impl_default_explicitly_defaulted_constructor() -> Result<()> {
3761 let ir = ir_from_cc(
Lukasz Anforowicz95551272022-01-20 00:02:24 +00003762 r#"#pragma clang lifetime_elision
3763 struct DefaultedConstructor final {
Lukasz Anforowicze643ec92021-12-22 15:45:15 +00003764 DefaultedConstructor() = default;
3765 };"#,
3766 )?;
3767 let rs_api = generate_rs_api(&ir)?;
3768 assert_rs_matches!(
3769 rs_api,
3770 quote! {
3771 impl Default for DefaultedConstructor {
3772 #[inline(always)]
3773 fn default() -> Self {
Rosica Dejanovskaaccf00b2022-04-01 13:28:04 -07003774 let mut tmp = rust_std::mem::MaybeUninit::<Self>::zeroed();
Lukasz Anforowicze643ec92021-12-22 15:45:15 +00003775 unsafe {
Lukasz Anforowicz40c2eb82022-01-11 18:22:31 +00003776 crate::detail::__rust_thunk___ZN20DefaultedConstructorC1Ev(&mut tmp);
Lukasz Anforowicze643ec92021-12-22 15:45:15 +00003777 tmp.assume_init()
3778 }
3779 }
3780 }
3781 }
3782 );
3783 let rs_api_impl = generate_rs_api_impl(&ir)?;
3784 assert_cc_matches!(
3785 rs_api_impl,
3786 quote! {
3787 extern "C" void __rust_thunk___ZN20DefaultedConstructorC1Ev(
Googler972d3582022-01-11 10:17:22 +00003788 class DefaultedConstructor* __this) {
Devin Jeanpierredeea7892022-03-29 02:13:31 -07003789 rs_api_impl_support::construct_at (std::forward<decltype(__this)>(__this)) ;
Lukasz Anforowicze643ec92021-12-22 15:45:15 +00003790 }
3791 }
3792 );
3793 Ok(())
3794 }
3795
3796 #[test]
Lukasz Anforowicz326c4e42022-01-27 14:43:00 +00003797 fn test_impl_clone_that_propagates_lifetime() -> Result<()> {
3798 // This test covers the case where a single lifetime applies to 1)
3799 // the `__this` parameter and 2) other constructor parameters. For
3800 // example, maybe the newly constructed object needs to have the
3801 // same lifetime as the constructor's parameter. (This might require
3802 // annotating the whole C++ struct with a lifetime, so maybe the
3803 // example below is not fully realistic/accurate...).
3804 let mut ir = ir_from_cc(
3805 r#"#pragma clang lifetime_elision
3806 struct Foo final {
Googler53f65942022-02-23 11:23:30 +00003807 [[clang::annotate("lifetimes", "a: a")]]
Lukasz Anforowicz326c4e42022-01-27 14:43:00 +00003808 Foo(const int& i);
3809 };"#,
3810 )?;
3811 let ctor: &mut Func = ir
3812 .items_mut()
3813 .filter_map(|item| match item {
3814 Item::Func(func) => Some(func),
3815 _ => None,
3816 })
3817 .find(|f| {
3818 matches!(&f.name, UnqualifiedIdentifier::Constructor)
3819 && f.params.get(1).map(|p| p.identifier.identifier == "i").unwrap_or_default()
3820 })
3821 .unwrap();
3822 {
3823 // Double-check that the test scenario set up above uses the same lifetime
3824 // for both of the constructor's parameters: `__this` and `i`.
3825 assert_eq!(ctor.params.len(), 2);
3826 let this_lifetime: LifetimeId =
3827 *ctor.params[0].type_.rs_type.lifetime_args.first().unwrap();
3828 let i_lifetime: LifetimeId =
3829 *ctor.params[1].type_.rs_type.lifetime_args.first_mut().unwrap();
3830 assert_eq!(i_lifetime, this_lifetime);
3831 }
3832
3833 // Before cl/423346348 the generated Rust code would incorrectly look
3834 // like this (note the mismatched 'a and 'b lifetimes):
3835 // fn from<'b>(i: &'a i32) -> Self
3836 // After this CL, this scenario will result in an explicit error.
Devin Jeanpierred7b48102022-03-31 04:15:03 -07003837 let rs_api = generate_rs_api(&ir)?;
3838 assert_rs_not_matches!(rs_api, quote! {impl From});
3839 let rs_api_str = tokens_to_string(rs_api)?;
3840 assert!(rs_api_str.contains(
3841 "// The lifetime of `__this` is unexpectedly also used by another parameter"
3842 ));
Lukasz Anforowicz326c4e42022-01-27 14:43:00 +00003843 Ok(())
3844 }
3845
3846 #[test]
Lukasz Anforowicz9bab8352021-12-22 17:35:31 +00003847 fn test_impl_default_non_trivial_struct() -> Result<()> {
3848 let ir = ir_from_cc(
Lukasz Anforowicz71716b72022-01-26 17:05:05 +00003849 r#"#pragma clang lifetime_elision
3850 struct NonTrivialStructWithConstructors final {
Lukasz Anforowicz9bab8352021-12-22 17:35:31 +00003851 NonTrivialStructWithConstructors();
3852 ~NonTrivialStructWithConstructors(); // Non-trivial
3853 };"#,
3854 )?;
3855 let rs_api = generate_rs_api(&ir)?;
3856 assert_rs_not_matches!(rs_api, quote! {impl Default});
3857 Ok(())
3858 }
3859
3860 #[test]
Lukasz Anforowicz71716b72022-01-26 17:05:05 +00003861 fn test_impl_from_for_explicit_conversion_constructor() -> Result<()> {
3862 let ir = ir_from_cc(
3863 r#"#pragma clang lifetime_elision
3864 struct SomeStruct final {
3865 explicit SomeStruct(int i);
3866 };"#,
3867 )?;
3868 let rs_api = generate_rs_api(&ir)?;
3869 // As discussed in b/214020567 for now we only generate `From::from` bindings
3870 // for *implicit* C++ conversion constructors.
3871 assert_rs_not_matches!(rs_api, quote! {impl From});
3872 Ok(())
3873 }
3874
3875 #[test]
3876 fn test_impl_from_for_implicit_conversion_constructor() -> Result<()> {
3877 let ir = ir_from_cc(
3878 r#"#pragma clang lifetime_elision
3879 struct SomeStruct final {
3880 SomeStruct(int i); // implicit - no `explicit` keyword
3881 };"#,
3882 )?;
3883 let rs_api = generate_rs_api(&ir)?;
3884 // As discussed in b/214020567 we generate `From::from` bindings for
3885 // *implicit* C++ conversion constructors.
3886 assert_rs_matches!(
3887 rs_api,
3888 quote! {
3889 impl From<i32> for SomeStruct {
3890 #[inline(always)]
3891 fn from(i: i32) -> Self {
Rosica Dejanovskaaccf00b2022-04-01 13:28:04 -07003892 let mut tmp = rust_std::mem::MaybeUninit::<Self>::zeroed();
Lukasz Anforowicz71716b72022-01-26 17:05:05 +00003893 unsafe {
3894 crate::detail::__rust_thunk___ZN10SomeStructC1Ei(&mut tmp, i);
3895 tmp.assume_init()
3896 }
3897 }
3898 }
3899 }
3900 );
3901 Ok(())
3902 }
3903
3904 #[test]
Lukasz Anforowicz5e623782022-03-14 16:52:23 +00003905 fn test_impl_from_for_implicit_conversion_from_reference() -> Result<()> {
3906 let ir = ir_from_cc(
3907 r#"#pragma clang lifetime_elision
3908 struct SomeOtherStruct final { int i; };
3909 struct StructUnderTest final {
3910 StructUnderTest(const SomeOtherStruct& other); // implicit - no `explicit` keyword
3911 };"#,
3912 )?;
3913 let rs_api = generate_rs_api(&ir)?;
3914 // This is a regression test for b/223800038: We want to ensure that the
3915 // code says `impl<'b>` (instead of incorrectly declaring that lifetime
3916 // in `fn from<'b>`).
3917 assert_rs_matches!(
3918 rs_api,
3919 quote! {
3920 impl<'b> From<&'b SomeOtherStruct> for StructUnderTest {
3921 #[inline(always)]
3922 fn from(other: &'b SomeOtherStruct) -> Self {
Rosica Dejanovskaaccf00b2022-04-01 13:28:04 -07003923 let mut tmp = rust_std::mem::MaybeUninit::<Self>::zeroed();
Lukasz Anforowicz5e623782022-03-14 16:52:23 +00003924 unsafe {
3925 crate::detail::__rust_thunk___ZN15StructUnderTestC1ERK15SomeOtherStruct(
3926 &mut tmp, other);
3927 tmp.assume_init()
3928 }
3929 }
3930 }
3931 },
3932 );
3933 Ok(())
3934 }
3935
3936 #[test]
Lukasz Anforowicz732ca642022-02-03 20:58:38 +00003937 fn test_impl_eq_for_member_function() -> Result<()> {
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +00003938 let ir = ir_from_cc(
3939 r#"#pragma clang lifetime_elision
3940 struct SomeStruct final {
3941 inline bool operator==(const SomeStruct& other) const {
3942 return i == other.i;
3943 }
3944 int i;
3945 };"#,
3946 )?;
3947 let rs_api = generate_rs_api(&ir)?;
3948 assert_rs_matches!(
3949 rs_api,
3950 quote! {
3951 impl PartialEq<SomeStruct> for SomeStruct {
3952 #[inline(always)]
3953 fn eq<'a, 'b>(&'a self, other: &'b SomeStruct) -> bool {
3954 unsafe { crate::detail::__rust_thunk___ZNK10SomeStructeqERKS_(self, other) }
3955 }
3956 }
3957 }
3958 );
3959 let rs_api_impl = generate_rs_api_impl(&ir)?;
3960 assert_cc_matches!(
3961 rs_api_impl,
3962 quote! {
3963 extern "C" bool __rust_thunk___ZNK10SomeStructeqERKS_(
3964 const class SomeStruct* __this, const class SomeStruct& other) {
Devin Jeanpierredeea7892022-03-29 02:13:31 -07003965 return __this->operator==(std::forward<decltype(other)>(other));
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +00003966 }
3967 }
3968 );
3969 Ok(())
3970 }
3971
3972 #[test]
Lukasz Anforowicz732ca642022-02-03 20:58:38 +00003973 fn test_impl_eq_for_free_function() -> Result<()> {
3974 let ir = ir_from_cc(
3975 r#"#pragma clang lifetime_elision
3976 struct SomeStruct final { int i; };
3977 bool operator==(const SomeStruct& lhs, const SomeStruct& rhs) {
3978 return lhs.i == rhs.i;
3979 }"#,
3980 )?;
3981 let rs_api = generate_rs_api(&ir)?;
3982 assert_rs_matches!(
3983 rs_api,
3984 quote! {
3985 impl PartialEq<SomeStruct> for SomeStruct {
3986 #[inline(always)]
3987 fn eq<'a, 'b>(&'a self, rhs: &'b SomeStruct) -> bool {
3988 unsafe { crate::detail::__rust_thunk___ZeqRK10SomeStructS1_(self, rhs) }
3989 }
3990 }
3991 }
3992 );
3993 Ok(())
3994 }
3995
3996 #[test]
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +00003997 fn test_impl_eq_non_const_member_function() -> Result<()> {
3998 let ir = ir_from_cc(
3999 r#"#pragma clang lifetime_elision
4000 struct SomeStruct final {
4001 bool operator==(const SomeStruct& other) /* no `const` here */;
4002 };"#,
4003 )?;
4004 let rs_api = generate_rs_api(&ir)?;
4005 assert_rs_not_matches!(rs_api, quote! {impl PartialEq});
4006 Ok(())
4007 }
4008
4009 #[test]
4010 fn test_impl_eq_rhs_by_value() -> Result<()> {
4011 let ir = ir_from_cc(
4012 r#"#pragma clang lifetime_elision
4013 struct SomeStruct final {
4014 bool operator==(SomeStruct other) const;
4015 };"#,
4016 )?;
4017 let rs_api = generate_rs_api(&ir)?;
4018 assert_rs_not_matches!(rs_api, quote! {impl PartialEq});
4019 Ok(())
4020 }
4021
4022 #[test]
Dmitri Gribenko67cbfd22022-03-24 13:39:34 +00004023 fn test_thunk_ident_function() -> Result<()> {
4024 let ir = ir_from_cc("inline int foo() {}")?;
4025 let func = retrieve_func(&ir, "foo");
4026 assert_eq!(thunk_ident(func), make_rs_ident("__rust_thunk___Z3foov"));
4027 Ok(())
Devin Jeanpierre45cb1162021-10-27 10:54:28 +00004028 }
4029
4030 #[test]
4031 fn test_thunk_ident_special_names() {
Marcel Hlopko4b13b962021-12-06 12:40:56 +00004032 let ir = ir_from_cc("struct Class {};").unwrap();
Devin Jeanpierre45cb1162021-10-27 10:54:28 +00004033
Googler45ad2752021-12-06 12:12:35 +00004034 let destructor =
4035 ir.functions().find(|f| f.name == UnqualifiedIdentifier::Destructor).unwrap();
Lukasz Anforowiczdd9ae0f2022-02-17 15:52:53 +00004036 assert_eq!(thunk_ident(destructor), make_rs_ident("__rust_thunk___ZN5ClassD1Ev"));
Devin Jeanpierre45cb1162021-10-27 10:54:28 +00004037
Lukasz Anforowicz49b5bbc2022-02-04 23:40:10 +00004038 let default_constructor = ir
4039 .functions()
4040 .find(|f| f.name == UnqualifiedIdentifier::Constructor && f.params.len() == 1)
4041 .unwrap();
Lukasz Anforowiczdd9ae0f2022-02-17 15:52:53 +00004042 assert_eq!(thunk_ident(default_constructor), make_rs_ident("__rust_thunk___ZN5ClassC1Ev"));
Devin Jeanpierre45cb1162021-10-27 10:54:28 +00004043 }
Googler7cced422021-12-06 11:58:39 +00004044
4045 #[test]
Marcel Hlopko89547752021-12-10 09:39:41 +00004046 fn test_elided_lifetimes() -> Result<()> {
Googler7cced422021-12-06 11:58:39 +00004047 let ir = ir_from_cc(
4048 r#"#pragma clang lifetime_elision
Devin Jeanpierre88343c72022-01-15 01:10:23 +00004049 struct S final {
Googler7cced422021-12-06 11:58:39 +00004050 int& f(int& i);
4051 };"#,
Marcel Hlopko89547752021-12-10 09:39:41 +00004052 )?;
4053 let rs_api = generate_rs_api(&ir)?;
4054 assert_rs_matches!(
4055 rs_api,
4056 quote! {
Lukasz Anforowicz231a3bb2022-01-12 14:05:59 +00004057 pub fn f<'a, 'b>(&'a mut self, i: &'b mut i32) -> &'a mut i32 { ... }
Marcel Hlopko89547752021-12-10 09:39:41 +00004058 }
Googler7cced422021-12-06 11:58:39 +00004059 );
Marcel Hlopko89547752021-12-10 09:39:41 +00004060 assert_rs_matches!(
4061 rs_api,
4062 quote! {
Googler6804a012022-01-05 07:04:36 +00004063 pub(crate) fn __rust_thunk___ZN1S1fERi<'a, 'b>(__this: &'a mut S, i: &'b mut i32)
4064 -> &'a mut i32;
Marcel Hlopko89547752021-12-10 09:39:41 +00004065 }
Googler7cced422021-12-06 11:58:39 +00004066 );
Marcel Hlopko89547752021-12-10 09:39:41 +00004067 Ok(())
Googler7cced422021-12-06 11:58:39 +00004068 }
Lukasz Anforowiczdaff0402021-12-23 00:37:50 +00004069
4070 #[test]
Googler386e5942022-02-24 08:53:29 +00004071 fn test_annotated_lifetimes() -> Result<()> {
4072 let ir = ir_from_cc(
4073 r#"[[clang::annotate("lifetimes", "a, a -> a")]]
4074 int& f(int& i1, int& i2);
4075 "#,
4076 )?;
4077 let rs_api = generate_rs_api(&ir)?;
4078 assert_rs_matches!(
4079 rs_api,
4080 quote! {
4081 pub fn f<'a>(i1: &'a mut i32, i2: &'a mut i32) -> &'a mut i32 { ... }
4082 }
4083 );
4084 assert_rs_matches!(
4085 rs_api,
4086 quote! {
4087 pub(crate) fn __rust_thunk___Z1fRiS_<'a>(i1: &'a mut i32, i2: &'a mut i32)
4088 -> &'a mut i32;
4089 }
4090 );
4091 Ok(())
4092 }
4093
4094 #[test]
Lukasz Anforowiczdaff0402021-12-23 00:37:50 +00004095 fn test_format_generic_params() -> Result<()> {
4096 assert_rs_matches!(format_generic_params(std::iter::empty::<syn::Ident>()), quote! {});
4097
Marcel Hlopkoeaae9b72022-01-21 15:54:11 +00004098 let idents = ["T1", "T2"].iter().map(|s| make_rs_ident(s));
Lukasz Anforowiczdaff0402021-12-23 00:37:50 +00004099 assert_rs_matches!(format_generic_params(idents), quote! { < T1, T2 > });
4100
4101 let lifetimes = ["a", "b"]
4102 .iter()
4103 .map(|s| syn::Lifetime::new(&format!("'{}", s), proc_macro2::Span::call_site()));
4104 assert_rs_matches!(format_generic_params(lifetimes), quote! { < 'a, 'b > });
4105
4106 Ok(())
4107 }
Googlerd03d05b2022-01-07 10:10:57 +00004108
4109 #[test]
4110 fn test_overloaded_functions() -> Result<()> {
4111 // TODO(b/213280424): We don't support creating bindings for overloaded
4112 // functions yet, except in the case of overloaded constructors with a
4113 // single parameter.
4114 let ir = ir_from_cc(
Lukasz Anforowicz55673c92022-01-27 19:37:26 +00004115 r#" #pragma clang lifetime_elision
4116 void f();
Googlerd03d05b2022-01-07 10:10:57 +00004117 void f(int i);
Devin Jeanpierre88343c72022-01-15 01:10:23 +00004118 struct S1 final {
Googlerd03d05b2022-01-07 10:10:57 +00004119 void f();
4120 void f(int i);
4121 };
Devin Jeanpierre88343c72022-01-15 01:10:23 +00004122 struct S2 final {
Googlerd03d05b2022-01-07 10:10:57 +00004123 void f();
4124 };
Devin Jeanpierre88343c72022-01-15 01:10:23 +00004125 struct S3 final {
Googlerd03d05b2022-01-07 10:10:57 +00004126 S3(int i);
4127 S3(double d);
4128 };
4129 "#,
4130 )?;
4131 let rs_api = generate_rs_api(&ir)?;
4132 let rs_api_str = tokens_to_string(rs_api.clone())?;
4133
4134 // Cannot overload free functions.
4135 assert!(rs_api_str.contains("Error while generating bindings for item 'f'"));
4136 assert_rs_not_matches!(rs_api, quote! {pub fn f()});
4137 assert_rs_not_matches!(rs_api, quote! {pub fn f(i: i32)});
4138
4139 // Cannot overload member functions.
4140 assert!(rs_api_str.contains("Error while generating bindings for item 'S1::f'"));
4141 assert_rs_not_matches!(rs_api, quote! {pub fn f(... S1 ...)});
4142
4143 // But we can import member functions that have the same name as a free
4144 // function.
Lukasz Anforowicz55673c92022-01-27 19:37:26 +00004145 assert_rs_matches!(rs_api, quote! {pub fn f<'a>(&'a mut self)});
Googlerd03d05b2022-01-07 10:10:57 +00004146
4147 // We can also import overloaded single-parameter constructors.
4148 assert_rs_matches!(rs_api, quote! {impl From<i32> for S3});
4149 assert_rs_matches!(rs_api, quote! {impl From<f64> for S3});
4150 Ok(())
4151 }
Googlerdcca7f72022-01-10 12:30:43 +00004152
4153 #[test]
4154 fn test_type_alias() -> Result<()> {
4155 let ir = ir_from_cc(
4156 r#"
Lukasz Anforowiczc503af42022-03-04 23:18:15 +00004157 // MyTypedefDecl doc comment
Googlerdcca7f72022-01-10 12:30:43 +00004158 typedef int MyTypedefDecl;
Lukasz Anforowiczc503af42022-03-04 23:18:15 +00004159
Googlerdcca7f72022-01-10 12:30:43 +00004160 using MyTypeAliasDecl = int;
Googler6a0a5252022-01-11 14:08:09 +00004161 using MyTypeAliasDecl_Alias = MyTypeAliasDecl;
4162
Devin Jeanpierre88343c72022-01-15 01:10:23 +00004163 struct S final {};
Googler6a0a5252022-01-11 14:08:09 +00004164 using S_Alias = S;
4165 using S_Alias_Alias = S_Alias;
4166
4167 inline void f(MyTypedefDecl t) {}
Googlerdcca7f72022-01-10 12:30:43 +00004168 "#,
4169 )?;
4170 let rs_api = generate_rs_api(&ir)?;
Lukasz Anforowiczc503af42022-03-04 23:18:15 +00004171 assert_rs_matches!(
4172 rs_api,
4173 quote! {
4174 #[doc = " MyTypedefDecl doc comment"]
4175 pub type MyTypedefDecl = i32;
4176 }
4177 );
Googler6a0a5252022-01-11 14:08:09 +00004178 assert_rs_matches!(rs_api, quote! { pub type MyTypeAliasDecl = i32; });
4179 assert_rs_matches!(rs_api, quote! { pub type MyTypeAliasDecl_Alias = MyTypeAliasDecl; });
4180 assert_rs_matches!(rs_api, quote! { pub type S_Alias = S; });
4181 assert_rs_matches!(rs_api, quote! { pub type S_Alias_Alias = S_Alias; });
4182 assert_rs_matches!(rs_api, quote! { pub fn f(t: MyTypedefDecl) });
4183 assert_cc_matches!(
4184 generate_rs_api_impl(&ir)?,
4185 quote! {
Devin Jeanpierredeea7892022-03-29 02:13:31 -07004186 extern "C" void __rust_thunk___Z1fi(MyTypedefDecl t){ f (std::forward<decltype(t)>(t)) ; }
Googler6a0a5252022-01-11 14:08:09 +00004187 }
4188 );
Googlerdcca7f72022-01-10 12:30:43 +00004189 Ok(())
4190 }
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00004191
4192 #[test]
4193 fn test_rs_type_kind_implements_copy() -> Result<()> {
Lukasz Anforowicz20651e32022-02-10 14:52:15 +00004194 let template = r#" LIFETIMES
Devin Jeanpierre88343c72022-01-15 01:10:23 +00004195 struct [[clang::trivial_abi]] TrivialStruct final { int i; };
4196 struct [[clang::trivial_abi]] UserDefinedCopyConstructor final {
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00004197 UserDefinedCopyConstructor(const UserDefinedCopyConstructor&);
4198 };
4199 using IntAlias = int;
4200 using TrivialAlias = TrivialStruct;
4201 using NonTrivialAlias = UserDefinedCopyConstructor;
4202 void func(PARAM_TYPE some_param);
4203 "#;
4204 assert_impl_all!(i32: Copy);
4205 assert_impl_all!(&i32: Copy);
4206 assert_not_impl_all!(&mut i32: Copy);
Lukasz Anforowicz20651e32022-02-10 14:52:15 +00004207 assert_impl_all!(Option<&i32>: Copy);
4208 assert_not_impl_all!(Option<&mut i32>: Copy);
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00004209 assert_impl_all!(*const i32: Copy);
4210 assert_impl_all!(*mut i32: Copy);
Lukasz Anforowicz20651e32022-02-10 14:52:15 +00004211 struct Test {
4212 // Test inputs:
4213 cc: &'static str,
4214 lifetimes: bool,
4215 // Expected test outputs:
4216 rs: &'static str,
4217 is_copy: bool,
4218 }
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00004219 let tests = vec![
4220 // Validity of the next few tests is verified via
4221 // `assert_[not_]impl_all!` static assertions above.
Lukasz Anforowicz20651e32022-02-10 14:52:15 +00004222 Test { cc: "int", lifetimes: true, rs: "i32", is_copy: true },
4223 Test { cc: "const int&", lifetimes: true, rs: "&'a i32", is_copy: true },
4224 Test { cc: "int&", lifetimes: true, rs: "&'a mut i32", is_copy: false },
4225 Test { cc: "const int*", lifetimes: true, rs: "Option<&'a i32>", is_copy: true },
4226 Test { cc: "int*", lifetimes: true, rs: "Option<&'a mut i32>", is_copy: false },
4227 Test { cc: "const int*", lifetimes: false, rs: "*const i32", is_copy: true },
4228 Test { cc: "int*", lifetimes: false, rs: "*mut i32", is_copy: true },
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00004229 // Tests below have been thought-through and verified "manually".
Lukasz Anforowicz20651e32022-02-10 14:52:15 +00004230 // TrivialStruct is expected to derive Copy.
4231 Test { cc: "TrivialStruct", lifetimes: true, rs: "TrivialStruct", is_copy: true },
4232 Test {
4233 cc: "UserDefinedCopyConstructor",
4234 lifetimes: true,
4235 rs: "UserDefinedCopyConstructor",
4236 is_copy: false,
4237 },
4238 Test { cc: "IntAlias", lifetimes: true, rs: "IntAlias", is_copy: true },
4239 Test { cc: "TrivialAlias", lifetimes: true, rs: "TrivialAlias", is_copy: true },
4240 Test { cc: "NonTrivialAlias", lifetimes: true, rs: "NonTrivialAlias", is_copy: false },
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00004241 ];
Lukasz Anforowicz20651e32022-02-10 14:52:15 +00004242 for test in tests.iter() {
4243 let test_name = format!("cc='{}', lifetimes={}", test.cc, test.lifetimes);
4244 let cc_input = template.replace("PARAM_TYPE", test.cc).replace(
4245 "LIFETIMES",
4246 if test.lifetimes { "#pragma clang lifetime_elision" } else { "" },
4247 );
4248 let ir = ir_from_cc(&cc_input)?;
Lukasz Anforowicz9c663ca2022-02-09 01:33:31 +00004249 let f = retrieve_func(&ir, "func");
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00004250 let t = RsTypeKind::new(&f.params[0].type_.rs_type, &ir)?;
Lukasz Anforowicz20651e32022-02-10 14:52:15 +00004251
Devin Jeanpierred2b7a8f2022-04-01 04:37:16 -07004252 let fmt = tokens_to_string(t.format(&ir))?;
Lukasz Anforowicz20651e32022-02-10 14:52:15 +00004253 assert_eq!(test.rs, fmt, "Testing: {}", test_name);
4254
4255 assert_eq!(test.is_copy, t.implements_copy(), "Testing: {}", test_name);
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00004256 }
4257 Ok(())
4258 }
Lukasz Anforowicza94ab702022-01-14 22:40:25 +00004259
4260 #[test]
4261 fn test_rs_type_kind_is_shared_ref_to_with_lifetimes() -> Result<()> {
4262 let ir = ir_from_cc(
4263 "#pragma clang lifetime_elision
4264 struct SomeStruct {};
4265 void foo(const SomeStruct& foo_param);
4266 void bar(SomeStruct& bar_param);",
4267 )?;
4268 let record = ir.records().next().unwrap();
Lukasz Anforowicz9c663ca2022-02-09 01:33:31 +00004269 let foo_func = retrieve_func(&ir, "foo");
4270 let bar_func = retrieve_func(&ir, "bar");
Lukasz Anforowicza94ab702022-01-14 22:40:25 +00004271
4272 // const-ref + lifetimes in C++ ===> shared-ref in Rust
4273 assert_eq!(foo_func.params.len(), 1);
4274 let foo_param = &foo_func.params[0];
4275 assert_eq!(&foo_param.identifier.identifier, "foo_param");
4276 let foo_type = RsTypeKind::new(&foo_param.type_.rs_type, &ir)?;
4277 assert!(foo_type.is_shared_ref_to(record));
4278 assert!(matches!(foo_type, RsTypeKind::Reference { mutability: Mutability::Const, .. }));
4279
4280 // non-const-ref + lifetimes in C++ ===> mutable-ref in Rust
4281 assert_eq!(bar_func.params.len(), 1);
4282 let bar_param = &bar_func.params[0];
4283 assert_eq!(&bar_param.identifier.identifier, "bar_param");
4284 let bar_type = RsTypeKind::new(&bar_param.type_.rs_type, &ir)?;
4285 assert!(!bar_type.is_shared_ref_to(record));
4286 assert!(matches!(bar_type, RsTypeKind::Reference { mutability: Mutability::Mut, .. }));
4287
4288 Ok(())
4289 }
4290
4291 #[test]
4292 fn test_rs_type_kind_is_shared_ref_to_without_lifetimes() -> Result<()> {
4293 let ir = ir_from_cc(
4294 "struct SomeStruct {};
4295 void foo(const SomeStruct& foo_param);",
4296 )?;
4297 let record = ir.records().next().unwrap();
Lukasz Anforowicz9c663ca2022-02-09 01:33:31 +00004298 let foo_func = retrieve_func(&ir, "foo");
Lukasz Anforowicza94ab702022-01-14 22:40:25 +00004299
4300 // const-ref + *no* lifetimes in C++ ===> const-pointer in Rust
4301 assert_eq!(foo_func.params.len(), 1);
4302 let foo_param = &foo_func.params[0];
4303 assert_eq!(&foo_param.identifier.identifier, "foo_param");
4304 let foo_type = RsTypeKind::new(&foo_param.type_.rs_type, &ir)?;
4305 assert!(!foo_type.is_shared_ref_to(record));
4306 assert!(matches!(foo_type, RsTypeKind::Pointer { mutability: Mutability::Const, .. }));
4307
4308 Ok(())
4309 }
Marcel Hlopkoeaae9b72022-01-21 15:54:11 +00004310
4311 #[test]
Lukasz Anforowicz90bdb962022-02-14 21:07:45 +00004312 fn test_rs_type_kind_dfs_iter_ordering() {
4313 // Set up a test input representing: A<B<C>, D<E>>.
4314 let a = {
4315 let b = {
4316 let c = RsTypeKind::Other { name: "C", type_args: vec![] };
4317 RsTypeKind::Other { name: "B", type_args: vec![c] }
4318 };
4319 let d = {
4320 let e = RsTypeKind::Other { name: "E", type_args: vec![] };
4321 RsTypeKind::Other { name: "D", type_args: vec![e] }
4322 };
4323 RsTypeKind::Other { name: "A", type_args: vec![b, d] }
4324 };
4325 let dfs_names = a
4326 .dfs_iter()
4327 .map(|t| match t {
4328 RsTypeKind::Other { name, .. } => *name,
4329 _ => unreachable!("Only 'other' types are used in this test"),
4330 })
4331 .collect_vec();
4332 assert_eq!(vec!["A", "B", "C", "D", "E"], dfs_names);
4333 }
4334
4335 #[test]
Lukasz Anforowiczcf230fd2022-02-18 19:20:39 +00004336 fn test_rs_type_kind_dfs_iter_ordering_for_func_ptr() {
4337 // Set up a test input representing: fn(A, B) -> C
4338 let f = {
4339 let a = RsTypeKind::Other { name: "A", type_args: vec![] };
4340 let b = RsTypeKind::Other { name: "B", type_args: vec![] };
4341 let c = RsTypeKind::Other { name: "C", type_args: vec![] };
4342 RsTypeKind::FuncPtr { abi: "blah", param_types: vec![a, b], return_type: Box::new(c) }
4343 };
4344 let dfs_names = f
4345 .dfs_iter()
4346 .map(|t| match t {
4347 RsTypeKind::FuncPtr { .. } => "fn",
4348 RsTypeKind::Other { name, .. } => *name,
4349 _ => unreachable!("Only FuncPtr and Other kinds are used in this test"),
4350 })
4351 .collect_vec();
4352 assert_eq!(vec!["fn", "A", "B", "C"], dfs_names);
4353 }
4354
4355 #[test]
Lukasz Anforowicz90bdb962022-02-14 21:07:45 +00004356 fn test_rs_type_kind_lifetimes() -> Result<()> {
4357 let ir = ir_from_cc(
4358 r#"
4359 #pragma clang lifetime_elision
4360 using TypeAlias = int&;
4361 struct SomeStruct {};
4362 void foo(int a, int& b, int* c, int** d, TypeAlias e, SomeStruct f); "#,
4363 )?;
4364 let f = retrieve_func(&ir, "foo");
4365 let ret = RsTypeKind::new(&f.return_type.rs_type, &ir)?;
4366 let a = RsTypeKind::new(&f.params[0].type_.rs_type, &ir)?;
4367 let b = RsTypeKind::new(&f.params[1].type_.rs_type, &ir)?;
4368 let c = RsTypeKind::new(&f.params[2].type_.rs_type, &ir)?;
4369 let d = RsTypeKind::new(&f.params[3].type_.rs_type, &ir)?;
4370 let e = RsTypeKind::new(&f.params[4].type_.rs_type, &ir)?;
4371 let f = RsTypeKind::new(&f.params[5].type_.rs_type, &ir)?;
4372
4373 assert_eq!(0, ret.lifetimes().count()); // No lifetimes on `void`.
4374 assert_eq!(0, a.lifetimes().count()); // No lifetimes on `int`.
4375 assert_eq!(1, b.lifetimes().count()); // `&'a i32` has a single lifetime.
4376 assert_eq!(1, c.lifetimes().count()); // `Option<&'b i32>` has a single lifetime.
4377 assert_eq!(2, d.lifetimes().count()); // `&'c Option<&'d i32>` has two lifetimes.
4378 assert_eq!(1, e.lifetimes().count()); // Lifetime of underlying type should show through.
4379 assert_eq!(0, f.lifetimes().count()); // No lifetimes on structs (yet).
4380 Ok(())
4381 }
4382
4383 #[test]
4384 fn test_rs_type_kind_lifetimes_raw_ptr() -> Result<()> {
4385 let ir = ir_from_cc("void foo(int* a);")?;
4386 let f = retrieve_func(&ir, "foo");
4387 let a = RsTypeKind::new(&f.params[0].type_.rs_type, &ir)?;
4388 assert_eq!(0, a.lifetimes().count()); // No lifetimes on `int*`.
4389 Ok(())
4390 }
4391
4392 #[test]
Marcel Hlopkoeaae9b72022-01-21 15:54:11 +00004393 fn test_rust_keywords_are_escaped_in_rs_api_file() -> Result<()> {
4394 let ir = ir_from_cc("struct type { int dyn; };")?;
4395 let rs_api = generate_rs_api(&ir)?;
4396 assert_rs_matches!(rs_api, quote! { struct r#type { ... r#dyn: i32 ... } });
4397 Ok(())
4398 }
4399
4400 #[test]
4401 fn test_rust_keywords_are_not_escaped_in_rs_api_impl_file() -> Result<()> {
4402 let ir = ir_from_cc("struct type { int dyn; };")?;
4403 let rs_api_impl = generate_rs_api_impl(&ir)?;
4404 assert_cc_matches!(rs_api_impl, quote! { static_assert(offsetof(class type, dyn) ... ) });
4405 Ok(())
4406 }
Marcel Hlopko14ee3c82022-02-09 09:46:23 +00004407
4408 #[test]
4409 fn test_no_aligned_attr() {
4410 let ir = ir_from_cc("struct SomeStruct {};").unwrap();
4411 let rs_api = generate_rs_api(&ir).unwrap();
4412
4413 assert_rs_matches! {rs_api, quote! {
4414 #[repr(C)]
4415 pub struct SomeStruct { ... }
4416 }};
4417 }
4418
4419 #[test]
4420 fn test_aligned_attr() {
4421 let ir = ir_from_cc("struct SomeStruct {} __attribute__((aligned(64)));").unwrap();
4422 let rs_api = generate_rs_api(&ir).unwrap();
4423
4424 assert_rs_matches! {rs_api, quote! {
4425 #[repr(C, align(64))]
4426 pub struct SomeStruct { ... }
4427 }
4428 };
4429 }
Devin Jeanpierre149950d2022-02-22 21:02:02 +00004430
4431 /// !Unpin references should not be pinned.
4432 #[test]
4433 fn test_nonunpin_ref_param() -> Result<()> {
4434 let rs_api_impl = generate_rs_api(&ir_from_cc(
4435 r#"
4436 #pragma clang lifetime_elision
4437 struct S {~S();};
4438 void Function(const S& s);
4439 "#,
4440 )?)?;
4441 assert_rs_matches!(
4442 rs_api_impl,
4443 quote! {
4444 fn Function<'a>(s: &'a S) { ... }
4445 }
4446 );
4447 Ok(())
4448 }
4449
4450 /// !Unpin mut references must be pinned.
4451 #[test]
4452 fn test_nonunpin_mut_param() -> Result<()> {
4453 let rs_api_impl = generate_rs_api(&ir_from_cc(
4454 r#"
4455 #pragma clang lifetime_elision
4456 struct S {~S();};
4457 void Function(S& s);
4458 "#,
4459 )?)?;
4460 assert_rs_matches!(
4461 rs_api_impl,
4462 quote! {
Rosica Dejanovskaaccf00b2022-04-01 13:28:04 -07004463 fn Function<'a>(s: rust_std::pin::Pin<&'a mut S>) { ... }
Devin Jeanpierre149950d2022-02-22 21:02:02 +00004464 }
4465 );
4466 Ok(())
4467 }
4468
4469 /// !Unpin &self should not be pinned.
4470 #[test]
4471 fn test_nonunpin_ref_self() -> Result<()> {
4472 let rs_api_impl = generate_rs_api(&ir_from_cc(
4473 r#"
4474 #pragma clang lifetime_elision
4475 struct S {
4476 ~S();
4477 void Function() const;
4478 };
4479 "#,
4480 )?)?;
4481 assert_rs_matches!(
4482 rs_api_impl,
4483 quote! {
4484 fn Function<'a>(&'a self) { ... }
4485 }
4486 );
4487 Ok(())
4488 }
4489
4490 /// !Unpin &mut self must be pinned.
4491 #[test]
4492 fn test_nonunpin_mut_self() -> Result<()> {
4493 let rs_api_impl = generate_rs_api(&ir_from_cc(
4494 r#"
4495 #pragma clang lifetime_elision
4496 struct S {
4497 ~S();
4498 void Function();
4499 };
4500 "#,
4501 )?)?;
4502 assert_rs_matches!(
4503 rs_api_impl,
4504 quote! {
Rosica Dejanovskaaccf00b2022-04-01 13:28:04 -07004505 fn Function<'a>(self: rust_std::pin::Pin<&'a mut Self>) { ... }
Devin Jeanpierre149950d2022-02-22 21:02:02 +00004506 }
4507 );
4508 Ok(())
4509 }
4510
4511 /// Drop::drop must not use self : Pin<...>.
4512 #[test]
4513 fn test_nonunpin_drop() -> Result<()> {
4514 let rs_api_impl = generate_rs_api(&ir_from_cc(
4515 r#"
4516 struct S {~S();};
4517 "#,
4518 )?)?;
4519 assert_rs_matches!(
4520 rs_api_impl,
4521 quote! {
4522 fn drop(&mut self) { ... }
4523 }
4524 );
4525 Ok(())
4526 }
Devin Jeanpierre6f607372022-03-22 21:34:38 +00004527
4528 #[test]
4529 fn test_nonunpin_one_arg_constructor() -> Result<()> {
4530 let ir = ir_from_cc(
4531 r#"#pragma clang lifetime_elision
4532 // This type must be `!Unpin`.
4533 struct HasConstructor {explicit HasConstructor(unsigned char input) {}};"#,
4534 )?;
4535 let rs_api = generate_rs_api(&ir)?;
4536 assert_rs_matches!(rs_api, quote! {impl !Unpin for HasConstructor {} });
4537 assert_rs_matches!(
4538 rs_api,
4539 quote! {
4540 impl ctor::CtorNew<u8> for HasConstructor {
4541 type CtorType = impl ctor::Ctor<Output = Self>;
4542
4543 #[inline (always)]
4544 fn ctor_new(input: u8) -> Self::CtorType {
Rosica Dejanovskaaccf00b2022-04-01 13:28:04 -07004545 ctor::FnCtor::new(move |dest: rust_std::pin::Pin<&mut rust_std::mem::MaybeUninit<Self>>| {
Devin Jeanpierre6f607372022-03-22 21:34:38 +00004546 unsafe {
Rosica Dejanovskaaccf00b2022-04-01 13:28:04 -07004547 crate::detail::__rust_thunk___ZN14HasConstructorC1Eh(rust_std::pin::Pin::into_inner_unchecked(dest), input);
Devin Jeanpierre6f607372022-03-22 21:34:38 +00004548 }
4549 })
4550 }
4551 }
4552 }
4553 );
4554 Ok(())
4555 }
Marcel Hlopko42abfc82021-08-09 07:03:17 +00004556}