blob: 2a4dfca3bc36e04ab44d16675b99941372ec397b [file] [log] [blame]
Marcel Hlopko42abfc82021-08-09 07:03:17 +00001// Part of the Crubit project, under the Apache License v2.0 with LLVM
2// Exceptions. See /LICENSE for license information.
3// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
4
Lukasz Anforowicz40c2eb82022-01-11 18:22:31 +00005use anyhow::{anyhow, bail, ensure, Context, Result};
Marcel Hlopko884ae7f2021-08-18 13:58:22 +00006use ffi_types::*;
Marcel Hlopko42abfc82021-08-09 07:03:17 +00007use ir::*;
8use itertools::Itertools;
Googler5ea88642021-09-29 08:05:59 +00009use proc_macro2::{Ident, Literal, TokenStream};
Devin Jeanpierre92ca2612022-04-06 11:35:13 -070010use quote::{format_ident, quote, ToTokens};
Devin Jeanpierre9886fb42022-04-01 04:31:20 -070011use std::collections::{BTreeSet, HashSet};
Marcel Hlopko42abfc82021-08-09 07:03:17 +000012use std::iter::Iterator;
13use std::panic::catch_unwind;
14use std::process;
Marcel Hlopko65d05f02021-12-09 12:29:24 +000015use token_stream_printer::{rs_tokens_to_formatted_string, tokens_to_string};
Marcel Hlopko42abfc82021-08-09 07:03:17 +000016
Marcel Hlopko45fba972021-08-23 19:52:20 +000017/// FFI equivalent of `Bindings`.
18#[repr(C)]
19pub struct FfiBindings {
20 rs_api: FfiU8SliceBox,
21 rs_api_impl: FfiU8SliceBox,
22}
23
24/// Deserializes IR from `json` and generates bindings source code.
Marcel Hlopko42abfc82021-08-09 07:03:17 +000025///
26/// This function panics on error.
27///
Lukasz Anforowiczdd9ae0f2022-02-17 15:52:53 +000028/// # Safety
29///
30/// Expectations:
31/// * function expects that param `json` is a FfiU8Slice for a valid array of
32/// bytes with the given size.
33/// * function expects that param `json` doesn't change during the call.
34///
Marcel Hlopko42abfc82021-08-09 07:03:17 +000035/// Ownership:
Michael Forsterbee84482021-10-13 08:35:38 +000036/// * function doesn't take ownership of (in other words it borrows) the
37/// param `json`
Marcel Hlopko42abfc82021-08-09 07:03:17 +000038/// * function passes ownership of the returned value to the caller
Marcel Hlopko42abfc82021-08-09 07:03:17 +000039#[no_mangle]
Marcel Hlopko45fba972021-08-23 19:52:20 +000040pub unsafe extern "C" fn GenerateBindingsImpl(json: FfiU8Slice) -> FfiBindings {
Marcel Hlopko42abfc82021-08-09 07:03:17 +000041 catch_unwind(|| {
Marcel Hlopko45fba972021-08-23 19:52:20 +000042 // It is ok to abort here.
43 let Bindings { rs_api, rs_api_impl } = generate_bindings(json.as_slice()).unwrap();
44
45 FfiBindings {
46 rs_api: FfiU8SliceBox::from_boxed_slice(rs_api.into_bytes().into_boxed_slice()),
47 rs_api_impl: FfiU8SliceBox::from_boxed_slice(
48 rs_api_impl.into_bytes().into_boxed_slice(),
49 ),
50 }
Marcel Hlopko42abfc82021-08-09 07:03:17 +000051 })
52 .unwrap_or_else(|_| process::abort())
53}
54
Marcel Hlopko45fba972021-08-23 19:52:20 +000055/// Source code for generated bindings.
56struct Bindings {
57 // Rust source code.
58 rs_api: String,
59 // C++ source code.
60 rs_api_impl: String,
Marcel Hlopko42abfc82021-08-09 07:03:17 +000061}
62
Marcel Hlopko45fba972021-08-23 19:52:20 +000063fn generate_bindings(json: &[u8]) -> Result<Bindings> {
64 let ir = deserialize_ir(json)?;
Marcel Hlopkoca84ff42021-12-09 14:15:14 +000065
66 // The code is formatted with a non-default rustfmt configuration. Prevent
Lukasz Anforowicz5b3f5302022-02-07 01:04:47 +000067 // downstream workflows from reformatting with a different configuration by
68 // marking the output with `@generated`. See also
69 // https://rust-lang.github.io/rustfmt/?version=v1.4.38&search=#format_generated_files
70 //
71 // TODO(lukasza): It would be nice to include "by $argv[0]"" in the
72 // @generated comment below. OTOH, `std::env::current_exe()` in our
73 // current build environment returns a guid-like path... :-/
Lukasz Anforowicz72c4d222022-02-18 19:07:28 +000074 //
75 // TODO(lukasza): Try to remove `#![rustfmt:skip]` - in theory it shouldn't
76 // be needed when `@generated` comment/keyword is present...
Lukasz Anforowicz5b3f5302022-02-07 01:04:47 +000077 let rs_api = format!(
Lukasz Anforowicz72c4d222022-02-18 19:07:28 +000078 "// Automatically @generated Rust bindings for C++ target\n\
79 // {target}\n\
80 #![rustfmt::skip]\n\
81 {code}",
Lukasz Anforowicz5b3f5302022-02-07 01:04:47 +000082 target = ir.current_target().0,
83 code = rs_tokens_to_formatted_string(generate_rs_api(&ir)?)?
84 );
Marcel Hlopko89547752021-12-10 09:39:41 +000085 let rs_api_impl = tokens_to_string(generate_rs_api_impl(&ir)?)?;
Marcel Hlopkoca84ff42021-12-09 14:15:14 +000086
Marcel Hlopko45fba972021-08-23 19:52:20 +000087 Ok(Bindings { rs_api, rs_api_impl })
88}
89
Devin Jeanpierre6d5e7cc2021-10-21 12:56:07 +000090/// Rust source code with attached information about how to modify the parent
91/// crate.
Devin Jeanpierre273eeae2021-10-06 13:29:35 +000092///
Michael Forsterbee84482021-10-13 08:35:38 +000093/// For example, the snippet `vec![].into_raw_parts()` is not valid unless the
94/// `vec_into_raw_parts` feature is enabled. So such a snippet should be
95/// represented as:
Devin Jeanpierre273eeae2021-10-06 13:29:35 +000096///
97/// ```
98/// RsSnippet {
Marcel Hlopkoeaae9b72022-01-21 15:54:11 +000099/// features: btree_set![make_rs_ident("vec_into_raw_parts")],
Devin Jeanpierre273eeae2021-10-06 13:29:35 +0000100/// tokens: quote!{vec![].into_raw_parts()},
101/// }
102/// ```
Lukasz Anforowiczeb19ac62022-02-05 00:10:16 +0000103#[derive(Clone, Debug)]
Devin Jeanpierre273eeae2021-10-06 13:29:35 +0000104struct RsSnippet {
105 /// Rust feature flags used by this snippet.
106 features: BTreeSet<Ident>,
107 /// The snippet itself, as a token stream.
108 tokens: TokenStream,
109}
110
111impl From<TokenStream> for RsSnippet {
112 fn from(tokens: TokenStream) -> Self {
113 RsSnippet { features: BTreeSet::new(), tokens }
114 }
115}
116
Michael Forsterbee84482021-10-13 08:35:38 +0000117/// If we know the original C++ function is codegenned and already compatible
118/// with `extern "C"` calling convention we skip creating/calling the C++ thunk
119/// since we can call the original C++ directly.
Marcel Hlopko3164eee2021-08-24 20:09:22 +0000120fn can_skip_cc_thunk(func: &Func) -> bool {
Devin Jeanpierre96839c12021-12-14 00:27:38 +0000121 // ## Inline functions
122 //
Michael Forsterbee84482021-10-13 08:35:38 +0000123 // Inline functions may not be codegenned in the C++ library since Clang doesn't
124 // know if Rust calls the function or not. Therefore in order to make inline
125 // functions callable from Rust we need to generate a C++ file that defines
126 // a thunk that delegates to the original inline function. When compiled,
127 // Clang will emit code for this thunk and Rust code will call the
Marcel Hlopko3164eee2021-08-24 20:09:22 +0000128 // thunk when the user wants to call the original inline function.
129 //
Michael Forsterbee84482021-10-13 08:35:38 +0000130 // This is not great runtime-performance-wise in regular builds (inline function
131 // will not be inlined, there will always be a function call), but it is
132 // correct. ThinLTO builds will be able to see through the thunk and inline
133 // code across the language boundary. For non-ThinLTO builds we plan to
134 // implement <internal link> which removes the runtime performance overhead.
Devin Jeanpierre96839c12021-12-14 00:27:38 +0000135 if func.is_inline {
136 return false;
137 }
138 // ## Virtual functions
139 //
140 // When calling virtual `A::Method()`, it's not necessarily the case that we'll
141 // specifically call the concrete `A::Method` impl. For example, if this is
142 // called on something whose dynamic type is some subclass `B` with an
143 // overridden `B::Method`, then we'll call that.
144 //
145 // We must reuse the C++ dynamic dispatching system. In this case, the easiest
146 // way to do it is by resorting to a C++ thunk, whose implementation will do
147 // the lookup.
148 //
149 // In terms of runtime performance, since this only occurs for virtual function
150 // calls, which are already slow, it may not be such a big deal. We can
151 // benchmark it later. :)
152 if let Some(meta) = &func.member_func_metadata {
153 if let Some(inst_meta) = &meta.instance_method_metadata {
154 if inst_meta.is_virtual {
155 return false;
156 }
157 }
158 }
Lukasz Anforowicz0b6a6ac2022-03-22 22:32:23 +0000159 // ## Custom calling convention requires a thunk.
160 //
161 // The thunk has the "C" calling convention, and internally can call the
162 // C++ function using any of the calling conventions supported by the C++
163 // compiler (which might not always match the set supported by Rust - e.g.,
164 // abi.rs doesn't contain "swiftcall" from
165 // clang::FunctionType::getNameForCallConv)
166 if !func.has_c_calling_convention {
167 return false;
168 }
Devin Jeanpierre96839c12021-12-14 00:27:38 +0000169
170 true
Marcel Hlopko3164eee2021-08-24 20:09:22 +0000171}
172
Googlerd03d05b2022-01-07 10:10:57 +0000173/// Uniquely identifies a generated Rust function.
Lukasz Anforowiczeb19ac62022-02-05 00:10:16 +0000174#[derive(Clone, Debug, PartialEq, Eq, Hash)]
Googlerd03d05b2022-01-07 10:10:57 +0000175struct FunctionId {
176 // If the function is on a trait impl, contains the name of the Self type for
177 // which the trait is being implemented.
178 self_type: Option<syn::Path>,
179 // Fully qualified path of the function. For functions in impl blocks, this
180 // includes the name of the type or trait on which the function is being
181 // implemented, e.g. `Default::default`.
182 function_path: syn::Path,
183}
184
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +0000185/// Returns the name of `func` in C++ syntax.
Googlerd03d05b2022-01-07 10:10:57 +0000186fn cxx_function_name(func: &Func, ir: &IR) -> Result<String> {
187 let record: Option<&str> = func
188 .member_func_metadata
189 .as_ref()
190 .map(|meta| meta.find_record(ir))
191 .transpose()?
Lukasz Anforowicz4c3a2cc2022-03-11 00:24:49 +0000192 .map(|r| &*r.cc_name);
Googlerd03d05b2022-01-07 10:10:57 +0000193
194 let func_name = match &func.name {
195 UnqualifiedIdentifier::Identifier(id) => id.identifier.clone(),
Lukasz Anforowicz9c663ca2022-02-09 01:33:31 +0000196 UnqualifiedIdentifier::Operator(op) => op.cc_name(),
Googlerd03d05b2022-01-07 10:10:57 +0000197 UnqualifiedIdentifier::Destructor => {
198 format!("~{}", record.expect("destructor must be associated with a record"))
199 }
200 UnqualifiedIdentifier::Constructor => {
Lukasz Anforowiczeb19ac62022-02-05 00:10:16 +0000201 record.expect("constructor must be associated with a record").to_string()
Googlerd03d05b2022-01-07 10:10:57 +0000202 }
203 };
204
205 if let Some(record_name) = record {
206 Ok(format!("{}::{}", record_name, func_name))
207 } else {
208 Ok(func_name)
209 }
210}
211
Lukasz Anforowiczeb19ac62022-02-05 00:10:16 +0000212fn make_unsupported_fn(func: &Func, ir: &IR, message: impl ToString) -> Result<UnsupportedItem> {
213 Ok(UnsupportedItem {
214 name: cxx_function_name(func, ir)?,
215 message: message.to_string(),
216 source_loc: func.source_loc.clone(),
Rosica Dejanovskad638cf52022-03-23 15:45:01 +0000217 id: func.id,
Lukasz Anforowiczeb19ac62022-02-05 00:10:16 +0000218 })
219}
220
Devin Jeanpierre9c34da62022-03-31 04:57:16 -0700221/// The name of a trait, with extra entries for specially-understood traits and
222/// families of traits.
Devin Jeanpierre103cbb52022-04-06 12:55:16 -0700223enum TraitName<'ir> {
224 /// The constructor trait for !Unpin types. e.g.
225 /// `CtorNew(RsTypeKind::Unit)` is the default constructor.
226 CtorNew(RsTypeKind<'ir>),
Devin Jeanpierre9c34da62022-03-31 04:57:16 -0700227 /// An Unpin constructor trait, e.g. From or Clone.
228 UnpinConstructor(TokenStream),
229 /// Any other trait, e.g. Eq.
230 Other(TokenStream),
231}
Devin Jeanpierre103cbb52022-04-06 12:55:16 -0700232impl<'ir> ToTokens for TraitName<'ir> {
Devin Jeanpierre9c34da62022-03-31 04:57:16 -0700233 fn to_tokens(&self, tokens: &mut TokenStream) {
234 match self {
235 Self::UnpinConstructor(t) | Self::Other(t) => t.to_tokens(tokens),
236 Self::CtorNew(args) => quote! { ctor::CtorNew < #args > }.to_tokens(tokens),
237 }
238 }
239}
240
241/// The kind of the `impl` block the function needs to be generated in.
Devin Jeanpierre103cbb52022-04-06 12:55:16 -0700242enum ImplKind<'ir> {
Devin Jeanpierre9c34da62022-03-31 04:57:16 -0700243 /// Used for free functions for which we don't want the `impl` block.
244 None { is_unsafe: bool },
245 /// Used for inherent methods for which we need an `impl SomeStruct { ... }`
246 /// block.
247 Struct {
248 /// For example, `SomeStruct`. Retrieved from
249 /// `func.member_func_metadata`.
250 record_name: Ident,
251 is_unsafe: bool,
252 /// Whether to format the first parameter as "self" (e.g. `__this:
253 /// &mut T` -> `&mut self`)
254 format_first_param_as_self: bool,
255 },
256 /// Used for trait methods for which we need an `impl TraitName for
257 /// SomeStruct { ... }` block.
258 Trait {
259 /// For example, `SomeStruct`.
260 /// Note that `record_name` might *not* be from
261 /// `func.member_func_metadata`.
262 record_name: Ident,
263 /// For example, `quote!{ From<i32> }`.
Devin Jeanpierre103cbb52022-04-06 12:55:16 -0700264 trait_name: TraitName<'ir>,
Devin Jeanpierre9c34da62022-03-31 04:57:16 -0700265
266 /// Where to declare lifetimes: `impl<'b>` VS `fn foo<'b>`.
267 declare_lifetimes: bool,
268 /// The generic params of trait `impl` (e.g. `<'b>`). These start
269 /// empty and only later are mutated into the correct value.
270 trait_generic_params: TokenStream,
271 /// Whether to format the first parameter as "self" (e.g. `__this:
272 /// &mut T` -> `&mut self`)
273 format_first_param_as_self: bool,
274 },
275}
Devin Jeanpierre103cbb52022-04-06 12:55:16 -0700276impl<'ir> ImplKind<'ir> {
Devin Jeanpierre9c34da62022-03-31 04:57:16 -0700277 fn new_trait(
Devin Jeanpierre103cbb52022-04-06 12:55:16 -0700278 trait_name: TraitName<'ir>,
Devin Jeanpierre9c34da62022-03-31 04:57:16 -0700279 record_name: Ident,
280 format_first_param_as_self: bool,
281 ) -> Self {
282 ImplKind::Trait {
283 trait_name,
284 record_name,
285 declare_lifetimes: false,
286 trait_generic_params: quote! {},
287 format_first_param_as_self,
288 }
289 }
290 fn new_generic_trait(
Devin Jeanpierre103cbb52022-04-06 12:55:16 -0700291 trait_name: TraitName<'ir>,
Devin Jeanpierre9c34da62022-03-31 04:57:16 -0700292 record_name: Ident,
293 format_first_param_as_self: bool,
294 ) -> Self {
295 ImplKind::Trait {
296 trait_name,
297 record_name,
298 declare_lifetimes: true,
299 trait_generic_params: quote! {},
300 format_first_param_as_self,
301 }
302 }
303 fn format_first_param_as_self(&self) -> bool {
304 matches!(
305 self,
306 Self::Trait { format_first_param_as_self: true, .. }
307 | Self::Struct { format_first_param_as_self: true, .. }
308 )
309 }
310 /// Returns whether the function is defined as `unsafe fn ...`.
311 fn is_unsafe(&self) -> bool {
312 matches!(self, Self::None { is_unsafe: true, .. } | Self::Struct { is_unsafe: true, .. })
313 }
314}
315
316/// Returns the shape of the generated Rust API for a given function definition.
Devin Jeanpierred7b48102022-03-31 04:15:03 -0700317///
Devin Jeanpierre9c34da62022-03-31 04:57:16 -0700318/// Returns:
Devin Jeanpierred7b48102022-03-31 04:15:03 -0700319///
320/// * `Err(_)`: something went wrong importing this function.
321/// * `Ok(None)`: the function imported as "nothing". (For example, a defaulted
322/// destructor might be mapped to no `Drop` impl at all.)
Devin Jeanpierre9c34da62022-03-31 04:57:16 -0700323/// * `Ok((func_name, impl_kind))`: The function name and ImplKind.
Devin Jeanpierre103cbb52022-04-06 12:55:16 -0700324fn api_func_shape<'ir>(
Devin Jeanpierre9c34da62022-03-31 04:57:16 -0700325 func: &Func,
326 ir: &IR,
Devin Jeanpierre103cbb52022-04-06 12:55:16 -0700327 param_type_kinds: &[RsTypeKind<'ir>],
328) -> Result<Option<(Ident, ImplKind<'ir>)>> {
Lukasz Anforowiczab65e292022-01-14 23:04:21 +0000329 let maybe_record: Option<&Record> =
Lukasz Anforowicz13cf7492021-12-22 15:29:52 +0000330 func.member_func_metadata.as_ref().map(|meta| meta.find_record(ir)).transpose()?;
Devin Jeanpierre9c34da62022-03-31 04:57:16 -0700331 let has_pointer_params =
332 param_type_kinds.iter().any(|p| matches!(p, RsTypeKind::Pointer { .. }));
333 let impl_kind: ImplKind;
Lukasz Anforowiczab65e292022-01-14 23:04:21 +0000334 let func_name: syn::Ident;
Googlerd03d05b2022-01-07 10:10:57 +0000335 match &func.name {
Lukasz Anforowicz9c663ca2022-02-09 01:33:31 +0000336 UnqualifiedIdentifier::Operator(op) if op.name == "==" => {
Lukasz Anforowiczeb19ac62022-02-05 00:10:16 +0000337 if param_type_kinds.len() != 2 {
338 bail!("Unexpected number of parameters in operator==: {:?}", func);
339 }
340 match (&param_type_kinds[0], &param_type_kinds[1]) {
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +0000341 (
Lukasz Anforowiczeb19ac62022-02-05 00:10:16 +0000342 RsTypeKind::Reference { referent: lhs, mutability: Mutability::Const, .. },
343 RsTypeKind::Reference { referent: rhs, mutability: Mutability::Const, .. },
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +0000344 ) => match **lhs {
Devin Jeanpierrefec5bfe2022-04-05 10:59:10 -0700345 RsTypeKind::Record { record: lhs_record, .. } => {
Lukasz Anforowicz4c3a2cc2022-03-11 00:24:49 +0000346 let lhs: Ident = make_rs_ident(&lhs_record.rs_name);
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +0000347 func_name = make_rs_ident("eq");
Lukasz Anforowicz5e623782022-03-14 16:52:23 +0000348 // Not using `ImplKind::new_generic_trait`, because #rhs
349 // should be stripped of references + because `&'a self`
350 // needs to have its lifetime declared next to `fn`, not
351 // next to `impl`.
Devin Jeanpierred9a6e6c2022-03-29 02:55:41 -0700352 impl_kind = ImplKind::new_trait(
353 TraitName::Other(quote! {PartialEq<#rhs>}),
354 lhs,
355 /* format_first_param_as_self= */ true,
356 );
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +0000357 }
Marcel Hlopko14ee3c82022-02-09 09:46:23 +0000358 _ => {
Devin Jeanpierred7b48102022-03-31 04:15:03 -0700359 bail!("operator== where lhs doesn't refer to a record",);
Marcel Hlopko14ee3c82022-02-09 09:46:23 +0000360 }
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +0000361 },
Marcel Hlopko14ee3c82022-02-09 09:46:23 +0000362 _ => {
Devin Jeanpierred7b48102022-03-31 04:15:03 -0700363 bail!("operator== where operands are not const references",);
Marcel Hlopko14ee3c82022-02-09 09:46:23 +0000364 }
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +0000365 };
366 }
Lukasz Anforowicz9c663ca2022-02-09 01:33:31 +0000367 UnqualifiedIdentifier::Operator(_) => {
Devin Jeanpierred7b48102022-03-31 04:15:03 -0700368 bail!("Bindings for this kind of operator are not supported");
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +0000369 }
Devin Jeanpierref2ec8712021-10-13 20:47:16 +0000370 UnqualifiedIdentifier::Identifier(id) => {
Marcel Hlopkoeaae9b72022-01-21 15:54:11 +0000371 func_name = make_rs_ident(&id.identifier);
Lukasz Anforowiczf9564622022-01-28 14:31:04 +0000372 match maybe_record {
373 None => {
Devin Jeanpierre7c3d8ed2022-03-29 03:02:04 -0700374 impl_kind = ImplKind::None { is_unsafe: has_pointer_params };
Lukasz Anforowiczf9564622022-01-28 14:31:04 +0000375 }
376 Some(record) => {
Devin Jeanpierred9a6e6c2022-03-29 02:55:41 -0700377 let format_first_param_as_self = if func.is_instance_method() {
Lukasz Anforowiczf9564622022-01-28 14:31:04 +0000378 let first_param = param_type_kinds.first().ok_or_else(|| {
379 anyhow!("Missing `__this` parameter in an instance method: {:?}", func)
380 })?;
Devin Jeanpierred9a6e6c2022-03-29 02:55:41 -0700381 first_param.is_ref_to(record)
Lukasz Anforowiczf9564622022-01-28 14:31:04 +0000382 } else {
Devin Jeanpierred9a6e6c2022-03-29 02:55:41 -0700383 false
384 };
Devin Jeanpierre6784e5e2022-03-29 02:59:01 -0700385 impl_kind = ImplKind::Struct {
386 record_name: make_rs_ident(&record.rs_name),
387 format_first_param_as_self,
Devin Jeanpierre7c3d8ed2022-03-29 03:02:04 -0700388 is_unsafe: has_pointer_params,
Devin Jeanpierre6784e5e2022-03-29 02:59:01 -0700389 };
Lukasz Anforowiczf9564622022-01-28 14:31:04 +0000390 }
391 };
Michael Forstered642022021-10-04 09:48:25 +0000392 }
Devin Jeanpierre91de7012021-10-21 12:53:51 +0000393 UnqualifiedIdentifier::Destructor => {
Lukasz Anforowiczab65e292022-01-14 23:04:21 +0000394 // Note: to avoid double-destruction of the fields, they are all wrapped in
395 // ManuallyDrop in this case. See `generate_record`.
396 let record =
397 maybe_record.ok_or_else(|| anyhow!("Destructors must be member functions."))?;
Lukasz Anforowicze57215c2022-01-12 14:54:16 +0000398 if !should_implement_drop(record) {
Devin Jeanpierred7b48102022-03-31 04:15:03 -0700399 return Ok(None);
Devin Jeanpierre91de7012021-10-21 12:53:51 +0000400 }
Devin Jeanpierred9a6e6c2022-03-29 02:55:41 -0700401 impl_kind = ImplKind::new_trait(
402 TraitName::Other(quote! {Drop}),
Devin Jeanpierre6784e5e2022-03-29 02:59:01 -0700403 make_rs_ident(&record.rs_name),
Devin Jeanpierred9a6e6c2022-03-29 02:55:41 -0700404 /* format_first_param_as_self= */ true,
405 );
Marcel Hlopkoeaae9b72022-01-21 15:54:11 +0000406 func_name = make_rs_ident("drop");
Devin Jeanpierre91de7012021-10-21 12:53:51 +0000407 }
Lukasz Anforowicz13cf7492021-12-22 15:29:52 +0000408 UnqualifiedIdentifier::Constructor => {
Lukasz Anforowicz71716b72022-01-26 17:05:05 +0000409 let member_func_metadata = func
410 .member_func_metadata
411 .as_ref()
412 .ok_or_else(|| anyhow!("Constructors must be member functions."))?;
413 let record = maybe_record
414 .ok_or_else(|| anyhow!("Constructors must be associated with a record."))?;
415 let instance_method_metadata =
416 member_func_metadata
417 .instance_method_metadata
418 .as_ref()
419 .ok_or_else(|| anyhow!("Constructors must be instance methods."))?;
Devin Jeanpierre7c3d8ed2022-03-29 03:02:04 -0700420 if has_pointer_params {
Lukasz Anforowicz55673c92022-01-27 19:37:26 +0000421 // TODO(b/216648347): Allow this outside of traits (e.g. after supporting
422 // translating C++ constructors into static methods in Rust).
Devin Jeanpierred7b48102022-03-31 04:15:03 -0700423 bail!(
Lukasz Anforowiczeb19ac62022-02-05 00:10:16 +0000424 "Unsafe constructors (e.g. with no elided or explicit lifetimes) \
425 are intentionally not supported",
426 );
Lukasz Anforowicz55673c92022-01-27 19:37:26 +0000427 }
428
Devin Jeanpierre6784e5e2022-03-29 02:59:01 -0700429 let record_name = make_rs_ident(&record.rs_name);
Devin Jeanpierre6f607372022-03-22 21:34:38 +0000430 if !record.is_unpin() {
431 func_name = make_rs_ident("ctor_new");
Devin Jeanpierre6f607372022-03-22 21:34:38 +0000432
433 match func.params.len() {
434 0 => bail!("Missing `__this` parameter in a constructor: {:?}", func),
435 2 => {
Lukasz Anforowicz5e623782022-03-14 16:52:23 +0000436 impl_kind = ImplKind::new_generic_trait(
Devin Jeanpierre103cbb52022-04-06 12:55:16 -0700437 TraitName::CtorNew(param_type_kinds[1].clone()),
Devin Jeanpierre1b8f4a12022-03-22 21:29:42 +0000438 record_name,
Devin Jeanpierred9a6e6c2022-03-29 02:55:41 -0700439 /* format_first_param_as_self= */ false,
Devin Jeanpierre1b8f4a12022-03-22 21:29:42 +0000440 );
Devin Jeanpierre6f607372022-03-22 21:34:38 +0000441 }
442 _ => {
443 // TODO(b/216648347): Support bindings for other constructors.
Devin Jeanpierred7b48102022-03-31 04:15:03 -0700444 bail!(
Devin Jeanpierre6f607372022-03-22 21:34:38 +0000445 "Only single-parameter constructors for T: !Unpin are supported for now",
Marcel Hlopko14ee3c82022-02-09 09:46:23 +0000446 );
Lukasz Anforowicz73326af2022-01-05 01:13:10 +0000447 }
448 }
Devin Jeanpierre6f607372022-03-22 21:34:38 +0000449 } else {
450 match func.params.len() {
451 0 => bail!("Missing `__this` parameter in a constructor: {:?}", func),
452 1 => {
453 impl_kind = ImplKind::new_trait(
454 TraitName::UnpinConstructor(quote! {Default}),
455 record_name,
Devin Jeanpierred9a6e6c2022-03-29 02:55:41 -0700456 /* format_first_param_as_self= */ false,
Devin Jeanpierre6f607372022-03-22 21:34:38 +0000457 );
458 func_name = make_rs_ident("default");
Devin Jeanpierre6f607372022-03-22 21:34:38 +0000459 }
460 2 => {
Devin Jeanpierre6f607372022-03-22 21:34:38 +0000461 if param_type_kinds[1].is_shared_ref_to(record) {
462 // Copy constructor
463 if should_derive_clone(record) {
Devin Jeanpierred7b48102022-03-31 04:15:03 -0700464 return Ok(None);
Devin Jeanpierre6f607372022-03-22 21:34:38 +0000465 } else {
466 impl_kind = ImplKind::new_trait(
467 TraitName::UnpinConstructor(quote! {Clone}),
468 record_name,
Devin Jeanpierred9a6e6c2022-03-29 02:55:41 -0700469 /* format_first_param_as_self= */ true,
Devin Jeanpierre6f607372022-03-22 21:34:38 +0000470 );
471 func_name = make_rs_ident("clone");
Devin Jeanpierre6f607372022-03-22 21:34:38 +0000472 }
473 } else if !instance_method_metadata.is_explicit_ctor {
Devin Jeanpierre92ca2612022-04-06 11:35:13 -0700474 let param_type = &param_type_kinds[1];
Devin Jeanpierre6f607372022-03-22 21:34:38 +0000475 impl_kind = ImplKind::new_generic_trait(
476 TraitName::UnpinConstructor(quote! {From< #param_type >}),
477 record_name,
Devin Jeanpierred9a6e6c2022-03-29 02:55:41 -0700478 /* format_first_param_as_self= */ false,
Devin Jeanpierre6f607372022-03-22 21:34:38 +0000479 );
480 func_name = make_rs_ident("from");
Devin Jeanpierre6f607372022-03-22 21:34:38 +0000481 } else {
Devin Jeanpierred7b48102022-03-31 04:15:03 -0700482 bail!("Not yet supported type of constructor parameter",);
Devin Jeanpierre6f607372022-03-22 21:34:38 +0000483 }
484 }
485 _ => {
486 // TODO(b/216648347): Support bindings for other constructors.
Devin Jeanpierred7b48102022-03-31 04:15:03 -0700487 bail!("More than 1 constructor parameter is not supported yet",);
Devin Jeanpierre6f607372022-03-22 21:34:38 +0000488 }
Lukasz Anforowicz73326af2022-01-05 01:13:10 +0000489 }
Lukasz Anforowiczab65e292022-01-14 23:04:21 +0000490 }
491 }
492 }
Devin Jeanpierre9c34da62022-03-31 04:57:16 -0700493 Ok(Some((func_name, impl_kind)))
494}
495
496/// Generates Rust source code for a given `Func`.
497///
498/// Returns:
499///
500/// * `Err(_)`: couldn't import the function, emit an `UnsupportedItem`.
501/// * `Ok(None)`: the function imported as "nothing". (For example, a defaulted
502/// destructor might be mapped to no `Drop` impl at all.)
503/// * `Ok((rs_api, rs_thunk, function_id))`: The Rust function definition,
504/// thunk FFI definition, and function ID.
505fn generate_func(func: &Func, ir: &IR) -> Result<Option<(RsSnippet, RsSnippet, FunctionId)>> {
Devin Jeanpierre9c34da62022-03-31 04:57:16 -0700506 let param_type_kinds = func
507 .params
508 .iter()
509 .map(|p| {
510 RsTypeKind::new(&p.type_.rs_type, ir).with_context(|| {
511 format!("Failed to process type of parameter {:?} on {:?}", p, func)
512 })
513 })
514 .collect::<Result<Vec<_>>>()?;
515
516 let (func_name, mut impl_kind) =
Devin Jeanpierre9886fb42022-04-01 04:31:20 -0700517 if let Some(values) = api_func_shape(func, ir, &param_type_kinds)? {
Devin Jeanpierre9c34da62022-03-31 04:57:16 -0700518 values
519 } else {
520 return Ok(None);
521 };
Lukasz Anforowiczab65e292022-01-14 23:04:21 +0000522
Devin Jeanpierred9cecff2022-03-29 02:53:58 -0700523 let return_type_fragment = RsTypeKind::new(&func.return_type.rs_type, ir)
Devin Jeanpierre82e8e362022-04-05 11:04:55 -0700524 .map(|t| t.format_as_return_type_fragment())
Devin Jeanpierred9cecff2022-03-29 02:53:58 -0700525 .with_context(|| format!("Failed to format return type for {:?}", func))?;
526 let param_idents =
527 func.params.iter().map(|p| make_rs_ident(&p.identifier.identifier)).collect_vec();
Devin Jeanpierre92ca2612022-04-06 11:35:13 -0700528 let param_types: Vec<_> = param_type_kinds.iter().map(|t| t.to_token_stream()).collect();
Devin Jeanpierred9cecff2022-03-29 02:53:58 -0700529 let thunk_ident = thunk_ident(func);
530
Lukasz Anforowiczab65e292022-01-14 23:04:21 +0000531 let api_func_def = {
Lukasz Anforowicz95551272022-01-20 00:02:24 +0000532 // Clone params, return type, etc - we may need to mutate them in the
533 // API func, but we want to retain the originals for the thunk.
Lukasz Anforowiczab65e292022-01-14 23:04:21 +0000534 let mut return_type_fragment = return_type_fragment.clone();
535 let mut thunk_args = param_idents.iter().map(|id| quote! { #id}).collect_vec();
536 let mut api_params = param_idents
537 .iter()
538 .zip(param_types.iter())
539 .map(|(ident, type_)| quote! { #ident : #type_ })
540 .collect_vec();
Lukasz Anforowicz95551272022-01-20 00:02:24 +0000541 let mut lifetimes = func.lifetime_params.iter().collect_vec();
Lukasz Anforowiczf7bdd392022-01-21 00:33:39 +0000542 let mut maybe_first_api_param = param_type_kinds.get(0);
Lukasz Anforowiczab65e292022-01-14 23:04:21 +0000543
Devin Jeanpierre6f607372022-03-22 21:34:38 +0000544 if let ImplKind::Trait {
545 trait_name: TraitName::UnpinConstructor(..) | TraitName::CtorNew(..),
546 ..
547 } = impl_kind
548 {
Lukasz Anforowiczab65e292022-01-14 23:04:21 +0000549 return_type_fragment = quote! { -> Self };
550
Lukasz Anforowiczf9564622022-01-28 14:31:04 +0000551 // Drop `__this` parameter from the public Rust API. Presence of
552 // element #0 is indirectly verified by a `Constructor`-related
553 // `match` branch a little bit above.
554 api_params.remove(0);
555 thunk_args.remove(0);
Lukasz Anforowicz95551272022-01-20 00:02:24 +0000556
Lukasz Anforowicz326c4e42022-01-27 14:43:00 +0000557 // Remove the lifetime associated with `__this`.
Devin Jeanpierre1b8f4a12022-03-22 21:29:42 +0000558 ensure!(
559 func.return_type.rs_type.is_unit_type(),
560 "Unexpectedly non-void return type of a constructor: {:?}",
561 func
562 );
Lukasz Anforowiczf7bdd392022-01-21 00:33:39 +0000563 let maybe_first_lifetime = func.params[0].type_.rs_type.lifetime_args.first();
Lukasz Anforowicz55673c92022-01-27 19:37:26 +0000564 let no_longer_needed_lifetime_id = maybe_first_lifetime
565 .ok_or_else(|| anyhow!("Missing lifetime on `__this` parameter: {:?}", func))?;
566 lifetimes.retain(|l| l.id != *no_longer_needed_lifetime_id);
Lukasz Anforowicz55673c92022-01-27 19:37:26 +0000567 if let Some(type_still_dependent_on_removed_lifetime) = param_type_kinds
568 .iter()
569 .skip(1) // Skipping `__this`
Lukasz Anforowicz90bdb962022-02-14 21:07:45 +0000570 .flat_map(|t| t.lifetimes())
571 .find(|lifetime_id| *lifetime_id == *no_longer_needed_lifetime_id)
Lukasz Anforowicz55673c92022-01-27 19:37:26 +0000572 {
573 bail!(
574 "The lifetime of `__this` is unexpectedly also used by another \
575 parameter {:?} in function {:?}",
576 type_still_dependent_on_removed_lifetime,
577 func.name
578 );
Lukasz Anforowicz95551272022-01-20 00:02:24 +0000579 }
580
581 // Rebind `maybe_first_api_param` to the next param after `__this`.
Lukasz Anforowiczf7bdd392022-01-21 00:33:39 +0000582 maybe_first_api_param = param_type_kinds.get(1);
Lukasz Anforowiczab65e292022-01-14 23:04:21 +0000583 }
584
Devin Jeanpierre6f607372022-03-22 21:34:38 +0000585 if let ImplKind::Trait { trait_name: TraitName::CtorNew(..), .. } = impl_kind {
586 return_type_fragment = quote! { -> Self::CtorType };
587 }
588
Lukasz Anforowiczab65e292022-01-14 23:04:21 +0000589 // Change `__this: &'a SomeStruct` into `&'a self` if needed.
Devin Jeanpierred9a6e6c2022-03-29 02:55:41 -0700590 if impl_kind.format_first_param_as_self() {
Lukasz Anforowiczab65e292022-01-14 23:04:21 +0000591 let first_api_param = maybe_first_api_param
592 .ok_or_else(|| anyhow!("No parameter to format as 'self': {:?}", func))?;
Devin Jeanpierrefec5bfe2022-04-05 10:59:10 -0700593 let self_decl = first_api_param.format_as_self_param(func, ir).with_context(|| {
594 format!("Failed to format as `self` param: {:?}", first_api_param)
595 })?;
Lukasz Anforowiczf9564622022-01-28 14:31:04 +0000596 // Presence of element #0 is verified by `ok_or_else` on
597 // `maybe_first_api_param` above.
598 api_params[0] = self_decl;
599 thunk_args[0] = quote! { self };
Lukasz Anforowiczab65e292022-01-14 23:04:21 +0000600 }
601
Devin Jeanpierre6f607372022-03-22 21:34:38 +0000602 // TODO(b/200067242): the Pin-wrapping code doesn't know to wrap &mut
603 // MaybeUninit<T> in Pin if T is !Unpin. It should understand
604 // 'structural pinning', so that we do not need into_inner_unchecked()
605 // here.
Devin Jeanpierre7bddfdb2022-03-14 11:04:40 +0000606 let func_body = match &impl_kind {
Devin Jeanpierre6f607372022-03-22 21:34:38 +0000607 ImplKind::Trait { trait_name: TraitName::CtorNew(..), .. } => {
Devin Jeanpierredeea7892022-03-29 02:13:31 -0700608 // TODO(b/226447239): check for copy here and instead use copies in that case?
Devin Jeanpierre6f607372022-03-22 21:34:38 +0000609 quote! {
Rosica Dejanovskaaccf00b2022-04-01 13:28:04 -0700610 ctor::FnCtor::new(move |dest: rust_std::pin::Pin<&mut rust_std::mem::MaybeUninit<Self>>| {
Devin Jeanpierre6f607372022-03-22 21:34:38 +0000611 unsafe {
Rosica Dejanovskaaccf00b2022-04-01 13:28:04 -0700612 crate::detail::#thunk_ident(rust_std::pin::Pin::into_inner_unchecked(dest) #( , #thunk_args )*);
Devin Jeanpierre6f607372022-03-22 21:34:38 +0000613 }
614 })
615 }
616 }
Devin Jeanpierre1b8f4a12022-03-22 21:29:42 +0000617 ImplKind::Trait { trait_name: TraitName::UnpinConstructor(..), .. } => {
Lukasz Anforowiczab65e292022-01-14 23:04:21 +0000618 // SAFETY: A user-defined constructor is not guaranteed to
619 // initialize all the fields. To make the `assume_init()` call
620 // below safe, the memory is zero-initialized first. This is a
621 // bit safer, because zero-initialized memory represents a valid
622 // value for the currently supported field types (this may
623 // change once the bindings generator starts supporting
624 // reference fields). TODO(b/213243309): Double-check if
625 // zero-initialization is desirable here.
626 quote! {
Rosica Dejanovskaaccf00b2022-04-01 13:28:04 -0700627 let mut tmp = rust_std::mem::MaybeUninit::<Self>::zeroed();
Lukasz Anforowiczab65e292022-01-14 23:04:21 +0000628 unsafe {
629 crate::detail::#thunk_ident( &mut tmp #( , #thunk_args )* );
630 tmp.assume_init()
Lukasz Anforowicz13cf7492021-12-22 15:29:52 +0000631 }
Lukasz Anforowicz13cf7492021-12-22 15:29:52 +0000632 }
Lukasz Anforowiczab65e292022-01-14 23:04:21 +0000633 }
Devin Jeanpierre7bddfdb2022-03-14 11:04:40 +0000634 _ => {
635 let mut body = quote! { crate::detail::#thunk_ident( #( #thunk_args ),* ) };
636 // Only need to wrap everything in an `unsafe { ... }` block if
637 // the *whole* api function is safe.
Devin Jeanpierre7c3d8ed2022-03-29 03:02:04 -0700638 if !impl_kind.is_unsafe() {
Devin Jeanpierre7bddfdb2022-03-14 11:04:40 +0000639 body = quote! { unsafe { #body } };
640 }
641 body
642 }
Lukasz Anforowiczab65e292022-01-14 23:04:21 +0000643 };
644
Devin Jeanpierre7c3d8ed2022-03-29 03:02:04 -0700645 let pub_ = match impl_kind {
646 ImplKind::None { .. } | ImplKind::Struct { .. } => quote! { pub },
647 ImplKind::Trait { .. } => quote! {},
648 };
649 let unsafe_ = if impl_kind.is_unsafe() {
650 quote! { unsafe }
651 } else {
652 quote! {}
Lukasz Anforowiczab65e292022-01-14 23:04:21 +0000653 };
654
Lukasz Anforowicz95551272022-01-20 00:02:24 +0000655 let lifetimes = lifetimes.into_iter().map(|l| format_lifetime_name(&l.name));
Lukasz Anforowicz5e623782022-03-14 16:52:23 +0000656 let fn_generic_params: TokenStream;
Devin Jeanpierre1b8f4a12022-03-22 21:29:42 +0000657 if let ImplKind::Trait { declare_lifetimes: true, trait_generic_params, .. } =
658 &mut impl_kind
659 {
Lukasz Anforowicz5e623782022-03-14 16:52:23 +0000660 *trait_generic_params = format_generic_params(lifetimes);
Devin Jeanpierre1b8f4a12022-03-22 21:29:42 +0000661 fn_generic_params = quote! {}
Lukasz Anforowicz5e623782022-03-14 16:52:23 +0000662 } else {
663 fn_generic_params = format_generic_params(lifetimes);
664 }
Lukasz Anforowicz95551272022-01-20 00:02:24 +0000665
Lukasz Anforowiczab65e292022-01-14 23:04:21 +0000666 quote! {
667 #[inline(always)]
Lukasz Anforowicz5e623782022-03-14 16:52:23 +0000668 #pub_ #unsafe_ fn #func_name #fn_generic_params(
669 #( #api_params ),* ) #return_type_fragment {
Lukasz Anforowiczab65e292022-01-14 23:04:21 +0000670 #func_body
671 }
Lukasz Anforowicz13cf7492021-12-22 15:29:52 +0000672 }
Michael Forstered642022021-10-04 09:48:25 +0000673 };
674
Devin Jeanpierred9cecff2022-03-29 02:53:58 -0700675 let doc_comment = generate_doc_comment(&func.doc_comment);
676 let mut features = BTreeSet::new();
Lukasz Anforowiczab65e292022-01-14 23:04:21 +0000677 let api_func: TokenStream;
678 let function_id: FunctionId;
Lukasz Anforowiczab65e292022-01-14 23:04:21 +0000679 match impl_kind {
Devin Jeanpierre7c3d8ed2022-03-29 03:02:04 -0700680 ImplKind::None { .. } => {
Lukasz Anforowiczab65e292022-01-14 23:04:21 +0000681 api_func = quote! { #doc_comment #api_func_def };
682 function_id = FunctionId { self_type: None, function_path: func_name.into() };
683 }
Devin Jeanpierre6784e5e2022-03-29 02:59:01 -0700684 ImplKind::Struct { record_name, .. } => {
Lukasz Anforowiczab65e292022-01-14 23:04:21 +0000685 api_func = quote! { impl #record_name { #doc_comment #api_func_def } };
686 function_id = FunctionId {
687 self_type: None,
688 function_path: syn::parse2(quote! { #record_name :: #func_name })?,
689 };
690 }
Lukasz Anforowicz5e623782022-03-14 16:52:23 +0000691 ImplKind::Trait { trait_name, record_name, trait_generic_params, .. } => {
Devin Jeanpierre6f607372022-03-22 21:34:38 +0000692 let extra_items;
693 match trait_name {
694 TraitName::CtorNew(..) => {
695 // This feature seems destined for stabilization, and makes the code
696 // simpler.
697 features.insert(make_rs_ident("type_alias_impl_trait"));
698 extra_items = quote! {type CtorType = impl ctor::Ctor<Output = Self>;};
699 }
700 _ => {
701 extra_items = quote! {};
702 }
703 };
Lukasz Anforowicz5e623782022-03-14 16:52:23 +0000704 api_func = quote! {
705 #doc_comment
706 impl #trait_generic_params #trait_name for #record_name {
Devin Jeanpierre6f607372022-03-22 21:34:38 +0000707 #extra_items
Lukasz Anforowicz5e623782022-03-14 16:52:23 +0000708 #api_func_def
709 }
710 };
Lukasz Anforowiczab65e292022-01-14 23:04:21 +0000711 function_id = FunctionId {
712 self_type: Some(record_name.into()),
713 function_path: syn::parse2(quote! { #trait_name :: #func_name })?,
714 };
715 }
716 }
717
Lukasz Anforowicz6d553632022-01-06 21:36:14 +0000718 let thunk = {
Devin Jeanpierre7e9a1de2021-12-03 08:04:22 +0000719 let thunk_attr = if can_skip_cc_thunk(func) {
Devin Jeanpierred9cecff2022-03-29 02:53:58 -0700720 let mangled_name = &func.mangled_name;
Devin Jeanpierre7e9a1de2021-12-03 08:04:22 +0000721 quote! {#[link_name = #mangled_name]}
722 } else {
723 quote! {}
724 };
725
Lukasz Anforowicz40c2eb82022-01-11 18:22:31 +0000726 // For constructors inject MaybeUninit into the type of `__this_` parameter.
727 let mut param_types = param_types;
728 if func.name == UnqualifiedIdentifier::Constructor {
729 if param_types.is_empty() || func.params.is_empty() {
730 bail!("Constructors should have at least one parameter (__this)");
731 }
Devin Jeanpierrefec5bfe2022-04-05 10:59:10 -0700732 param_types[0] =
Devin Jeanpierre82e8e362022-04-05 11:04:55 -0700733 param_type_kinds[0].format_mut_ref_as_uninitialized().with_context(|| {
Devin Jeanpierre1b8f4a12022-03-22 21:29:42 +0000734 format!(
735 "Failed to format `__this` param for a constructor thunk: {:?}",
736 func.params[0]
737 )
Devin Jeanpierre149950d2022-02-22 21:02:02 +0000738 })?;
739 } else if func.name == UnqualifiedIdentifier::Destructor {
740 if param_types.is_empty() || func.params.is_empty() {
741 bail!("Destructors should have at least one parameter (__this)");
742 }
Devin Jeanpierre82e8e362022-04-05 11:04:55 -0700743 param_types[0] = param_type_kinds[0].format_ref_as_raw_ptr().with_context(|| {
Devin Jeanpierrefec5bfe2022-04-05 10:59:10 -0700744 format!(
745 "Failed to format `__this` param for a destructor thunk: {:?}",
746 func.params[0]
747 )
748 })?;
Lukasz Anforowicz40c2eb82022-01-11 18:22:31 +0000749 }
750
Lukasz Anforowicz95551272022-01-20 00:02:24 +0000751 let lifetimes = func.lifetime_params.iter().map(|l| format_lifetime_name(&l.name));
752 let generic_params = format_generic_params(lifetimes);
753
Devin Jeanpierre7e9a1de2021-12-03 08:04:22 +0000754 quote! {
755 #thunk_attr
Lukasz Anforowicz4ad012b2021-12-15 18:13:40 +0000756 pub(crate) fn #thunk_ident #generic_params( #( #param_idents: #param_types ),*
757 ) #return_type_fragment ;
Devin Jeanpierre7e9a1de2021-12-03 08:04:22 +0000758 }
Michael Forstered642022021-10-04 09:48:25 +0000759 };
760
Devin Jeanpierred7b48102022-03-31 04:15:03 -0700761 Ok(Some((RsSnippet { features, tokens: api_func }, thunk.into(), function_id)))
Michael Forstered642022021-10-04 09:48:25 +0000762}
763
Michael Forstercc5941a2021-10-07 07:12:24 +0000764fn generate_doc_comment(comment: &Option<String>) -> TokenStream {
765 match comment {
Michael Forster028800b2021-10-05 12:39:59 +0000766 Some(text) => {
Marcel Hlopko89547752021-12-10 09:39:41 +0000767 // token_stream_printer (and rustfmt) don't put a space between /// and the doc
768 // comment, let's add it here so our comments are pretty.
Lukasz Anforowiczdd9ae0f2022-02-17 15:52:53 +0000769 let doc = format!(" {}", text.replace('\n', "\n "));
Michael Forster028800b2021-10-05 12:39:59 +0000770 quote! {#[doc=#doc]}
771 }
772 None => quote! {},
Michael Forstercc5941a2021-10-07 07:12:24 +0000773 }
774}
Lukasz Anforowiczdaff0402021-12-23 00:37:50 +0000775
Devin Jeanpierre92ca2612022-04-06 11:35:13 -0700776fn format_generic_params<T: ToTokens>(params: impl IntoIterator<Item = T>) -> TokenStream {
Lukasz Anforowiczdaff0402021-12-23 00:37:50 +0000777 let mut params = params.into_iter().peekable();
778 if params.peek().is_none() {
779 quote! {}
780 } else {
781 quote! { < #( #params ),* > }
782 }
783}
784
Lukasz Anforowicze57215c2022-01-12 14:54:16 +0000785fn should_implement_drop(record: &Record) -> bool {
786 match record.destructor.definition {
787 // TODO(b/202258760): Only omit destructor if `Copy` is specified.
788 SpecialMemberDefinition::Trivial => false,
789
790 // TODO(b/212690698): Avoid calling into the C++ destructor (e.g. let
791 // Rust drive `drop`-ing) to avoid (somewhat unergonomic) ManuallyDrop
792 // if we can ask Rust to preserve C++ field destruction order in
793 // NontrivialMembers case.
794 SpecialMemberDefinition::NontrivialMembers => true,
795
796 // The `impl Drop` for NontrivialUserDefined needs to call into the
797 // user-defined destructor on C++ side.
798 SpecialMemberDefinition::NontrivialUserDefined => true,
799
800 // TODO(b/213516512): Today the IR doesn't contain Func entries for
801 // deleted functions/destructors/etc. But, maybe we should generate
802 // `impl Drop` in this case? With `unreachable!`? With
803 // `std::mem::forget`?
804 SpecialMemberDefinition::Deleted => false,
805 }
806}
807
808/// Returns whether fields of type `ty` need to be wrapped in `ManuallyDrop<T>`
809/// to prevent the fields from being destructed twice (once by the C++
810/// destructor calkled from the `impl Drop` of the struct and once by `drop` on
811/// the Rust side).
812///
813/// A type is safe to destroy twice if it implements `Copy`. Fields of such
814/// don't need to be wrapped in `ManuallyDrop<T>` even if the struct
815/// containing the fields provides an `impl Drop` that calles into a C++
816/// destructor (in addition to dropping the fields on the Rust side).
817///
818/// Note that it is not enough to just be `!needs_drop<T>()`: Rust only
819/// guarantees that it is safe to use-after-destroy for `Copy` types. See
820/// e.g. the documentation for
821/// [`drop_in_place`](https://doc.rust-lang.org/std/ptr/fn.drop_in_place.html):
822///
823/// > if `T` is not `Copy`, using the pointed-to value after calling
824/// > `drop_in_place` can cause undefined behavior
Teddy Katzd2cd1422022-04-04 09:41:33 -0700825///
Devin Jeanpierrefec5bfe2022-04-05 10:59:10 -0700826/// For non-Copy union fields, failing to use `ManuallyDrop<T>` would
827/// additionally cause a compile-time error until https://github.com/rust-lang/rust/issues/55149 is stabilized.
Lukasz Anforowicze57215c2022-01-12 14:54:16 +0000828fn needs_manually_drop(ty: &ir::RsType, ir: &IR) -> Result<bool> {
829 let ty_implements_copy = RsTypeKind::new(ty, ir)?.implements_copy();
830 Ok(!ty_implements_copy)
831}
832
Michael Forsterbee84482021-10-13 08:35:38 +0000833/// Generates Rust source code for a given `Record` and associated assertions as
834/// a tuple.
Marcel Hlopkoc0956cf2021-11-29 08:31:28 +0000835fn generate_record(record: &Record, ir: &IR) -> Result<(RsSnippet, RsSnippet)> {
Lukasz Anforowicz4c3a2cc2022-03-11 00:24:49 +0000836 let ident = make_rs_ident(&record.rs_name);
Michael Forstercc5941a2021-10-07 07:12:24 +0000837 let doc_comment = generate_doc_comment(&record.doc_comment);
Marcel Hlopkob4b28742021-09-15 12:45:20 +0000838 let field_idents =
Marcel Hlopkoeaae9b72022-01-21 15:54:11 +0000839 record.fields.iter().map(|f| make_rs_ident(&f.identifier.identifier)).collect_vec();
Michael Forstercc5941a2021-10-07 07:12:24 +0000840 let field_doc_coments =
841 record.fields.iter().map(|f| generate_doc_comment(&f.doc_comment)).collect_vec();
Lukasz Anforowiczfed64e62022-03-22 22:39:04 +0000842
843 let mut field_copy_trait_assertions: Vec<TokenStream> = vec![];
Devin Jeanpierre09c6f452021-09-29 07:34:24 +0000844 let field_types = record
845 .fields
846 .iter()
Devin Jeanpierreb69bcae2022-02-03 09:45:50 +0000847 .enumerate()
848 .map(|(i, f)| {
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +0000849 // [[no_unique_address]] fields are replaced by an unaligned block of memory
850 // which fills space up to the next field.
Devin Jeanpierreb69bcae2022-02-03 09:45:50 +0000851 // See: docs/struct_layout
852 if f.is_no_unique_address {
853 let next_offset = if let Some(next) = record.fields.get(i + 1) {
854 next.offset
855 } else {
856 record.size * 8
857 };
858 let width = Literal::usize_unsuffixed((next_offset - f.offset) / 8);
Rosica Dejanovskaaccf00b2022-04-01 13:28:04 -0700859 return Ok(quote! {[rust_std::mem::MaybeUninit<u8>; #width]});
Devin Jeanpierreb69bcae2022-02-03 09:45:50 +0000860 }
Devin Jeanpierrefec5bfe2022-04-05 10:59:10 -0700861 let mut formatted = format_rs_type(&f.type_.rs_type, ir).with_context(|| {
862 format!("Failed to format type for field {:?} on record {:?}", f, record)
863 })?;
Lukasz Anforowicze57215c2022-01-12 14:54:16 +0000864 // TODO(b/212696226): Verify cases where ManuallyDrop<T> is skipped
865 // via static asserts in the generated code.
Teddy Katzd2cd1422022-04-04 09:41:33 -0700866 if should_implement_drop(record) || record.is_union {
Lukasz Anforowiczfed64e62022-03-22 22:39:04 +0000867 if needs_manually_drop(&f.type_.rs_type, ir)? {
868 // TODO(b/212690698): Avoid (somewhat unergonomic) ManuallyDrop
869 // if we can ask Rust to preserve field destruction order if the
870 // destructor is the SpecialMemberDefinition::NontrivialMembers
871 // case.
Rosica Dejanovskaaccf00b2022-04-01 13:28:04 -0700872 formatted = quote! { rust_std::mem::ManuallyDrop<#formatted> }
Lukasz Anforowiczfed64e62022-03-22 22:39:04 +0000873 } else {
874 field_copy_trait_assertions.push(quote! {
875 const _: () = { assert_impl_all!(#formatted: Copy); };
876 });
877 }
Lukasz Anforowicz6d553632022-01-06 21:36:14 +0000878 };
Devin Jeanpierre7e9a1de2021-12-03 08:04:22 +0000879 Ok(formatted)
880 })
Devin Jeanpierre09c6f452021-09-29 07:34:24 +0000881 .collect::<Result<Vec<_>>>()?;
Lukasz Anforowiczfed64e62022-03-22 22:39:04 +0000882
Googlerec589eb2021-09-17 07:45:39 +0000883 let field_accesses = record
884 .fields
885 .iter()
886 .map(|f| {
Devin Jeanpierreb69bcae2022-02-03 09:45:50 +0000887 if f.access == AccessSpecifier::Public && !f.is_no_unique_address {
Googlerec589eb2021-09-17 07:45:39 +0000888 quote! { pub }
889 } else {
890 quote! {}
891 }
892 })
893 .collect_vec();
Googlerec648ff2021-09-23 07:19:53 +0000894 let size = record.size;
895 let alignment = record.alignment;
Lukasz Anforowiczfed64e62022-03-22 22:39:04 +0000896 let field_offset_assertions =
Googleraaa0a532021-10-01 09:11:27 +0000897 record.fields.iter().zip(field_idents.iter()).map(|(field, field_ident)| {
898 let offset = field.offset;
899 quote! {
900 // The IR contains the offset in bits, while offset_of!()
901 // returns the offset in bytes, so we need to convert.
Googler209b10a2021-12-06 09:11:57 +0000902 const _: () = assert!(offset_of!(#ident, #field_ident) * 8 == #offset);
Googleraaa0a532021-10-01 09:11:27 +0000903 }
904 });
Lukasz Anforowiczfed64e62022-03-22 22:39:04 +0000905 // TODO(b/212696226): Generate `assert_impl_all!` or `assert_not_impl_all!`
906 // assertions about the `Copy` trait - this trait should be implemented
907 // iff `should_implement_drop(record)` is false.
Michael Forsterdb8101a2021-10-08 06:56:03 +0000908 let mut record_features = BTreeSet::new();
909 let mut assertion_features = BTreeSet::new();
Devin Jeanpierre273eeae2021-10-06 13:29:35 +0000910
911 // TODO(mboehme): For the time being, we're using unstable features to
912 // be able to use offset_of!() in static assertions. This is fine for a
913 // prototype, but longer-term we want to either get those features
914 // stabilized or find an alternative. For more details, see
915 // b/200120034#comment15
Marcel Hlopkoeaae9b72022-01-21 15:54:11 +0000916 assertion_features.insert(make_rs_ident("const_ptr_offset_from"));
Devin Jeanpierre2ed14ec2021-10-06 11:32:19 +0000917
Lukasz Anforowicze57215c2022-01-12 14:54:16 +0000918 let derives = generate_derives(record);
Devin Jeanpierre9227d2c2021-10-06 12:26:05 +0000919 let derives = if derives.is_empty() {
920 quote! {}
921 } else {
922 quote! {#[derive( #(#derives),* )]}
923 };
Teddy Katzd2cd1422022-04-04 09:41:33 -0700924 let record_kind = if record.is_union {
925 quote! { union }
926 } else {
927 quote! { struct }
928 };
Lukasz Anforowiczdd9ae0f2022-02-17 15:52:53 +0000929 let unpin_impl = if record.is_unpin() {
930 quote! {}
Devin Jeanpierreea700d32021-10-06 11:33:56 +0000931 } else {
Michael Forsterbee84482021-10-13 08:35:38 +0000932 // negative_impls are necessary for universal initialization due to Rust's
933 // coherence rules: PhantomPinned isn't enough to prove to Rust that a
934 // blanket impl that requires Unpin doesn't apply. See http://<internal link>=h.f6jp8ifzgt3n
Marcel Hlopkoeaae9b72022-01-21 15:54:11 +0000935 record_features.insert(make_rs_ident("negative_impls"));
Lukasz Anforowiczdd9ae0f2022-02-17 15:52:53 +0000936 quote! {
Michael Forsterdb8101a2021-10-08 06:56:03 +0000937 __NEWLINE__ __NEWLINE__
938 impl !Unpin for #ident {}
Lukasz Anforowiczdd9ae0f2022-02-17 15:52:53 +0000939 }
940 };
Devin Jeanpierre273eeae2021-10-06 13:29:35 +0000941
Devin Jeanpierrec80e6242022-02-03 01:56:40 +0000942 let mut repr_attributes = vec![quote! {C}];
943 if record.override_alignment && record.alignment > 1 {
944 let alignment = Literal::usize_unsuffixed(record.alignment);
945 repr_attributes.push(quote! {align(#alignment)});
946 }
947
948 // Adjust the struct to also include base class subobjects. We use an opaque
949 // field because subobjects can live in the alignment of base class
950 // subobjects.
951 let base_subobjects_field = if let Some(base_size) = record.base_size {
952 let n = proc_macro2::Literal::usize_unsuffixed(base_size);
953 quote! {
Rosica Dejanovskaaccf00b2022-04-01 13:28:04 -0700954 __base_class_subobjects: [rust_std::mem::MaybeUninit<u8>; #n],
Devin Jeanpierrec80e6242022-02-03 01:56:40 +0000955 }
956 } else {
957 quote! {}
958 };
959
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +0000960 let empty_struct_placeholder_field =
961 if record.fields.is_empty() && record.base_size.unwrap_or(0) == 0 {
962 quote! {
963 /// Prevent empty C++ struct being zero-size in Rust.
Rosica Dejanovskaaccf00b2022-04-01 13:28:04 -0700964 placeholder: rust_std::mem::MaybeUninit<u8>,
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +0000965 }
966 } else {
967 quote! {}
968 };
Googlerf4792062021-10-20 07:21:21 +0000969
Devin Jeanpierre58181ac2022-02-14 21:30:05 +0000970 let no_unique_address_accessors = cc_struct_no_unique_address_impl(record, ir)?;
Devin Jeanpierre56777022022-02-03 01:57:15 +0000971 let base_class_into = cc_struct_upcast_impl(record, ir)?;
972
Michael Forsterdb8101a2021-10-08 06:56:03 +0000973 let record_tokens = quote! {
Michael Forster028800b2021-10-05 12:39:59 +0000974 #doc_comment
Devin Jeanpierre9227d2c2021-10-06 12:26:05 +0000975 #derives
Devin Jeanpierrec80e6242022-02-03 01:56:40 +0000976 #[repr(#( #repr_attributes ),*)]
Teddy Katzd2cd1422022-04-04 09:41:33 -0700977 pub #record_kind #ident {
Devin Jeanpierrec80e6242022-02-03 01:56:40 +0000978 #base_subobjects_field
Michael Forstercc5941a2021-10-07 07:12:24 +0000979 #( #field_doc_coments #field_accesses #field_idents: #field_types, )*
Googlerf4792062021-10-20 07:21:21 +0000980 #empty_struct_placeholder_field
Marcel Hlopkob4b28742021-09-15 12:45:20 +0000981 }
Googlerec648ff2021-09-23 07:19:53 +0000982
Devin Jeanpierre58181ac2022-02-14 21:30:05 +0000983 #no_unique_address_accessors
984
Devin Jeanpierre56777022022-02-03 01:57:15 +0000985 #base_class_into
986
Devin Jeanpierreea700d32021-10-06 11:33:56 +0000987 #unpin_impl
Devin Jeanpierre273eeae2021-10-06 13:29:35 +0000988 };
989
Lukasz Anforowicz95938f82022-03-24 13:51:54 +0000990 let record_trait_assertions = {
Devin Jeanpierre92ca2612022-04-06 11:35:13 -0700991 let record_type_name = RsTypeKind::new_record(record, ir).to_token_stream();
Lukasz Anforowicz95938f82022-03-24 13:51:54 +0000992 let mut assertions: Vec<TokenStream> = vec![];
993 let mut add_assertion = |assert_impl_macro: TokenStream, trait_name: TokenStream| {
994 assertions.push(quote! {
995 const _: () = { #assert_impl_macro (#record_type_name: #trait_name); };
996 });
997 };
998 if should_derive_clone(record) {
999 add_assertion(quote! { assert_impl_all! }, quote! { Clone });
1000 } else {
1001 // Can't `assert_not_impl_all!` here, because `Clone` may be
1002 // implemented rather than derived.
1003 }
1004 let mut add_conditional_assertion = |should_impl_trait: bool, trait_name: TokenStream| {
1005 let assert_impl_macro = if should_impl_trait {
1006 quote! { assert_impl_all! }
1007 } else {
1008 quote! { assert_not_impl_all! }
1009 };
1010 add_assertion(assert_impl_macro, trait_name);
1011 };
1012 add_conditional_assertion(should_derive_copy(record), quote! { Copy });
1013 add_conditional_assertion(should_implement_drop(record), quote! { Drop });
1014 assertions
1015 };
Michael Forsterdb8101a2021-10-08 06:56:03 +00001016 let assertion_tokens = quote! {
Rosica Dejanovskaaccf00b2022-04-01 13:28:04 -07001017 const _: () = assert!(rust_std::mem::size_of::<#ident>() == #size);
1018 const _: () = assert!(rust_std::mem::align_of::<#ident>() == #alignment);
Lukasz Anforowicz95938f82022-03-24 13:51:54 +00001019 #( #record_trait_assertions )*
Lukasz Anforowiczfed64e62022-03-22 22:39:04 +00001020 #( #field_offset_assertions )*
1021 #( #field_copy_trait_assertions )*
Michael Forsterdb8101a2021-10-08 06:56:03 +00001022 };
1023
1024 Ok((
1025 RsSnippet { features: record_features, tokens: record_tokens },
1026 RsSnippet { features: assertion_features, tokens: assertion_tokens },
1027 ))
Marcel Hlopkob4b28742021-09-15 12:45:20 +00001028}
1029
Lukasz Anforowicz2e41bb62022-01-11 18:23:07 +00001030fn should_derive_clone(record: &Record) -> bool {
Devin Jeanpierre88343c72022-01-15 01:10:23 +00001031 record.is_unpin()
Devin Jeanpierre2ed14ec2021-10-06 11:32:19 +00001032 && record.copy_constructor.access == ir::AccessSpecifier::Public
1033 && record.copy_constructor.definition == SpecialMemberDefinition::Trivial
Lukasz Anforowicz2e41bb62022-01-11 18:23:07 +00001034}
1035
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00001036fn should_derive_copy(record: &Record) -> bool {
1037 // TODO(b/202258760): Make `Copy` inclusion configurable.
1038 should_derive_clone(record)
1039}
1040
1041fn generate_derives(record: &Record) -> Vec<Ident> {
1042 let mut derives = vec![];
Lukasz Anforowicz2e41bb62022-01-11 18:23:07 +00001043 if should_derive_clone(record) {
Marcel Hlopkoeaae9b72022-01-21 15:54:11 +00001044 derives.push(make_rs_ident("Clone"));
Devin Jeanpierre2ed14ec2021-10-06 11:32:19 +00001045 }
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00001046 if should_derive_copy(record) {
Marcel Hlopkoeaae9b72022-01-21 15:54:11 +00001047 derives.push(make_rs_ident("Copy"));
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00001048 }
1049 derives
Devin Jeanpierre2ed14ec2021-10-06 11:32:19 +00001050}
1051
Teddy Katz76fa42b2022-02-23 01:22:56 +00001052fn generate_enum(enum_: &Enum, ir: &IR) -> Result<TokenStream> {
1053 let name = make_rs_ident(&enum_.identifier.identifier);
Devin Jeanpierre9886fb42022-04-01 04:31:20 -07001054 let underlying_type = format_rs_type(&enum_.underlying_type.rs_type, ir)?;
Teddy Katz76fa42b2022-02-23 01:22:56 +00001055 let enumerator_names =
1056 enum_.enumerators.iter().map(|enumerator| make_rs_ident(&enumerator.identifier.identifier));
1057 let enumerator_values = enum_.enumerators.iter().map(|enumerator| enumerator.value);
1058 Ok(quote! {
1059 #[repr(transparent)]
1060 #[derive(Debug, PartialEq, Eq, Copy, Clone, Hash, PartialOrd, Ord)]
1061 pub struct #name(#underlying_type);
1062 impl #name {
1063 #(pub const #enumerator_names: #name = #name(#enumerator_values);)*
1064 }
1065 impl From<#underlying_type> for #name {
1066 fn from(value: #underlying_type) -> #name {
1067 #name(v)
1068 }
1069 }
1070 impl From<#name> for #underlying_type {
1071 fn from(value: #name) -> #underlying_type {
1072 v.0
1073 }
1074 }
1075 })
1076}
1077
Googler6a0a5252022-01-11 14:08:09 +00001078fn generate_type_alias(type_alias: &TypeAlias, ir: &IR) -> Result<TokenStream> {
Marcel Hlopkoeaae9b72022-01-21 15:54:11 +00001079 let ident = make_rs_ident(&type_alias.identifier.identifier);
Lukasz Anforowiczc503af42022-03-04 23:18:15 +00001080 let doc_comment = generate_doc_comment(&type_alias.doc_comment);
Devin Jeanpierre9886fb42022-04-01 04:31:20 -07001081 let underlying_type = format_rs_type(&type_alias.underlying_type.rs_type, ir)
Googler6a0a5252022-01-11 14:08:09 +00001082 .with_context(|| format!("Failed to format underlying type for {:?}", type_alias))?;
Lukasz Anforowiczc503af42022-03-04 23:18:15 +00001083 Ok(quote! {
1084 #doc_comment
1085 pub type #ident = #underlying_type;
1086 })
Googler6a0a5252022-01-11 14:08:09 +00001087}
1088
Michael Forster523dbd42021-10-12 11:05:44 +00001089/// Generates Rust source code for a given `UnsupportedItem`.
1090fn generate_unsupported(item: &UnsupportedItem) -> Result<TokenStream> {
Googler48a74dd2021-10-25 07:31:53 +00001091 let location = if item.source_loc.filename.is_empty() {
1092 "<unknown location>".to_string()
1093 } else {
1094 // TODO(forster): The "google3" prefix should probably come from a command line
1095 // argument.
1096 // TODO(forster): Consider linking to the symbol instead of to the line number
1097 // to avoid wrong links while generated files have not caught up.
1098 format!("google3/{};l={}", &item.source_loc.filename, &item.source_loc.line)
1099 };
Michael Forster6a184ad2021-10-12 13:04:05 +00001100 let message = format!(
Googler48a74dd2021-10-25 07:31:53 +00001101 "{}\nError while generating bindings for item '{}':\n{}",
1102 &location, &item.name, &item.message
Michael Forster6a184ad2021-10-12 13:04:05 +00001103 );
Michael Forster523dbd42021-10-12 11:05:44 +00001104 Ok(quote! { __COMMENT__ #message })
1105}
1106
Michael Forsterf1dce422021-10-13 09:50:16 +00001107/// Generates Rust source code for a given `Comment`.
1108fn generate_comment(comment: &Comment) -> Result<TokenStream> {
1109 let text = &comment.text;
1110 Ok(quote! { __COMMENT__ #text })
1111}
1112
Rosica Dejanovska8da8f792022-03-29 02:02:22 -07001113#[derive(Clone, Debug, Default)]
1114struct GeneratedItem {
1115 item: TokenStream,
1116 thunks: TokenStream,
1117 assertions: TokenStream,
1118 features: BTreeSet<Ident>,
1119 has_record: bool,
1120}
1121
1122fn generate_item(
1123 item: &Item,
1124 ir: &IR,
1125 overloaded_funcs: &HashSet<FunctionId>,
1126) -> Result<GeneratedItem> {
1127 let generated_item = match item {
Devin Jeanpierred7b48102022-03-31 04:15:03 -07001128 Item::Func(func) => match generate_func(func, ir) {
1129 Err(e) => GeneratedItem {
1130 item: generate_unsupported(&make_unsupported_fn(func, ir, format!("{e}"))?)?,
1131 ..Default::default()
1132 },
1133 Ok(None) => GeneratedItem::default(),
1134 Ok(Some((api_func, thunk, function_id))) => {
Rosica Dejanovska8da8f792022-03-29 02:02:22 -07001135 if overloaded_funcs.contains(&function_id) {
1136 GeneratedItem {
1137 item: generate_unsupported(&make_unsupported_fn(
1138 func,
1139 ir,
1140 "Cannot generate bindings for overloaded function",
1141 )?)?,
1142 ..Default::default()
1143 }
1144 } else {
1145 GeneratedItem {
1146 item: api_func.tokens,
1147 thunks: thunk.tokens,
1148 features: api_func.features.union(&thunk.features).cloned().collect(),
1149 ..Default::default()
1150 }
1151 }
1152 }
1153 },
1154 Item::Record(record) => {
1155 if !ir.is_current_target(&record.owning_target)
1156 && !ir.is_stdlib_target(&record.owning_target)
1157 {
1158 GeneratedItem { ..Default::default() }
1159 } else {
1160 let (snippet, assertions_snippet) = generate_record(record, ir)?;
1161 GeneratedItem {
1162 item: snippet.tokens,
1163 assertions: assertions_snippet.tokens,
1164 features: snippet
1165 .features
1166 .union(&assertions_snippet.features)
1167 .cloned()
1168 .collect(),
1169 has_record: true,
1170 ..Default::default()
1171 }
1172 }
1173 }
1174 Item::Enum(enum_) => {
1175 if !ir.is_current_target(&enum_.owning_target)
1176 && !ir.is_stdlib_target(&enum_.owning_target)
1177 {
1178 GeneratedItem { ..Default::default() }
1179 } else {
1180 GeneratedItem { item: generate_enum(enum_, ir)?, ..Default::default() }
1181 }
1182 }
1183 Item::TypeAlias(type_alias) => {
1184 if !ir.is_current_target(&type_alias.owning_target)
1185 && !ir.is_stdlib_target(&type_alias.owning_target)
1186 {
1187 GeneratedItem { ..Default::default() }
1188 } else {
1189 GeneratedItem { item: generate_type_alias(type_alias, ir)?, ..Default::default() }
1190 }
1191 }
1192 Item::UnsupportedItem(unsupported) => {
1193 GeneratedItem { item: generate_unsupported(unsupported)?, ..Default::default() }
1194 }
1195 Item::Comment(comment) => {
1196 GeneratedItem { item: generate_comment(comment)?, ..Default::default() }
1197 }
1198 };
1199
1200 Ok(generated_item)
1201}
1202
Marcel Hlopko89547752021-12-10 09:39:41 +00001203fn generate_rs_api(ir: &IR) -> Result<TokenStream> {
Michael Forstered642022021-10-04 09:48:25 +00001204 let mut items = vec![];
Marcel Hlopko42abfc82021-08-09 07:03:17 +00001205 let mut thunks = vec![];
Michael Forsterdb8101a2021-10-08 06:56:03 +00001206 let mut assertions = vec![];
Marcel Hlopko42abfc82021-08-09 07:03:17 +00001207
Googler454f2652021-12-06 12:53:12 +00001208 // We import nullable pointers as an Option<&T> and assume that at the ABI
1209 // level, None is represented as a zero pointer value whereas Some is
1210 // represented as as non-zero pointer value. This seems like a pretty safe
1211 // assumption to make, but to provide some safeguard, assert that
1212 // `Option<&i32>` and `&i32` have the same size.
1213 assertions.push(quote! {
Rosica Dejanovskaaccf00b2022-04-01 13:28:04 -07001214 const _: () = assert!(rust_std::mem::size_of::<Option<&i32>>() == rust_std::mem::size_of::<&i32>());
Googler454f2652021-12-06 12:53:12 +00001215 });
1216
Michael Forsterbee84482021-10-13 08:35:38 +00001217 // TODO(jeanpierreda): Delete has_record, either in favor of using RsSnippet, or not
1218 // having uses. See https://chat.google.com/room/AAAAnQmj8Qs/6QbkSvWcfhA
Devin Jeanpierreea700d32021-10-06 11:33:56 +00001219 let mut has_record = false;
Devin Jeanpierre273eeae2021-10-06 13:29:35 +00001220 let mut features = BTreeSet::new();
Marcel Hlopko42abfc82021-08-09 07:03:17 +00001221
Lukasz Anforowicz72c4d222022-02-18 19:07:28 +00001222 // For #![rustfmt::skip].
1223 features.insert(make_rs_ident("custom_inner_attributes"));
1224
Googlerd03d05b2022-01-07 10:10:57 +00001225 // Identify all functions having overloads that we can't import (yet).
1226 // TODO(b/213280424): Implement support for overloaded functions.
1227 let mut seen_funcs = HashSet::new();
1228 let mut overloaded_funcs = HashSet::new();
1229 for func in ir.functions() {
Devin Jeanpierred7b48102022-03-31 04:15:03 -07001230 if let Ok(Some((.., function_id))) = generate_func(func, ir) {
Googlerd03d05b2022-01-07 10:10:57 +00001231 if !seen_funcs.insert(function_id.clone()) {
1232 overloaded_funcs.insert(function_id);
1233 }
1234 }
1235 }
1236
Marcel Hlopko3b9bf9e2021-11-29 08:25:14 +00001237 for item in ir.items() {
Rosica Dejanovska8da8f792022-03-29 02:02:22 -07001238 let generated = generate_item(item, ir, &overloaded_funcs)?;
1239 items.push(generated.item);
Devin Jeanpierreb5ba1402022-03-29 07:29:24 -07001240 if !generated.thunks.is_empty() {
Rosica Dejanovska8da8f792022-03-29 02:02:22 -07001241 thunks.push(generated.thunks);
Michael Forstered642022021-10-04 09:48:25 +00001242 }
Devin Jeanpierreb5ba1402022-03-29 07:29:24 -07001243 if !generated.assertions.is_empty() {
Rosica Dejanovska8da8f792022-03-29 02:02:22 -07001244 assertions.push(generated.assertions);
1245 }
1246 features.extend(generated.features);
1247 has_record = has_record || generated.has_record;
Marcel Hlopko42abfc82021-08-09 07:03:17 +00001248 }
1249
Marcel Hlopkob4b28742021-09-15 12:45:20 +00001250 let mod_detail = if thunks.is_empty() {
1251 quote! {}
1252 } else {
1253 quote! {
1254 mod detail {
Googler55647142022-01-11 12:37:39 +00001255 #[allow(unused_imports)]
Devin Jeanpierred4dde0e2021-10-13 20:48:25 +00001256 use super::*;
Marcel Hlopkob4b28742021-09-15 12:45:20 +00001257 extern "C" {
1258 #( #thunks )*
1259 }
Marcel Hlopko42abfc82021-08-09 07:03:17 +00001260 }
1261 }
1262 };
1263
Rosica Dejanovskaaccf00b2022-04-01 13:28:04 -07001264 // TODO(b/227790881): Replace usage of rust_std with ::std once the issue
1265 // is fixed.
Devin Jeanpierreea700d32021-10-06 11:33:56 +00001266 let imports = if has_record {
Googlerec648ff2021-09-23 07:19:53 +00001267 quote! {
Rosica Dejanovskabbed2012022-04-05 04:06:30 -07001268 use ::std as rust_std;
Googleraaa0a532021-10-01 09:11:27 +00001269 use memoffset_unstable_const::offset_of;
Lukasz Anforowiczfed64e62022-03-22 22:39:04 +00001270 use static_assertions::{assert_impl_all, assert_not_impl_all};
Googlerec648ff2021-09-23 07:19:53 +00001271 }
Michael Forstered642022021-10-04 09:48:25 +00001272 } else {
Rosica Dejanovskaaccf00b2022-04-01 13:28:04 -07001273 quote! {
Rosica Dejanovskabbed2012022-04-05 04:06:30 -07001274 use ::std as rust_std;
Rosica Dejanovskaaccf00b2022-04-01 13:28:04 -07001275 }
Googlerec648ff2021-09-23 07:19:53 +00001276 };
1277
Devin Jeanpierre273eeae2021-10-06 13:29:35 +00001278 let features = if features.is_empty() {
1279 quote! {}
1280 } else {
1281 quote! {
1282 #![feature( #(#features),* )]
1283 }
1284 };
1285
Marcel Hlopko89547752021-12-10 09:39:41 +00001286 Ok(quote! {
Googler55647142022-01-11 12:37:39 +00001287 #features __NEWLINE__
1288 #![allow(non_camel_case_types)] __NEWLINE__
1289 #![allow(non_snake_case)] __NEWLINE__ __NEWLINE__
1290
Michael Forsterdb8101a2021-10-08 06:56:03 +00001291 #imports __NEWLINE__ __NEWLINE__
Googlerec648ff2021-09-23 07:19:53 +00001292
Michael Forsterdb8101a2021-10-08 06:56:03 +00001293 #( #items __NEWLINE__ __NEWLINE__ )*
Marcel Hlopkob4b28742021-09-15 12:45:20 +00001294
Michael Forsterdb8101a2021-10-08 06:56:03 +00001295 #mod_detail __NEWLINE__ __NEWLINE__
1296
1297 #( #assertions __NEWLINE__ __NEWLINE__ )*
Marcel Hlopko89547752021-12-10 09:39:41 +00001298 })
Marcel Hlopko42abfc82021-08-09 07:03:17 +00001299}
1300
Marcel Hlopkoeaae9b72022-01-21 15:54:11 +00001301/// Makes an 'Ident' to be used in the Rust source code. Escapes Rust keywords.
1302fn make_rs_ident(ident: &str) -> Ident {
1303 // TODO(https://github.com/dtolnay/syn/pull/1098): Remove the hardcoded list once syn recognizes
1304 // 2018 and 2021 keywords.
1305 if ["async", "await", "try", "dyn"].contains(&ident) {
1306 return format_ident!("r#{}", ident);
1307 }
1308 match syn::parse_str::<syn::Ident>(ident) {
1309 Ok(_) => format_ident!("{}", ident),
1310 Err(_) => format_ident!("r#{}", ident),
1311 }
1312}
1313
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +00001314/// Formats a C++ identifier. Does not escape C++ keywords.
1315fn format_cc_ident(ident: &str) -> TokenStream {
1316 ident.parse().unwrap()
Marcel Hlopko42abfc82021-08-09 07:03:17 +00001317}
1318
Devin Jeanpierrefec5bfe2022-04-05 10:59:10 -07001319/// Returns Some(crate_ident) if this is an imported crate.
1320fn rs_imported_crate_name(owning_target: &BazelLabel, ir: &IR) -> Option<Ident> {
Googler6a0a5252022-01-11 14:08:09 +00001321 if ir.is_current_target(owning_target) || ir.is_stdlib_target(owning_target) {
Devin Jeanpierrefec5bfe2022-04-05 10:59:10 -07001322 None
Googler6a0a5252022-01-11 14:08:09 +00001323 } else {
Devin Jeanpierre084ebbd2022-04-01 04:32:46 -07001324 let owning_crate_name = owning_target.target_name();
Marcel Hlopkod906b892022-01-27 08:52:36 +00001325 // TODO(b/216587072): Remove this hacky escaping and use the import! macro once
1326 // available
Lukasz Anforowiczdd9ae0f2022-02-17 15:52:53 +00001327 let escaped_owning_crate_name = owning_crate_name.replace('-', "_");
Marcel Hlopkod906b892022-01-27 08:52:36 +00001328 let owning_crate = make_rs_ident(&escaped_owning_crate_name);
Devin Jeanpierrefec5bfe2022-04-05 10:59:10 -07001329 Some(owning_crate)
Googler6a0a5252022-01-11 14:08:09 +00001330 }
1331}
1332
Devin Jeanpierre103cbb52022-04-06 12:55:16 -07001333#[derive(Copy, Clone, Debug, Eq, PartialEq)]
Lukasz Anforowicz40c2eb82022-01-11 18:22:31 +00001334enum Mutability {
1335 Const,
1336 Mut,
1337}
1338
1339impl Mutability {
1340 fn format_for_pointer(&self) -> TokenStream {
1341 match self {
1342 Mutability::Mut => quote! {mut},
1343 Mutability::Const => quote! {const},
1344 }
1345 }
1346
1347 fn format_for_reference(&self) -> TokenStream {
1348 match self {
1349 Mutability::Mut => quote! {mut},
1350 Mutability::Const => quote! {},
1351 }
1352 }
1353}
1354
1355// TODO(b/213947473): Instead of having a separate RsTypeKind here, consider
1356// changing ir::RsType into a similar `enum`, with fields that contain
Rosica Dejanovskad638cf52022-03-23 15:45:01 +00001357// references (e.g. &'ir Record`) instead of ItemIds.
Devin Jeanpierre103cbb52022-04-06 12:55:16 -07001358#[derive(Clone, Debug)]
Lukasz Anforowicz40c2eb82022-01-11 18:22:31 +00001359enum RsTypeKind<'ir> {
Devin Jeanpierrefec5bfe2022-04-05 10:59:10 -07001360 Pointer {
1361 pointee: Box<RsTypeKind<'ir>>,
1362 mutability: Mutability,
1363 },
1364 Reference {
1365 referent: Box<RsTypeKind<'ir>>,
1366 mutability: Mutability,
1367 lifetime: Lifetime,
1368 },
1369 RvalueReference {
1370 referent: Box<RsTypeKind<'ir>>,
1371 mutability: Mutability,
1372 lifetime: Lifetime,
1373 },
1374 FuncPtr {
1375 abi: &'ir str,
1376 return_type: Box<RsTypeKind<'ir>>,
1377 param_types: Vec<RsTypeKind<'ir>>,
1378 },
1379 Record {
1380 record: &'ir Record,
1381 /// The imported crate this comes from, or None if the current crate.
1382 crate_ident: Option<Ident>,
1383 },
1384 TypeAlias {
1385 type_alias: &'ir TypeAlias,
1386 underlying_type: Box<RsTypeKind<'ir>>,
1387 /// The imported crate this comes from, or None if the current crate.
1388 crate_ident: Option<Ident>,
1389 },
Lukasz Anforowicz40c2eb82022-01-11 18:22:31 +00001390 Unit,
Devin Jeanpierrefec5bfe2022-04-05 10:59:10 -07001391 Other {
1392 name: &'ir str,
1393 type_args: Vec<RsTypeKind<'ir>>,
1394 },
Lukasz Anforowicz40c2eb82022-01-11 18:22:31 +00001395}
1396
1397impl<'ir> RsTypeKind<'ir> {
1398 pub fn new(ty: &'ir ir::RsType, ir: &'ir IR) -> Result<Self> {
1399 // The lambdas deduplicate code needed by multiple `match` branches.
1400 let get_type_args = || -> Result<Vec<RsTypeKind<'ir>>> {
1401 ty.type_args.iter().map(|type_arg| RsTypeKind::<'ir>::new(type_arg, ir)).collect()
1402 };
1403 let get_pointee = || -> Result<Box<RsTypeKind<'ir>>> {
1404 if ty.type_args.len() != 1 {
1405 bail!("Missing pointee/referent type (need exactly 1 type argument): {:?}", ty);
1406 }
1407 Ok(Box::new(get_type_args()?.remove(0)))
1408 };
Devin Jeanpierred2b7a8f2022-04-01 04:37:16 -07001409 let get_lifetime = || -> Result<Lifetime> {
Lukasz Anforowicz40c2eb82022-01-11 18:22:31 +00001410 if ty.lifetime_args.len() != 1 {
1411 bail!("Missing reference lifetime (need exactly 1 lifetime argument): {:?}", ty);
1412 }
Devin Jeanpierred2b7a8f2022-04-01 04:37:16 -07001413 let lifetime_id = ty.lifetime_args[0];
1414 ir.get_lifetime(lifetime_id)
1415 .ok_or_else(|| anyhow!("no known lifetime with id {lifetime_id:?}"))
1416 .cloned()
Lukasz Anforowicz40c2eb82022-01-11 18:22:31 +00001417 };
1418
1419 let result = match ty.name.as_deref() {
1420 None => {
1421 ensure!(
1422 ty.type_args.is_empty(),
1423 "Type arguments on records nor type aliases are not yet supported: {:?}",
1424 ty
1425 );
1426 match ir.item_for_type(ty)? {
Devin Jeanpierrefec5bfe2022-04-05 10:59:10 -07001427 Item::Record(record) => RsTypeKind::new_record(record, ir),
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00001428 Item::TypeAlias(type_alias) => RsTypeKind::TypeAlias {
1429 type_alias,
Devin Jeanpierrefec5bfe2022-04-05 10:59:10 -07001430 crate_ident: rs_imported_crate_name(&type_alias.owning_target, ir),
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00001431 underlying_type: Box::new(RsTypeKind::new(
1432 &type_alias.underlying_type.rs_type,
1433 ir,
1434 )?),
1435 },
Lukasz Anforowicz40c2eb82022-01-11 18:22:31 +00001436 other_item => bail!("Item does not define a type: {:?}", other_item),
1437 }
1438 }
1439 Some(name) => match name {
1440 "()" => {
1441 if !ty.type_args.is_empty() {
1442 bail!("Unit type must not have type arguments: {:?}", ty);
1443 }
1444 RsTypeKind::Unit
1445 }
1446 "*mut" => {
1447 RsTypeKind::Pointer { pointee: get_pointee()?, mutability: Mutability::Mut }
1448 }
1449 "*const" => {
1450 RsTypeKind::Pointer { pointee: get_pointee()?, mutability: Mutability::Const }
1451 }
1452 "&mut" => RsTypeKind::Reference {
1453 referent: get_pointee()?,
1454 mutability: Mutability::Mut,
Devin Jeanpierred2b7a8f2022-04-01 04:37:16 -07001455 lifetime: get_lifetime()?,
Lukasz Anforowicz40c2eb82022-01-11 18:22:31 +00001456 },
1457 "&" => RsTypeKind::Reference {
1458 referent: get_pointee()?,
1459 mutability: Mutability::Const,
Devin Jeanpierred2b7a8f2022-04-01 04:37:16 -07001460 lifetime: get_lifetime()?,
Lukasz Anforowicz40c2eb82022-01-11 18:22:31 +00001461 },
Devin Jeanpierredeea7892022-03-29 02:13:31 -07001462 "#RvalueReference mut" => RsTypeKind::RvalueReference {
1463 referent: get_pointee()?,
1464 mutability: Mutability::Mut,
Devin Jeanpierred2b7a8f2022-04-01 04:37:16 -07001465 lifetime: get_lifetime()?,
Devin Jeanpierredeea7892022-03-29 02:13:31 -07001466 },
1467 "#RvalueReference const" => RsTypeKind::RvalueReference {
1468 referent: get_pointee()?,
1469 mutability: Mutability::Const,
Devin Jeanpierred2b7a8f2022-04-01 04:37:16 -07001470 lifetime: get_lifetime()?,
Devin Jeanpierredeea7892022-03-29 02:13:31 -07001471 },
Lukasz Anforowiczcf230fd2022-02-18 19:20:39 +00001472 name => {
1473 let mut type_args = get_type_args()?;
1474 match name.strip_prefix("#funcPtr ") {
1475 None => RsTypeKind::Other { name, type_args },
1476 Some(abi) => {
1477 // TODO(b/217419782): Consider enforcing `'static` lifetime.
1478 ensure!(!type_args.is_empty(), "No return type in fn type: {:?}", ty);
1479 RsTypeKind::FuncPtr {
1480 abi,
1481 return_type: Box::new(type_args.remove(type_args.len() - 1)),
1482 param_types: type_args,
1483 }
Devin Jeanpierre1b8f4a12022-03-22 21:29:42 +00001484 }
Lukasz Anforowiczcf230fd2022-02-18 19:20:39 +00001485 }
Devin Jeanpierre1b8f4a12022-03-22 21:29:42 +00001486 }
Lukasz Anforowicz40c2eb82022-01-11 18:22:31 +00001487 },
1488 };
1489 Ok(result)
1490 }
1491
Devin Jeanpierrefec5bfe2022-04-05 10:59:10 -07001492 pub fn new_record(record: &'ir Record, ir: &'ir IR) -> Self {
1493 RsTypeKind::Record {
1494 record,
1495 crate_ident: rs_imported_crate_name(&record.owning_target, ir),
1496 }
1497 }
1498
Devin Jeanpierre149950d2022-02-22 21:02:02 +00001499 /// Returns true if the type is known to be `Unpin`, false otherwise.
Devin Jeanpierref85ae592022-04-01 04:33:57 -07001500 pub fn is_unpin(&self) -> bool {
Devin Jeanpierre149950d2022-02-22 21:02:02 +00001501 match self {
Devin Jeanpierrefec5bfe2022-04-05 10:59:10 -07001502 RsTypeKind::Record { record, .. } => record.is_unpin(),
Devin Jeanpierref85ae592022-04-01 04:33:57 -07001503 RsTypeKind::TypeAlias { underlying_type, .. } => underlying_type.is_unpin(),
Devin Jeanpierre149950d2022-02-22 21:02:02 +00001504 _ => true,
1505 }
1506 }
1507
Devin Jeanpierre82e8e362022-04-05 11:04:55 -07001508 pub fn format_as_return_type_fragment(&self) -> TokenStream {
Lukasz Anforowiczcf230fd2022-02-18 19:20:39 +00001509 match self {
Devin Jeanpierred2b7a8f2022-04-01 04:37:16 -07001510 RsTypeKind::Unit => quote! {},
Lukasz Anforowiczcf230fd2022-02-18 19:20:39 +00001511 other_type => {
Devin Jeanpierre92ca2612022-04-06 11:35:13 -07001512 quote! { -> #other_type }
Lukasz Anforowiczcf230fd2022-02-18 19:20:39 +00001513 }
1514 }
1515 }
1516
Lukasz Anforowiczcde4b1b2022-02-03 21:20:55 +00001517 /// Formats this RsTypeKind as `&'a mut MaybeUninit<SomeStruct>`. This is
1518 /// used to format `__this` parameter in a constructor thunk.
Devin Jeanpierre82e8e362022-04-05 11:04:55 -07001519 pub fn format_mut_ref_as_uninitialized(&self) -> Result<TokenStream> {
Lukasz Anforowiczcde4b1b2022-02-03 21:20:55 +00001520 match self {
Devin Jeanpierred2b7a8f2022-04-01 04:37:16 -07001521 RsTypeKind::Reference { referent, lifetime, mutability: Mutability::Mut } => {
Devin Jeanpierred2b7a8f2022-04-01 04:37:16 -07001522 let lifetime = format_lifetime_name(&lifetime.name);
Devin Jeanpierre92ca2612022-04-06 11:35:13 -07001523 Ok(quote! { & #lifetime mut rust_std::mem::MaybeUninit< #referent > })
Lukasz Anforowicz40c2eb82022-01-11 18:22:31 +00001524 }
Devin Jeanpierre149950d2022-02-22 21:02:02 +00001525 _ => bail!("Expected reference to format as MaybeUninit, got: {:?}", self),
Lukasz Anforowiczcde4b1b2022-02-03 21:20:55 +00001526 }
Lukasz Anforowicz40c2eb82022-01-11 18:22:31 +00001527 }
1528
Devin Jeanpierre149950d2022-02-22 21:02:02 +00001529 /// Formats a reference or pointer as a raw pointer.
Devin Jeanpierre82e8e362022-04-05 11:04:55 -07001530 pub fn format_ref_as_raw_ptr(&self) -> Result<TokenStream> {
Devin Jeanpierre149950d2022-02-22 21:02:02 +00001531 match self {
1532 RsTypeKind::Reference { referent: pointee, mutability, .. }
1533 | RsTypeKind::Pointer { pointee, mutability } => {
Devin Jeanpierre149950d2022-02-22 21:02:02 +00001534 let mut_ = mutability.format_for_pointer();
Devin Jeanpierre92ca2612022-04-06 11:35:13 -07001535 Ok(quote! { * #mut_ #pointee })
Devin Jeanpierre149950d2022-02-22 21:02:02 +00001536 }
1537 _ => bail!("Expected reference to format as raw ptr, got: {:?}", self),
1538 }
1539 }
1540
1541 /// Formats this RsTypeKind as the `self` parameter: usually, `&'a self` or
1542 /// `&'a mut self`.
1543 ///
1544 /// If this is !Unpin, however, it uses `self: Pin<&mut Self>` instead.
Devin Jeanpierre9886fb42022-04-01 04:31:20 -07001545 pub fn format_as_self_param(&self, func: &Func, ir: &IR) -> Result<TokenStream> {
Lukasz Anforowicz732ca642022-02-03 20:58:38 +00001546 if func.name == UnqualifiedIdentifier::Destructor {
1547 let record = func
1548 .member_func_metadata
1549 .as_ref()
1550 .ok_or_else(|| anyhow!("Destructors must be member functions: {:?}", func))?
1551 .find_record(ir)?;
1552 if self.is_mut_ptr_to(record) {
1553 // Even in C++ it is UB to retain `this` pointer and dereference it
1554 // after a destructor runs. Therefore it is safe to use `&self` or
1555 // `&mut self` in Rust even if IR represents `__this` as a Rust
1556 // pointer (e.g. when lifetime annotations are missing - lifetime
1557 // annotations are required to represent it as a Rust reference).
1558 return Ok(quote! { &mut self });
1559 }
Lukasz Anforowicz231a3bb2022-01-12 14:05:59 +00001560 }
1561
1562 match self {
Devin Jeanpierred2b7a8f2022-04-01 04:37:16 -07001563 RsTypeKind::Reference { referent, lifetime, mutability } => {
Devin Jeanpierre149950d2022-02-22 21:02:02 +00001564 let mut_ = mutability.format_for_reference();
Devin Jeanpierred2b7a8f2022-04-01 04:37:16 -07001565 let lifetime = format_lifetime_name(&lifetime.name);
Devin Jeanpierre1b8f4a12022-03-22 21:29:42 +00001566 if mutability == &Mutability::Mut
Devin Jeanpierref85ae592022-04-01 04:33:57 -07001567 && !referent.is_unpin()
Devin Jeanpierre1b8f4a12022-03-22 21:29:42 +00001568 && func.name != UnqualifiedIdentifier::Destructor
1569 {
Devin Jeanpierrefec5bfe2022-04-05 10:59:10 -07001570 // TODO(b/200067242): Add a `use rust_std::pin::Pin` to the crate, and use
1571 // `Pin`.
Rosica Dejanovskaaccf00b2022-04-01 13:28:04 -07001572 Ok(quote! {self: rust_std::pin::Pin< & #lifetime #mut_ Self>})
Devin Jeanpierre149950d2022-02-22 21:02:02 +00001573 } else {
1574 Ok(quote! { & #lifetime #mut_ self })
1575 }
Lukasz Anforowicz231a3bb2022-01-12 14:05:59 +00001576 }
Lukasz Anforowicz732ca642022-02-03 20:58:38 +00001577 _ => bail!("Unexpected type of `self` parameter: {:?}", self),
Lukasz Anforowicz231a3bb2022-01-12 14:05:59 +00001578 }
1579 }
1580
Lukasz Anforowicz90bdb962022-02-14 21:07:45 +00001581 /// Returns whether the type represented by `self` implements the `Copy`
1582 /// trait.
Lukasz Anforowicza94ab702022-01-14 22:40:25 +00001583 pub fn implements_copy(&self) -> bool {
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00001584 // TODO(b/212696226): Verify results of `implements_copy` via static
1585 // assertions in the generated Rust code (because incorrect results
1586 // can silently lead to unsafe behavior).
1587 match self {
1588 RsTypeKind::Unit => true,
1589 RsTypeKind::Pointer { .. } => true,
Lukasz Anforowiczcf230fd2022-02-18 19:20:39 +00001590 RsTypeKind::FuncPtr { .. } => true,
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00001591 RsTypeKind::Reference { mutability: Mutability::Const, .. } => true,
1592 RsTypeKind::Reference { mutability: Mutability::Mut, .. } => false,
Devin Jeanpierredeea7892022-03-29 02:13:31 -07001593 RsTypeKind::RvalueReference { .. } => false,
Devin Jeanpierrefec5bfe2022-04-05 10:59:10 -07001594 RsTypeKind::Record { record, .. } => should_derive_copy(record),
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00001595 RsTypeKind::TypeAlias { underlying_type, .. } => underlying_type.implements_copy(),
Lukasz Anforowiczd81bea92022-02-11 08:57:58 +00001596 RsTypeKind::Other { type_args, .. } => {
Lukasz Anforowicz20651e32022-02-10 14:52:15 +00001597 // All types that may appear here without `type_args` (e.g.
1598 // primitive types like `i32`) implement `Copy`. Generic types
1599 // that may be present here (e.g. Option<...>) are `Copy` if all
1600 // of their `type_args` are `Copy`.
1601 type_args.iter().all(|t| t.implements_copy())
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00001602 }
1603 }
1604 }
Lukasz Anforowicza94ab702022-01-14 22:40:25 +00001605
Lukasz Anforowiczf9564622022-01-28 14:31:04 +00001606 pub fn is_mut_ptr_to(&self, expected_record: &Record) -> bool {
1607 match self {
1608 RsTypeKind::Pointer { pointee, mutability: Mutability::Mut, .. } => {
1609 pointee.is_record(expected_record)
1610 }
1611 _ => false,
1612 }
1613 }
1614
1615 pub fn is_ref_to(&self, expected_record: &Record) -> bool {
1616 match self {
1617 RsTypeKind::Reference { referent, .. } => referent.is_record(expected_record),
1618 _ => false,
1619 }
1620 }
1621
Lukasz Anforowicza94ab702022-01-14 22:40:25 +00001622 pub fn is_shared_ref_to(&self, expected_record: &Record) -> bool {
1623 match self {
1624 RsTypeKind::Reference { referent, mutability: Mutability::Const, .. } => {
Lukasz Anforowiczf9564622022-01-28 14:31:04 +00001625 referent.is_record(expected_record)
Lukasz Anforowicza94ab702022-01-14 22:40:25 +00001626 }
1627 _ => false,
1628 }
1629 }
Lukasz Anforowiczf9564622022-01-28 14:31:04 +00001630
1631 pub fn is_record(&self, expected_record: &Record) -> bool {
1632 match self {
Devin Jeanpierrefec5bfe2022-04-05 10:59:10 -07001633 RsTypeKind::Record { record: actual_record, .. } => {
1634 actual_record.id == expected_record.id
1635 }
Lukasz Anforowiczf9564622022-01-28 14:31:04 +00001636 _ => false,
1637 }
1638 }
Lukasz Anforowicz90bdb962022-02-14 21:07:45 +00001639
1640 /// Iterates over `self` and all the nested types (e.g. pointees, generic
1641 /// type args, etc.) in DFS order.
1642 pub fn dfs_iter<'ty>(&'ty self) -> impl Iterator<Item = &'ty RsTypeKind<'ir>> + '_ {
1643 RsTypeKindIter::new(self)
1644 }
1645
1646 /// Iterates over all `LifetimeId`s in `self` and in all the nested types.
1647 /// Note that the results might contain duplicate LifetimeId values (e.g.
1648 /// if the same LifetimeId is used in two `type_args`).
1649 pub fn lifetimes(&self) -> impl Iterator<Item = LifetimeId> + '_ {
1650 self.dfs_iter().filter_map(|t| match t {
Devin Jeanpierred2b7a8f2022-04-01 04:37:16 -07001651 RsTypeKind::Reference { lifetime, .. } => Some(lifetime.id),
Lukasz Anforowicz90bdb962022-02-14 21:07:45 +00001652 _ => None,
1653 })
1654 }
1655}
1656
Devin Jeanpierre92ca2612022-04-06 11:35:13 -07001657impl<'ir> ToTokens for RsTypeKind<'ir> {
1658 fn to_tokens(&self, tokens: &mut TokenStream) {
1659 self.to_token_stream().to_tokens(tokens)
1660 }
1661
1662 fn to_token_stream(&self) -> TokenStream {
1663 match self {
1664 RsTypeKind::Pointer { pointee, mutability } => {
1665 let mutability = mutability.format_for_pointer();
1666 quote! {* #mutability #pointee}
1667 }
1668 RsTypeKind::Reference { referent, mutability, lifetime } => {
1669 let mut_ = mutability.format_for_reference();
1670 let lifetime = format_lifetime_name(&lifetime.name);
1671 let reference = quote! {& #lifetime #mut_ #referent};
1672 if mutability == &Mutability::Mut && !referent.is_unpin() {
1673 // TODO(b/200067242): Add a `use rust_std::pin::Pin` to the crate, and use
1674 // `Pin`. This either requires deciding how to qualify pin at
1675 // RsTypeKind-creation time, or returning an RsSnippet from here (and not
1676 // implementing ToTokens, but instead some other interface.)
1677 quote! {rust_std::pin::Pin< #reference >}
1678 } else {
1679 reference
1680 }
1681 }
1682 RsTypeKind::RvalueReference { referent, mutability, lifetime } => {
1683 let lifetime = format_lifetime_name(&lifetime.name);
1684 // TODO(b/200067242): Add a `use ctor::RvalueReference` (etc.) to the crate.
1685 if mutability == &Mutability::Mut {
1686 quote! {ctor::RvalueReference<#lifetime, #referent>}
1687 } else {
1688 quote! {ctor::ConstRvalueReference<#lifetime, #referent>}
1689 }
1690 }
1691 RsTypeKind::FuncPtr { abi, return_type, param_types } => {
1692 let return_frag = return_type.format_as_return_type_fragment();
1693 quote! { extern #abi fn( #( #param_types ),* ) #return_frag }
1694 }
1695 RsTypeKind::Record { record, crate_ident } => {
1696 let record_ident = make_rs_ident(&record.rs_name);
1697 let crate_ident = crate_ident.iter();
1698 quote! {#(#crate_ident::)* #record_ident}
1699 }
1700 RsTypeKind::TypeAlias { type_alias, crate_ident, .. } => {
1701 let alias_ident = make_rs_ident(&type_alias.identifier.identifier);
1702 let crate_ident = crate_ident.iter();
1703 quote! {#(#crate_ident::)* #alias_ident}
1704 }
1705 RsTypeKind::Unit => quote! {()},
1706 RsTypeKind::Other { name, type_args } => {
1707 let ident = make_rs_ident(name);
1708 let generic_params = format_generic_params(type_args);
1709 quote! {#ident #generic_params}
1710 }
1711 }
1712 }
1713}
1714
Lukasz Anforowicz90bdb962022-02-14 21:07:45 +00001715struct RsTypeKindIter<'ty, 'ir> {
1716 todo: Vec<&'ty RsTypeKind<'ir>>,
1717}
1718
1719impl<'ty, 'ir> RsTypeKindIter<'ty, 'ir> {
1720 pub fn new(ty: &'ty RsTypeKind<'ir>) -> Self {
1721 Self { todo: vec![ty] }
1722 }
1723}
1724
1725impl<'ty, 'ir> Iterator for RsTypeKindIter<'ty, 'ir> {
1726 type Item = &'ty RsTypeKind<'ir>;
1727
1728 fn next(&mut self) -> Option<Self::Item> {
1729 match self.todo.pop() {
1730 None => None,
1731 Some(curr) => {
1732 match curr {
Devin Jeanpierrefec5bfe2022-04-05 10:59:10 -07001733 RsTypeKind::Unit | RsTypeKind::Record { .. } => (),
Lukasz Anforowicz90bdb962022-02-14 21:07:45 +00001734 RsTypeKind::Pointer { pointee, .. } => self.todo.push(pointee),
1735 RsTypeKind::Reference { referent, .. } => self.todo.push(referent),
Devin Jeanpierredeea7892022-03-29 02:13:31 -07001736 RsTypeKind::RvalueReference { referent, .. } => self.todo.push(referent),
Lukasz Anforowicz90bdb962022-02-14 21:07:45 +00001737 RsTypeKind::TypeAlias { underlying_type: t, .. } => self.todo.push(t),
Lukasz Anforowiczcf230fd2022-02-18 19:20:39 +00001738 RsTypeKind::FuncPtr { return_type, param_types, .. } => {
1739 self.todo.push(return_type);
1740 self.todo.extend(param_types.iter().rev());
Devin Jeanpierre1b8f4a12022-03-22 21:29:42 +00001741 }
Lukasz Anforowicz90bdb962022-02-14 21:07:45 +00001742 RsTypeKind::Other { type_args, .. } => self.todo.extend(type_args.iter().rev()),
1743 };
1744 Some(curr)
1745 }
1746 }
1747 }
Lukasz Anforowicz40c2eb82022-01-11 18:22:31 +00001748}
1749
Lukasz Anforowicz95551272022-01-20 00:02:24 +00001750fn format_lifetime_name(lifetime_name: &str) -> TokenStream {
1751 let lifetime =
1752 syn::Lifetime::new(&format!("'{}", lifetime_name), proc_macro2::Span::call_site());
1753 quote! { #lifetime }
1754}
1755
Devin Jeanpierre9886fb42022-04-01 04:31:20 -07001756fn format_rs_type(ty: &ir::RsType, ir: &IR) -> Result<TokenStream> {
Lukasz Anforowicz40c2eb82022-01-11 18:22:31 +00001757 RsTypeKind::new(ty, ir)
Devin Jeanpierre92ca2612022-04-06 11:35:13 -07001758 .map(|kind| kind.to_token_stream())
Lukasz Anforowicz40c2eb82022-01-11 18:22:31 +00001759 .with_context(|| format!("Failed to format Rust type {:?}", ty))
Devin Jeanpierre7a7328e2021-09-17 07:10:08 +00001760}
1761
Googler6a0a5252022-01-11 14:08:09 +00001762fn cc_type_name_for_item(item: &ir::Item) -> Result<TokenStream> {
Devin Jeanpierre1b8f4a12022-03-22 21:29:42 +00001763 Ok(match item {
Lukasz Anforowicz4c3a2cc2022-03-11 00:24:49 +00001764 Item::Record(record) => {
1765 let ident = format_cc_ident(&record.cc_name);
1766 quote! { class #ident }
Devin Jeanpierre1b8f4a12022-03-22 21:29:42 +00001767 }
Lukasz Anforowicz4c3a2cc2022-03-11 00:24:49 +00001768 Item::TypeAlias(type_alias) => {
1769 let ident = format_cc_ident(&type_alias.identifier.identifier);
1770 quote! { #ident }
Devin Jeanpierre1b8f4a12022-03-22 21:29:42 +00001771 }
Googler6a0a5252022-01-11 14:08:09 +00001772 _ => bail!("Item does not define a type: {:?}", item),
Lukasz Anforowicz4c3a2cc2022-03-11 00:24:49 +00001773 })
Googler6a0a5252022-01-11 14:08:09 +00001774}
1775
Lukasz Anforowicz71e9b592022-03-04 19:03:02 +00001776// Maps a Rust ABI [1] into a Clang attribute. See also
1777// `ConvertCcCallConvIntoRsApi` in importer.cc.
1778// [1]
1779// https://doc.rust-lang.org/reference/items/functions.html#extern-function-qualifier
1780fn format_cc_call_conv_as_clang_attribute(rs_abi: &str) -> Result<TokenStream> {
1781 match rs_abi {
1782 "cdecl" => Ok(quote! {}),
1783 "fastcall" => Ok(quote! { __attribute__((fastcall)) }),
1784 "stdcall" => Ok(quote! { __attribute__((stdcall)) }),
1785 "thiscall" => Ok(quote! { __attribute__((thiscall)) }),
1786 "vectorcall" => Ok(quote! { __attribute__((vectorcall)) }),
1787 _ => bail!("Unsupported ABI: {}", rs_abi),
1788 }
1789}
1790
Marcel Hlopkoc0956cf2021-11-29 08:31:28 +00001791fn format_cc_type(ty: &ir::CcType, ir: &IR) -> Result<TokenStream> {
Devin Jeanpierre09c6f452021-09-29 07:34:24 +00001792 let const_fragment = if ty.is_const {
Devin Jeanpierre184f9ac2021-09-17 13:47:03 +00001793 quote! {const}
1794 } else {
1795 quote! {}
1796 };
Marcel Hlopkoc0956cf2021-11-29 08:31:28 +00001797 if let Some(ref name) = ty.name {
1798 match name.as_str() {
1799 "*" => {
Googlerff7fc232021-12-02 09:43:00 +00001800 if ty.type_args.len() != 1 {
1801 bail!("Invalid pointer type (need exactly 1 type argument): {:?}", ty);
Marcel Hlopkoc0956cf2021-11-29 08:31:28 +00001802 }
Googlerff7fc232021-12-02 09:43:00 +00001803 assert_eq!(ty.type_args.len(), 1);
1804 let nested_type = format_cc_type(&ty.type_args[0], ir)?;
Marcel Hlopkoc0956cf2021-11-29 08:31:28 +00001805 Ok(quote! {#nested_type * #const_fragment})
Devin Jeanpierre7a7328e2021-09-17 07:10:08 +00001806 }
Lukasz Anforowicz275fa922022-01-05 16:13:20 +00001807 "&" => {
1808 if ty.type_args.len() != 1 {
1809 bail!("Invalid reference type (need exactly 1 type argument): {:?}", ty);
1810 }
1811 let nested_type = format_cc_type(&ty.type_args[0], ir)?;
1812 Ok(quote! {#nested_type &})
1813 }
Devin Jeanpierredeea7892022-03-29 02:13:31 -07001814 "&&" => {
1815 if ty.type_args.len() != 1 {
1816 bail!("Invalid rvalue reference type (need exactly 1 type argument): {:?}", ty);
1817 }
1818 let nested_type = format_cc_type(&ty.type_args[0], ir)?;
1819 Ok(quote! {#nested_type &&})
1820 }
Lukasz Anforowicz71e9b592022-03-04 19:03:02 +00001821 cc_type_name => match cc_type_name.strip_prefix("#funcValue ") {
1822 None => {
1823 if !ty.type_args.is_empty() {
1824 bail!("Type not yet supported: {:?}", ty);
1825 }
1826 let idents = cc_type_name.split_whitespace().map(format_cc_ident);
1827 Ok(quote! {#( #idents )* #const_fragment})
Devin Jeanpierre1b8f4a12022-03-22 21:29:42 +00001828 }
Lukasz Anforowicz71e9b592022-03-04 19:03:02 +00001829 Some(abi) => match ty.type_args.split_last() {
1830 None => bail!("funcValue type without a return type: {:?}", ty),
1831 Some((ret_type, param_types)) => {
1832 let ret_type = format_cc_type(ret_type, ir)?;
1833 let param_types = param_types
1834 .iter()
1835 .map(|t| format_cc_type(t, ir))
1836 .collect::<Result<Vec<_>>>()?;
1837 let attr = format_cc_call_conv_as_clang_attribute(abi)?;
1838 // `type_identity_t` is used below to avoid having to
1839 // emit spiral-like syntax where some syntax elements of
1840 // an inner type (e.g. function type as below) can
1841 // surround syntax elements of an outer type (e.g. a
1842 // pointer type). Compare: `int (*foo)(int, int)` VS
1843 // `type_identity_t<int(int, int)>* foo`.
1844 Ok(quote! { rs_api_impl_support::type_identity_t<
Devin Jeanpierre1b8f4a12022-03-22 21:29:42 +00001845 #ret_type ( #( #param_types ),* ) #attr
1846 > })
1847 }
1848 },
1849 },
Devin Jeanpierre7a7328e2021-09-17 07:10:08 +00001850 }
Marcel Hlopkoc0956cf2021-11-29 08:31:28 +00001851 } else {
Googler6a0a5252022-01-11 14:08:09 +00001852 let item = ir.item_for_type(ty)?;
1853 let type_name = cc_type_name_for_item(item)?;
1854 Ok(quote! {#const_fragment #type_name})
Devin Jeanpierre7a7328e2021-09-17 07:10:08 +00001855 }
1856}
1857
Marcel Hlopkoa0f38662021-12-03 08:45:26 +00001858fn cc_struct_layout_assertion(record: &Record, ir: &IR) -> TokenStream {
Googler6a0a5252022-01-11 14:08:09 +00001859 if !ir.is_current_target(&record.owning_target) && !ir.is_stdlib_target(&record.owning_target) {
Marcel Hlopkoa0f38662021-12-03 08:45:26 +00001860 return quote! {};
1861 }
Lukasz Anforowicz4c3a2cc2022-03-11 00:24:49 +00001862 let record_ident = format_cc_ident(&record.cc_name);
Googler5ea88642021-09-29 08:05:59 +00001863 let size = Literal::usize_unsuffixed(record.size);
1864 let alignment = Literal::usize_unsuffixed(record.alignment);
Lukasz Anforowicz74704712021-12-22 15:30:31 +00001865 let field_assertions =
1866 record.fields.iter().filter(|f| f.access == AccessSpecifier::Public).map(|field| {
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +00001867 let field_ident = format_cc_ident(&field.identifier.identifier);
Lukasz Anforowicz74704712021-12-22 15:30:31 +00001868 let offset = Literal::usize_unsuffixed(field.offset);
1869 // The IR contains the offset in bits, while C++'s offsetof()
1870 // returns the offset in bytes, so we need to convert.
1871 quote! {
Googler972d3582022-01-11 10:17:22 +00001872 static_assert(offsetof(class #record_ident, #field_ident) * 8 == #offset);
Lukasz Anforowicz74704712021-12-22 15:30:31 +00001873 }
1874 });
Googler5ea88642021-09-29 08:05:59 +00001875 quote! {
Googler972d3582022-01-11 10:17:22 +00001876 static_assert(sizeof(class #record_ident) == #size);
1877 static_assert(alignof(class #record_ident) == #alignment);
Googler5ea88642021-09-29 08:05:59 +00001878 #( #field_assertions )*
1879 }
1880}
1881
Devin Jeanpierre58181ac2022-02-14 21:30:05 +00001882// Returns the accessor functions for no_unique_address member variables.
1883fn cc_struct_no_unique_address_impl(record: &Record, ir: &IR) -> Result<TokenStream> {
1884 let mut fields = vec![];
1885 let mut types = vec![];
1886 for field in &record.fields {
1887 if field.access != AccessSpecifier::Public || !field.is_no_unique_address {
1888 continue;
1889 }
1890 fields.push(make_rs_ident(&field.identifier.identifier));
Devin Jeanpierrefec5bfe2022-04-05 10:59:10 -07001891 types.push(format_rs_type(&field.type_.rs_type, ir).with_context(|| {
1892 format!("Failed to format type for field {:?} on record {:?}", field, record)
1893 })?)
Devin Jeanpierre58181ac2022-02-14 21:30:05 +00001894 }
1895
1896 if fields.is_empty() {
1897 return Ok(quote! {});
1898 }
1899
Lukasz Anforowicz4c3a2cc2022-03-11 00:24:49 +00001900 let ident = make_rs_ident(&record.rs_name);
Devin Jeanpierre58181ac2022-02-14 21:30:05 +00001901 Ok(quote! {
1902 impl #ident {
1903 #(
1904 pub fn #fields(&self) -> &#types {
1905 unsafe {&* (&self.#fields as *const _ as *const #types)}
1906 }
1907 )*
1908 }
1909 })
1910}
1911
Devin Jeanpierre56777022022-02-03 01:57:15 +00001912/// Returns the implementation of base class conversions, for converting a type
1913/// to its unambiguous public base classes.
1914///
1915/// TODO(b/216195042): Implement this in terms of a supporting trait which casts
1916/// raw pointers. Then, we would have blanket impls for reference, pinned mut
1917/// reference, etc. conversion. The current version is just enough to test the
1918/// logic in importer.
1919//
1920// TODO(b/216195042): Should this use, like, AsRef/AsMut (and some equivalent
1921// for Pin)?
1922fn cc_struct_upcast_impl(record: &Record, ir: &IR) -> Result<TokenStream> {
1923 let mut impls = Vec::with_capacity(record.unambiguous_public_bases.len());
1924 for base in &record.unambiguous_public_bases {
Lukasz Anforowicz44047252022-03-23 13:04:48 +00001925 let base_record: &Record = ir
1926 .find_decl(base.base_record_id)
1927 .with_context(|| format!("Can't find a base record of {:?}", record))?;
Devin Jeanpierre56777022022-02-03 01:57:15 +00001928 if let Some(offset) = base.offset {
1929 let offset = Literal::i64_unsuffixed(offset);
1930 // TODO(b/216195042): Correctly handle imported records, lifetimes.
Lukasz Anforowicz4c3a2cc2022-03-11 00:24:49 +00001931 let base_name = make_rs_ident(&base_record.rs_name);
1932 let derived_name = make_rs_ident(&record.rs_name);
Devin Jeanpierre56777022022-02-03 01:57:15 +00001933 impls.push(quote! {
1934 impl<'a> From<&'a #derived_name> for &'a #base_name {
1935 fn from(x: &'a #derived_name) -> Self {
1936 unsafe {
1937 &*((x as *const _ as *const u8).offset(#offset) as *const #base_name)
1938 }
1939 }
1940 }
1941 });
1942 } else {
1943 // TODO(b/216195042): determine offset dynamically / use a dynamic
1944 // cast. This requires a new C++ function to be
1945 // generated, so that we have something to call.
1946 }
1947 }
1948
1949 Ok(quote! {
1950 #(#impls)*
1951 })
1952}
1953
Googlera675ae02021-12-07 08:04:59 +00001954fn thunk_ident(func: &Func) -> Ident {
1955 format_ident!("__rust_thunk__{}", func.mangled_name)
Devin Jeanpierref2ec8712021-10-13 20:47:16 +00001956}
1957
Marcel Hlopko89547752021-12-10 09:39:41 +00001958fn generate_rs_api_impl(ir: &IR) -> Result<TokenStream> {
Michael Forsterbee84482021-10-13 08:35:38 +00001959 // This function uses quote! to generate C++ source code out of convenience.
1960 // This is a bold idea so we have to continously evaluate if it still makes
1961 // sense or the cost of working around differences in Rust and C++ tokens is
1962 // greather than the value added.
Marcel Hlopko3164eee2021-08-24 20:09:22 +00001963 //
Michael Forsterbee84482021-10-13 08:35:38 +00001964 // See rs_bindings_from_cc/
1965 // token_stream_printer.rs for a list of supported placeholders.
Marcel Hlopko3164eee2021-08-24 20:09:22 +00001966 let mut thunks = vec![];
Michael Forster7ef80732021-10-01 18:12:19 +00001967 for func in ir.functions() {
Lukasz Anforowiczdd9ae0f2022-02-17 15:52:53 +00001968 if can_skip_cc_thunk(func) {
Marcel Hlopko3164eee2021-08-24 20:09:22 +00001969 continue;
1970 }
1971
Googlera675ae02021-12-07 08:04:59 +00001972 let thunk_ident = thunk_ident(func);
Devin Jeanpierref2ec8712021-10-13 20:47:16 +00001973 let implementation_function = match &func.name {
Lukasz Anforowicz9c663ca2022-02-09 01:33:31 +00001974 UnqualifiedIdentifier::Operator(op) => {
1975 let name = syn::parse_str::<TokenStream>(&op.name)?;
1976 quote! { operator #name }
1977 }
Devin Jeanpierref2ec8712021-10-13 20:47:16 +00001978 UnqualifiedIdentifier::Identifier(id) => {
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +00001979 let fn_ident = format_cc_ident(&id.identifier);
Lukasz Anforowiczaab8ad22021-12-19 20:29:26 +00001980 let static_method_metadata = func
1981 .member_func_metadata
1982 .as_ref()
1983 .filter(|meta| meta.instance_method_metadata.is_none());
1984 match static_method_metadata {
1985 None => quote! {#fn_ident},
1986 Some(meta) => {
Devin Jeanpierre1b8f4a12022-03-22 21:29:42 +00001987 let record_ident = format_cc_ident(&meta.find_record(ir)?.cc_name);
Lukasz Anforowiczaab8ad22021-12-19 20:29:26 +00001988 quote! { #record_ident :: #fn_ident }
1989 }
1990 }
Devin Jeanpierref2ec8712021-10-13 20:47:16 +00001991 }
Lukasz Anforowicz7b0042d2022-01-06 23:00:19 +00001992 // Use `destroy_at` to avoid needing to spell out the class name. Destructor identiifers
Devin Jeanpierrecc6cf092021-12-16 04:31:14 +00001993 // use the name of the type itself, without namespace qualification, template
1994 // parameters, or aliases. We do not need to use that naming scheme anywhere else in
1995 // the bindings, and it can be difficult (impossible?) to spell in the general case. By
1996 // using destroy_at, we avoid needing to determine or remember what the correct spelling
Lukasz Anforowicz7b0042d2022-01-06 23:00:19 +00001997 // is. Similar arguments apply to `construct_at`.
Lukasz Anforowicze643ec92021-12-22 15:45:15 +00001998 UnqualifiedIdentifier::Constructor => {
Lukasz Anforowicz7b0042d2022-01-06 23:00:19 +00001999 quote! { rs_api_impl_support::construct_at }
Lukasz Anforowicze643ec92021-12-22 15:45:15 +00002000 }
Devin Jeanpierref2ec8712021-10-13 20:47:16 +00002001 UnqualifiedIdentifier::Destructor => quote! {std::destroy_at},
Devin Jeanpierref2ec8712021-10-13 20:47:16 +00002002 };
Marcel Hlopkoc0956cf2021-11-29 08:31:28 +00002003 let return_type_name = format_cc_type(&func.return_type.cc_type, ir)?;
Lukasz Anforowicze643ec92021-12-22 15:45:15 +00002004 let return_stmt = if func.return_type.cc_type.is_void() {
2005 quote! {}
2006 } else {
2007 quote! { return }
2008 };
Marcel Hlopko3164eee2021-08-24 20:09:22 +00002009
2010 let param_idents =
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +00002011 func.params.iter().map(|p| format_cc_ident(&p.identifier.identifier)).collect_vec();
Marcel Hlopko3164eee2021-08-24 20:09:22 +00002012
Devin Jeanpierre09c6f452021-09-29 07:34:24 +00002013 let param_types = func
2014 .params
2015 .iter()
Marcel Hlopkoc0956cf2021-11-29 08:31:28 +00002016 .map(|p| format_cc_type(&p.type_.cc_type, ir))
Devin Jeanpierre09c6f452021-09-29 07:34:24 +00002017 .collect::<Result<Vec<_>>>()?;
Marcel Hlopko3164eee2021-08-24 20:09:22 +00002018
Lukasz Anforowiczb3d89aa2022-01-12 14:35:52 +00002019 let needs_this_deref = match &func.member_func_metadata {
2020 None => false,
2021 Some(meta) => match &func.name {
2022 UnqualifiedIdentifier::Constructor | UnqualifiedIdentifier::Destructor => false,
Marcel Hlopko14ee3c82022-02-09 09:46:23 +00002023 UnqualifiedIdentifier::Identifier(_) | UnqualifiedIdentifier::Operator(_) => {
2024 meta.instance_method_metadata.is_some()
2025 }
Lukasz Anforowiczb3d89aa2022-01-12 14:35:52 +00002026 },
2027 };
Devin Jeanpierredeea7892022-03-29 02:13:31 -07002028
2029 let arg_expressions: Vec<_> = param_idents
2030 .iter()
2031 .map(
2032 // Forward references along. (If the parameter is a value, not a reference, this
2033 // will create an lvalue reference, and still do the right thing.)
2034 |ident| quote! {std::forward<decltype(#ident)>(#ident)},
2035 )
2036 .collect();
Lukasz Anforowiczb3d89aa2022-01-12 14:35:52 +00002037 let (implementation_function, arg_expressions) = if !needs_this_deref {
Devin Jeanpierredeea7892022-03-29 02:13:31 -07002038 (implementation_function, arg_expressions.clone())
Lukasz Anforowiczb3d89aa2022-01-12 14:35:52 +00002039 } else {
2040 let this_param = func
2041 .params
2042 .first()
2043 .ok_or_else(|| anyhow!("Instance methods must have `__this` param."))?;
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +00002044 let this_arg = format_cc_ident(&this_param.identifier.identifier);
Lukasz Anforowiczb3d89aa2022-01-12 14:35:52 +00002045 (
2046 quote! { #this_arg -> #implementation_function},
Devin Jeanpierredeea7892022-03-29 02:13:31 -07002047 arg_expressions.iter().skip(1).cloned().collect_vec(),
Lukasz Anforowiczb3d89aa2022-01-12 14:35:52 +00002048 )
2049 };
2050
Marcel Hlopko3164eee2021-08-24 20:09:22 +00002051 thunks.push(quote! {
2052 extern "C" #return_type_name #thunk_ident( #( #param_types #param_idents ),* ) {
Lukasz Anforowiczb3d89aa2022-01-12 14:35:52 +00002053 #return_stmt #implementation_function( #( #arg_expressions ),* );
Marcel Hlopko3164eee2021-08-24 20:09:22 +00002054 }
2055 });
2056 }
2057
Marcel Hlopkoa0f38662021-12-03 08:45:26 +00002058 let layout_assertions = ir.records().map(|record| cc_struct_layout_assertion(record, ir));
Googler5ea88642021-09-29 08:05:59 +00002059
Devin Jeanpierre231ef8d2021-10-27 10:50:44 +00002060 let mut standard_headers = <BTreeSet<Ident>>::new();
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +00002061 standard_headers.insert(format_ident!("memory")); // ubiquitous.
Devin Jeanpierre231ef8d2021-10-27 10:50:44 +00002062 if ir.records().next().is_some() {
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +00002063 standard_headers.insert(format_ident!("cstddef"));
Devin Jeanpierre231ef8d2021-10-27 10:50:44 +00002064 };
Googler5ea88642021-09-29 08:05:59 +00002065
Devin Jeanpierre1b8f4a12022-03-22 21:29:42 +00002066 let mut includes = vec!["rs_bindings_from_cc/support/cxx20_backports.h"];
Lukasz Anforowicz4457baf2021-12-23 17:24:04 +00002067
Michael Forsterbee84482021-10-13 08:35:38 +00002068 // In order to generate C++ thunk in all the cases Clang needs to be able to
2069 // access declarations from public headers of the C++ library.
Lukasz Anforowicz4457baf2021-12-23 17:24:04 +00002070 includes.extend(ir.used_headers().map(|i| &i.name as &str));
Marcel Hlopko3164eee2021-08-24 20:09:22 +00002071
Marcel Hlopko89547752021-12-10 09:39:41 +00002072 Ok(quote! {
Googler5ea88642021-09-29 08:05:59 +00002073 #( __HASH_TOKEN__ include <#standard_headers> __NEWLINE__)*
Devin Jeanpierre7c74f842022-02-03 07:08:06 +00002074 __NEWLINE__
Michael Forsterdb8101a2021-10-08 06:56:03 +00002075 #( __HASH_TOKEN__ include #includes __NEWLINE__)* __NEWLINE__
Marcel Hlopkob8069ae2022-02-19 09:31:00 +00002076 __HASH_TOKEN__ pragma clang diagnostic push __NEWLINE__
2077 // Disable Clang thread-safety-analysis warnings that would otherwise
2078 // complain about thunks that call mutex locking functions in an unpaired way.
2079 __HASH_TOKEN__ pragma clang diagnostic ignored "-Wthread-safety-analysis" __NEWLINE__
Marcel Hlopko3164eee2021-08-24 20:09:22 +00002080
Michael Forsterdb8101a2021-10-08 06:56:03 +00002081 #( #thunks )* __NEWLINE__ __NEWLINE__
Googler5ea88642021-09-29 08:05:59 +00002082
Michael Forsterdb8101a2021-10-08 06:56:03 +00002083 #( #layout_assertions __NEWLINE__ __NEWLINE__ )*
Marcel Hlopkoc6b726c2021-10-07 06:53:09 +00002084
Marcel Hlopkob8069ae2022-02-19 09:31:00 +00002085 __NEWLINE__
2086 __HASH_TOKEN__ pragma clang diagnostic pop __NEWLINE__
Marcel Hlopkoc6b726c2021-10-07 06:53:09 +00002087 // To satisfy http://cs/symbol:devtools.metadata.Presubmit.CheckTerminatingNewline check.
2088 __NEWLINE__
Marcel Hlopko89547752021-12-10 09:39:41 +00002089 })
Marcel Hlopko3164eee2021-08-24 20:09:22 +00002090}
2091
Marcel Hlopko42abfc82021-08-09 07:03:17 +00002092#[cfg(test)]
2093mod tests {
Devin Jeanpierre45cb1162021-10-27 10:54:28 +00002094 use super::*;
Michael Forstered642022021-10-04 09:48:25 +00002095 use anyhow::anyhow;
Dmitri Gribenko67cbfd22022-03-24 13:39:34 +00002096 use ir_testing::{ir_from_cc, ir_from_cc_dependency, ir_record, retrieve_func};
Lukasz Anforowicz282bfd72022-03-22 22:38:17 +00002097 use static_assertions::{assert_impl_all, assert_not_impl_all};
Marcel Hlopko89547752021-12-10 09:39:41 +00002098 use token_stream_matchers::{
Lukasz Anforowicz71e9b592022-03-04 19:03:02 +00002099 assert_cc_matches, assert_cc_not_matches, assert_ir_matches, assert_rs_matches,
2100 assert_rs_not_matches,
Devin Jeanpierre7e9a1de2021-12-03 08:04:22 +00002101 };
Michael Forsterdb8101a2021-10-08 06:56:03 +00002102 use token_stream_printer::tokens_to_string;
Marcel Hlopko42abfc82021-08-09 07:03:17 +00002103
2104 #[test]
Marcel Hlopkob8069ae2022-02-19 09:31:00 +00002105 fn test_disable_thread_safety_warnings() -> Result<()> {
2106 let ir = ir_from_cc("inline void foo() {}")?;
2107 let rs_api_impl = generate_rs_api_impl(&ir)?;
2108 assert_cc_matches!(
2109 rs_api_impl,
2110 quote! {
2111 ...
2112 __HASH_TOKEN__ pragma clang diagnostic push
2113 __HASH_TOKEN__ pragma clang diagnostic ignored "-Wthread-safety-analysis"
2114 ...
2115
2116 __HASH_TOKEN__ pragma clang diagnostic pop
2117 ...
2118 }
2119 );
2120 Ok(())
2121 }
2122
2123 #[test]
Marcel Hlopko3b9bf9e2021-11-29 08:25:14 +00002124 // TODO(hlopko): Move this test to a more principled place where it can access
2125 // `ir_testing`.
2126 fn test_duplicate_decl_ids_err() {
2127 let mut r1 = ir_record("R1");
Rosica Dejanovskad638cf52022-03-23 15:45:01 +00002128 r1.id = ItemId(42);
Marcel Hlopko3b9bf9e2021-11-29 08:25:14 +00002129 let mut r2 = ir_record("R2");
Rosica Dejanovskad638cf52022-03-23 15:45:01 +00002130 r2.id = ItemId(42);
Marcel Hlopko3b9bf9e2021-11-29 08:25:14 +00002131 let result = make_ir_from_items([r1.into(), r2.into()]);
2132 assert!(result.is_err());
2133 assert!(result.unwrap_err().to_string().contains("Duplicate decl_id found in"));
2134 }
2135
2136 #[test]
Marcel Hlopko45fba972021-08-23 19:52:20 +00002137 fn test_simple_function() -> Result<()> {
Marcel Hlopko89547752021-12-10 09:39:41 +00002138 let ir = ir_from_cc("int Add(int a, int b);")?;
2139 let rs_api = generate_rs_api(&ir)?;
2140 assert_rs_matches!(
2141 rs_api,
2142 quote! {
Michael Forsterdb8101a2021-10-08 06:56:03 +00002143 #[inline(always)]
Marcel Hlopko89547752021-12-10 09:39:41 +00002144 pub fn Add(a: i32, b: i32) -> i32 {
Googlera675ae02021-12-07 08:04:59 +00002145 unsafe { crate::detail::__rust_thunk___Z3Addii(a, b) }
Marcel Hlopko89547752021-12-10 09:39:41 +00002146 }
2147 }
2148 );
2149 assert_rs_matches!(
2150 rs_api,
2151 quote! {
Michael Forsterdb8101a2021-10-08 06:56:03 +00002152 mod detail {
Googler55647142022-01-11 12:37:39 +00002153 #[allow(unused_imports)]
Devin Jeanpierred4dde0e2021-10-13 20:48:25 +00002154 use super::*;
Michael Forsterdb8101a2021-10-08 06:56:03 +00002155 extern "C" {
2156 #[link_name = "_Z3Addii"]
Googlera675ae02021-12-07 08:04:59 +00002157 pub(crate) fn __rust_thunk___Z3Addii(a: i32, b: i32) -> i32;
Marcel Hlopko89547752021-12-10 09:39:41 +00002158 }
2159 }
2160 }
Marcel Hlopko42abfc82021-08-09 07:03:17 +00002161 );
Michael Forsterdb8101a2021-10-08 06:56:03 +00002162
Marcel Hlopko89547752021-12-10 09:39:41 +00002163 assert_cc_not_matches!(generate_rs_api_impl(&ir)?, quote! {__rust_thunk___Z3Addii});
Michael Forsterdb8101a2021-10-08 06:56:03 +00002164
Marcel Hlopko3164eee2021-08-24 20:09:22 +00002165 Ok(())
2166 }
2167
2168 #[test]
2169 fn test_inline_function() -> Result<()> {
Marcel Hlopko89547752021-12-10 09:39:41 +00002170 let ir = ir_from_cc("inline int Add(int a, int b);")?;
2171 let rs_api = generate_rs_api(&ir)?;
2172 assert_rs_matches!(
2173 rs_api,
2174 quote! {
Michael Forsterdb8101a2021-10-08 06:56:03 +00002175 #[inline(always)]
Marcel Hlopko89547752021-12-10 09:39:41 +00002176 pub fn Add(a: i32, b: i32) -> i32 {
Googlera675ae02021-12-07 08:04:59 +00002177 unsafe { crate::detail::__rust_thunk___Z3Addii(a, b) }
Marcel Hlopko89547752021-12-10 09:39:41 +00002178 }
2179 }
2180 );
2181 assert_rs_matches!(
2182 rs_api,
2183 quote! {
Michael Forsterdb8101a2021-10-08 06:56:03 +00002184 mod detail {
Googler55647142022-01-11 12:37:39 +00002185 #[allow(unused_imports)]
Devin Jeanpierred4dde0e2021-10-13 20:48:25 +00002186 use super::*;
Michael Forsterdb8101a2021-10-08 06:56:03 +00002187 extern "C" {
Googlera675ae02021-12-07 08:04:59 +00002188 pub(crate) fn __rust_thunk___Z3Addii(a: i32, b: i32) -> i32;
Marcel Hlopko89547752021-12-10 09:39:41 +00002189 }
2190 }
2191 }
Marcel Hlopko3164eee2021-08-24 20:09:22 +00002192 );
2193
Marcel Hlopko89547752021-12-10 09:39:41 +00002194 assert_cc_matches!(
2195 generate_rs_api_impl(&ir)?,
2196 quote! {
Googlera675ae02021-12-07 08:04:59 +00002197 extern "C" int __rust_thunk___Z3Addii(int a, int b) {
Devin Jeanpierredeea7892022-03-29 02:13:31 -07002198 return Add(std::forward<decltype(a)>(a), std::forward<decltype(b)>(b));
Marcel Hlopko3164eee2021-08-24 20:09:22 +00002199 }
Marcel Hlopko89547752021-12-10 09:39:41 +00002200 }
Marcel Hlopko3164eee2021-08-24 20:09:22 +00002201 );
Marcel Hlopko42abfc82021-08-09 07:03:17 +00002202 Ok(())
2203 }
Marcel Hlopkob4b28742021-09-15 12:45:20 +00002204
2205 #[test]
Marcel Hlopkoa0f38662021-12-03 08:45:26 +00002206 fn test_simple_function_with_types_from_other_target() -> Result<()> {
2207 let ir = ir_from_cc_dependency(
2208 "inline ReturnStruct DoSomething(ParamStruct param);",
2209 "struct ReturnStruct {}; struct ParamStruct {};",
2210 )?;
2211
Marcel Hlopko89547752021-12-10 09:39:41 +00002212 let rs_api = generate_rs_api(&ir)?;
2213 assert_rs_matches!(
2214 rs_api,
2215 quote! {
Marcel Hlopkoa0f38662021-12-03 08:45:26 +00002216 #[inline(always)]
2217 pub fn DoSomething(param: dependency::ParamStruct)
Marcel Hlopko89547752021-12-10 09:39:41 +00002218 -> dependency::ReturnStruct {
Googlera675ae02021-12-07 08:04:59 +00002219 unsafe { crate::detail::__rust_thunk___Z11DoSomething11ParamStruct(param) }
Marcel Hlopko89547752021-12-10 09:39:41 +00002220 }
2221 }
2222 );
2223 assert_rs_matches!(
2224 rs_api,
2225 quote! {
2226 mod detail {
Googler55647142022-01-11 12:37:39 +00002227 #[allow(unused_imports)]
Marcel Hlopko89547752021-12-10 09:39:41 +00002228 use super::*;
2229 extern "C" {
2230 pub(crate) fn __rust_thunk___Z11DoSomething11ParamStruct(param: dependency::ParamStruct)
2231 -> dependency::ReturnStruct;
2232 }
2233 }}
Marcel Hlopkoa0f38662021-12-03 08:45:26 +00002234 );
2235
Marcel Hlopko89547752021-12-10 09:39:41 +00002236 assert_cc_matches!(
2237 generate_rs_api_impl(&ir)?,
2238 quote! {
Googler972d3582022-01-11 10:17:22 +00002239 extern "C" class ReturnStruct __rust_thunk___Z11DoSomething11ParamStruct(class ParamStruct param) {
Devin Jeanpierredeea7892022-03-29 02:13:31 -07002240 return DoSomething(std::forward<decltype(param)>(param));
Marcel Hlopkoa0f38662021-12-03 08:45:26 +00002241 }
Marcel Hlopko89547752021-12-10 09:39:41 +00002242 }
Marcel Hlopkoa0f38662021-12-03 08:45:26 +00002243 );
2244 Ok(())
2245 }
2246
2247 #[test]
Marcel Hlopkob4b28742021-09-15 12:45:20 +00002248 fn test_simple_struct() -> Result<()> {
Marcel Hlopko89547752021-12-10 09:39:41 +00002249 let ir = ir_from_cc(&tokens_to_string(quote! {
Devin Jeanpierre88343c72022-01-15 01:10:23 +00002250 struct SomeStruct final {
Marcel Hlopko89547752021-12-10 09:39:41 +00002251 int public_int;
2252 protected:
2253 int protected_int;
2254 private:
2255 int private_int;
2256 };
2257 })?)?;
Michael Forster028800b2021-10-05 12:39:59 +00002258
Marcel Hlopko89547752021-12-10 09:39:41 +00002259 let rs_api = generate_rs_api(&ir)?;
2260 assert_rs_matches!(
2261 rs_api,
2262 quote! {
Michael Forsterdb8101a2021-10-08 06:56:03 +00002263 #[derive(Clone, Copy)]
2264 #[repr(C)]
2265 pub struct SomeStruct {
2266 pub public_int: i32,
2267 protected_int: i32,
2268 private_int: i32,
Marcel Hlopko89547752021-12-10 09:39:41 +00002269 }
2270 }
2271 );
2272 assert_rs_matches!(
2273 rs_api,
2274 quote! {
Rosica Dejanovskaaccf00b2022-04-01 13:28:04 -07002275 const _: () = assert!(rust_std::mem::size_of::<Option<&i32>>() == rust_std::mem::size_of::<&i32>());
2276 const _: () = assert!(rust_std::mem::size_of::<SomeStruct>() == 12usize);
2277 const _: () = assert!(rust_std::mem::align_of::<SomeStruct>() == 4usize);
Lukasz Anforowicz95938f82022-03-24 13:51:54 +00002278 const _: () = { assert_impl_all!(SomeStruct: Clone); };
2279 const _: () = { assert_impl_all!(SomeStruct: Copy); };
2280 const _: () = { assert_not_impl_all!(SomeStruct: Drop); };
Googler209b10a2021-12-06 09:11:57 +00002281 const _: () = assert!(offset_of!(SomeStruct, public_int) * 8 == 0usize);
2282 const _: () = assert!(offset_of!(SomeStruct, protected_int) * 8 == 32usize);
2283 const _: () = assert!(offset_of!(SomeStruct, private_int) * 8 == 64usize);
Marcel Hlopko89547752021-12-10 09:39:41 +00002284 }
Marcel Hlopkob4b28742021-09-15 12:45:20 +00002285 );
Marcel Hlopko89547752021-12-10 09:39:41 +00002286 let rs_api_impl = generate_rs_api_impl(&ir)?;
2287 assert_cc_matches!(
2288 rs_api_impl,
2289 quote! {
Googler972d3582022-01-11 10:17:22 +00002290 extern "C" void __rust_thunk___ZN10SomeStructD1Ev(class SomeStruct * __this) {
Devin Jeanpierredeea7892022-03-29 02:13:31 -07002291 std :: destroy_at (std::forward<decltype(__this)>(__this)) ;
Marcel Hlopko89547752021-12-10 09:39:41 +00002292 }
2293 }
2294 );
2295 assert_cc_matches!(
2296 rs_api_impl,
2297 quote! {
Googler972d3582022-01-11 10:17:22 +00002298 static_assert(sizeof(class SomeStruct) == 12);
2299 static_assert(alignof(class SomeStruct) == 4);
2300 static_assert(offsetof(class SomeStruct, public_int) * 8 == 0);
Marcel Hlopko89547752021-12-10 09:39:41 +00002301 }
Googler5ea88642021-09-29 08:05:59 +00002302 );
Marcel Hlopkob4b28742021-09-15 12:45:20 +00002303 Ok(())
2304 }
Devin Jeanpierre7a7328e2021-09-17 07:10:08 +00002305
Devin Jeanpierre2ed14ec2021-10-06 11:32:19 +00002306 #[test]
Lukasz Anforowicz275fa922022-01-05 16:13:20 +00002307 fn test_ref_to_struct_in_thunk_impls() -> Result<()> {
Googler972d3582022-01-11 10:17:22 +00002308 let ir = ir_from_cc("struct S{}; inline void foo(class S& s) {} ")?;
Lukasz Anforowicz275fa922022-01-05 16:13:20 +00002309 let rs_api_impl = generate_rs_api_impl(&ir)?;
2310 assert_cc_matches!(
2311 rs_api_impl,
2312 quote! {
Googler972d3582022-01-11 10:17:22 +00002313 extern "C" void __rust_thunk___Z3fooR1S(class S& s) {
Devin Jeanpierredeea7892022-03-29 02:13:31 -07002314 foo(std::forward<decltype(s)>(s));
Lukasz Anforowicz275fa922022-01-05 16:13:20 +00002315 }
2316 }
2317 );
2318 Ok(())
2319 }
2320
2321 #[test]
2322 fn test_const_ref_to_struct_in_thunk_impls() -> Result<()> {
Googler972d3582022-01-11 10:17:22 +00002323 let ir = ir_from_cc("struct S{}; inline void foo(const class S& s) {} ")?;
Lukasz Anforowicz275fa922022-01-05 16:13:20 +00002324 let rs_api_impl = generate_rs_api_impl(&ir)?;
2325 assert_cc_matches!(
2326 rs_api_impl,
2327 quote! {
Googler972d3582022-01-11 10:17:22 +00002328 extern "C" void __rust_thunk___Z3fooRK1S(const class S& s) {
Devin Jeanpierredeea7892022-03-29 02:13:31 -07002329 foo(std::forward<decltype(s)>(s));
Lukasz Anforowicz275fa922022-01-05 16:13:20 +00002330 }
2331 }
2332 );
2333 Ok(())
2334 }
2335
2336 #[test]
Lukasz Anforowicz957cbf22022-01-05 16:14:05 +00002337 fn test_unsigned_int_in_thunk_impls() -> Result<()> {
2338 let ir = ir_from_cc("inline void foo(unsigned int i) {} ")?;
2339 let rs_api_impl = generate_rs_api_impl(&ir)?;
2340 assert_cc_matches!(
2341 rs_api_impl,
2342 quote! {
2343 extern "C" void __rust_thunk___Z3fooj(unsigned int i) {
Devin Jeanpierredeea7892022-03-29 02:13:31 -07002344 foo(std::forward<decltype(i)>(i));
Lukasz Anforowicz957cbf22022-01-05 16:14:05 +00002345 }
2346 }
2347 );
2348 Ok(())
2349 }
2350
2351 #[test]
Marcel Hlopkodd1fcb12021-12-22 14:13:59 +00002352 fn test_record_static_methods_qualify_call_in_thunk() -> Result<()> {
2353 let ir = ir_from_cc(&tokens_to_string(quote! {
2354 struct SomeStruct {
2355 static inline int some_func() { return 42; }
2356 };
2357 })?)?;
2358
2359 assert_cc_matches!(
2360 generate_rs_api_impl(&ir)?,
2361 quote! {
2362 extern "C" int __rust_thunk___ZN10SomeStruct9some_funcEv() {
2363 return SomeStruct::some_func();
2364 }
2365 }
2366 );
2367 Ok(())
2368 }
2369
2370 #[test]
Lukasz Anforowiczb3d89aa2022-01-12 14:35:52 +00002371 fn test_record_instance_methods_deref_this_in_thunk() -> Result<()> {
2372 let ir = ir_from_cc(&tokens_to_string(quote! {
2373 struct SomeStruct {
2374 inline int some_func(int arg) const { return 42 + arg; }
2375 };
2376 })?)?;
2377
2378 assert_cc_matches!(
2379 generate_rs_api_impl(&ir)?,
2380 quote! {
2381 extern "C" int __rust_thunk___ZNK10SomeStruct9some_funcEi(
2382 const class SomeStruct* __this, int arg) {
Devin Jeanpierredeea7892022-03-29 02:13:31 -07002383 return __this->some_func(std::forward<decltype(arg)>(arg));
Lukasz Anforowiczb3d89aa2022-01-12 14:35:52 +00002384 }
2385 }
2386 );
2387 Ok(())
2388 }
2389
2390 #[test]
Marcel Hlopkoa0f38662021-12-03 08:45:26 +00002391 fn test_struct_from_other_target() -> Result<()> {
2392 let ir = ir_from_cc_dependency("// intentionally empty", "struct SomeStruct {};")?;
Marcel Hlopko89547752021-12-10 09:39:41 +00002393 assert_rs_not_matches!(generate_rs_api(&ir)?, quote! { SomeStruct });
2394 assert_cc_not_matches!(generate_rs_api_impl(&ir)?, quote! { SomeStruct });
Marcel Hlopkoa0f38662021-12-03 08:45:26 +00002395 Ok(())
2396 }
2397
2398 #[test]
Devin Jeanpierre2ed14ec2021-10-06 11:32:19 +00002399 fn test_copy_derives() {
Devin Jeanpierreccfefc82021-10-27 10:54:00 +00002400 let record = ir_record("S");
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00002401 assert_eq!(generate_derives(&record), &["Clone", "Copy"]);
Devin Jeanpierre2ed14ec2021-10-06 11:32:19 +00002402 }
2403
2404 #[test]
2405 fn test_copy_derives_not_is_trivial_abi() {
Devin Jeanpierreccfefc82021-10-27 10:54:00 +00002406 let mut record = ir_record("S");
Devin Jeanpierre2ed14ec2021-10-06 11:32:19 +00002407 record.is_trivial_abi = false;
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00002408 assert_eq!(generate_derives(&record), &[""; 0]);
Devin Jeanpierre2ed14ec2021-10-06 11:32:19 +00002409 }
2410
Devin Jeanpierre88343c72022-01-15 01:10:23 +00002411 /// Even if it's trivially relocatable, !Unpin C++ type cannot be
2412 /// cloned/copied or otherwise used by value, because values would allow
2413 /// assignment into the Pin.
2414 ///
2415 /// All !Unpin C++ types, not just non trivially relocatable ones, are
2416 /// unsafe to assign in the Rust sense.
Devin Jeanpierree6e16652021-12-22 15:54:46 +00002417 #[test]
Devin Jeanpierre88343c72022-01-15 01:10:23 +00002418 fn test_copy_derives_not_final() {
Devin Jeanpierree6e16652021-12-22 15:54:46 +00002419 let mut record = ir_record("S");
Teddy Katzd2cd1422022-04-04 09:41:33 -07002420 record.is_inheritable = true;
Devin Jeanpierre88343c72022-01-15 01:10:23 +00002421 assert_eq!(generate_derives(&record), &[""; 0]);
Devin Jeanpierree6e16652021-12-22 15:54:46 +00002422 }
2423
Devin Jeanpierre2ed14ec2021-10-06 11:32:19 +00002424 #[test]
2425 fn test_copy_derives_ctor_nonpublic() {
Devin Jeanpierreccfefc82021-10-27 10:54:00 +00002426 let mut record = ir_record("S");
Devin Jeanpierre2ed14ec2021-10-06 11:32:19 +00002427 for access in [ir::AccessSpecifier::Protected, ir::AccessSpecifier::Private] {
2428 record.copy_constructor.access = access;
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00002429 assert_eq!(generate_derives(&record), &[""; 0]);
Devin Jeanpierre2ed14ec2021-10-06 11:32:19 +00002430 }
2431 }
2432
2433 #[test]
2434 fn test_copy_derives_ctor_deleted() {
Devin Jeanpierreccfefc82021-10-27 10:54:00 +00002435 let mut record = ir_record("S");
Devin Jeanpierre2ed14ec2021-10-06 11:32:19 +00002436 record.copy_constructor.definition = ir::SpecialMemberDefinition::Deleted;
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00002437 assert_eq!(generate_derives(&record), &[""; 0]);
Devin Jeanpierre2ed14ec2021-10-06 11:32:19 +00002438 }
2439
2440 #[test]
Devin Jeanpierrebe2f33b2021-10-21 12:54:19 +00002441 fn test_copy_derives_ctor_nontrivial_members() {
Devin Jeanpierreccfefc82021-10-27 10:54:00 +00002442 let mut record = ir_record("S");
Devin Jeanpierrebe2f33b2021-10-21 12:54:19 +00002443 record.copy_constructor.definition = ir::SpecialMemberDefinition::NontrivialMembers;
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00002444 assert_eq!(generate_derives(&record), &[""; 0]);
Devin Jeanpierrebe2f33b2021-10-21 12:54:19 +00002445 }
2446
2447 #[test]
2448 fn test_copy_derives_ctor_nontrivial_self() {
Devin Jeanpierreccfefc82021-10-27 10:54:00 +00002449 let mut record = ir_record("S");
Devin Jeanpierre7b62e952021-12-08 21:43:30 +00002450 record.copy_constructor.definition = ir::SpecialMemberDefinition::NontrivialUserDefined;
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00002451 assert_eq!(generate_derives(&record), &[""; 0]);
Devin Jeanpierre2ed14ec2021-10-06 11:32:19 +00002452 }
2453
Devin Jeanpierre7a7328e2021-09-17 07:10:08 +00002454 #[test]
2455 fn test_ptr_func() -> Result<()> {
Marcel Hlopko89547752021-12-10 09:39:41 +00002456 let ir = ir_from_cc(&tokens_to_string(quote! {
2457 inline int* Deref(int*const* p);
2458 })?)?;
Devin Jeanpierred6da7002021-10-21 12:55:20 +00002459
Marcel Hlopko89547752021-12-10 09:39:41 +00002460 let rs_api = generate_rs_api(&ir)?;
2461 assert_rs_matches!(
2462 rs_api,
2463 quote! {
Michael Forsterdb8101a2021-10-08 06:56:03 +00002464 #[inline(always)]
Lukasz Anforowiczf7bdd392022-01-21 00:33:39 +00002465 pub unsafe fn Deref(p: *const *mut i32) -> *mut i32 {
2466 crate::detail::__rust_thunk___Z5DerefPKPi(p)
Marcel Hlopko89547752021-12-10 09:39:41 +00002467 }
2468 }
2469 );
2470 assert_rs_matches!(
2471 rs_api,
2472 quote! {
Michael Forsterdb8101a2021-10-08 06:56:03 +00002473 mod detail {
Googler55647142022-01-11 12:37:39 +00002474 #[allow(unused_imports)]
Devin Jeanpierred4dde0e2021-10-13 20:48:25 +00002475 use super::*;
Michael Forsterdb8101a2021-10-08 06:56:03 +00002476 extern "C" {
Googlera675ae02021-12-07 08:04:59 +00002477 pub(crate) fn __rust_thunk___Z5DerefPKPi(p: *const *mut i32) -> *mut i32;
Marcel Hlopko89547752021-12-10 09:39:41 +00002478 }
2479 }
2480 }
Devin Jeanpierre7a7328e2021-09-17 07:10:08 +00002481 );
Devin Jeanpierre184f9ac2021-09-17 13:47:03 +00002482
Marcel Hlopko89547752021-12-10 09:39:41 +00002483 assert_cc_matches!(
2484 generate_rs_api_impl(&ir)?,
2485 quote! {
Googlera675ae02021-12-07 08:04:59 +00002486 extern "C" int* __rust_thunk___Z5DerefPKPi(int* const * p) {
Devin Jeanpierredeea7892022-03-29 02:13:31 -07002487 return Deref(std::forward<decltype(p)>(p));
Devin Jeanpierre184f9ac2021-09-17 13:47:03 +00002488 }
Marcel Hlopko89547752021-12-10 09:39:41 +00002489 }
Devin Jeanpierre184f9ac2021-09-17 13:47:03 +00002490 );
Devin Jeanpierre7a7328e2021-09-17 07:10:08 +00002491 Ok(())
2492 }
Michael Forstered642022021-10-04 09:48:25 +00002493
2494 #[test]
Googlerdb111532022-01-05 06:12:13 +00002495 fn test_const_char_ptr_func() -> Result<()> {
2496 // This is a regression test: We used to include the "const" in the name
2497 // of the CcType, which caused a panic in the code generator
2498 // ('"const char" is not a valid Ident').
2499 // It's therefore important that f() is inline so that we need to
2500 // generate a thunk for it (where we then process the CcType).
2501 let ir = ir_from_cc(&tokens_to_string(quote! {
2502 inline void f(const char *str);
2503 })?)?;
2504
2505 let rs_api = generate_rs_api(&ir)?;
2506 assert_rs_matches!(
2507 rs_api,
2508 quote! {
2509 #[inline(always)]
Lukasz Anforowiczf7bdd392022-01-21 00:33:39 +00002510 pub unsafe fn f(str: *const i8) {
2511 crate::detail::__rust_thunk___Z1fPKc(str)
Googlerdb111532022-01-05 06:12:13 +00002512 }
2513 }
2514 );
2515 assert_rs_matches!(
2516 rs_api,
2517 quote! {
2518 extern "C" {
2519 pub(crate) fn __rust_thunk___Z1fPKc(str: *const i8);
2520 }
2521 }
2522 );
2523
2524 assert_cc_matches!(
2525 generate_rs_api_impl(&ir)?,
2526 quote! {
Devin Jeanpierredeea7892022-03-29 02:13:31 -07002527 extern "C" void __rust_thunk___Z1fPKc(char const * str){ f(std::forward<decltype(str)>(str)) ; }
Googlerdb111532022-01-05 06:12:13 +00002528 }
2529 );
2530 Ok(())
2531 }
2532
2533 #[test]
Lukasz Anforowiczcf230fd2022-02-18 19:20:39 +00002534 fn test_func_ptr_where_params_are_primitive_types() -> Result<()> {
2535 let ir = ir_from_cc(r#" int (*get_ptr_to_func())(float, double); "#)?;
2536 let rs_api = generate_rs_api(&ir)?;
2537 let rs_api_impl = generate_rs_api_impl(&ir)?;
2538 assert_rs_matches!(
2539 rs_api,
2540 quote! {
2541 #[inline(always)]
2542 pub fn get_ptr_to_func() -> Option<extern "C" fn (f32, f64) -> i32> {
2543 unsafe { crate::detail::__rust_thunk___Z15get_ptr_to_funcv() }
2544 }
2545 }
2546 );
2547 assert_rs_matches!(
2548 rs_api,
2549 quote! {
2550 mod detail {
2551 #[allow(unused_imports)]
2552 use super::*;
2553 extern "C" {
2554 #[link_name = "_Z15get_ptr_to_funcv"]
2555 pub(crate) fn __rust_thunk___Z15get_ptr_to_funcv()
2556 -> Option<extern "C" fn(f32, f64) -> i32>;
2557 }
2558 }
2559 }
2560 );
2561 // Verify that no C++ thunk got generated.
2562 assert_cc_not_matches!(rs_api_impl, quote! { __rust_thunk___Z15get_ptr_to_funcv });
2563
2564 // TODO(b/217419782): Add another test for more exotic calling conventions /
2565 // abis.
2566
2567 // TODO(b/217419782): Add another test for pointer to a function that
2568 // takes/returns non-trivially-movable types by value. See also
2569 // <internal link>
2570
2571 Ok(())
2572 }
2573
2574 #[test]
Lukasz Anforowicz92c81c32022-03-04 19:03:56 +00002575 fn test_func_ref() -> Result<()> {
2576 let ir = ir_from_cc(r#" int (&get_ref_to_func())(float, double); "#)?;
2577 let rs_api = generate_rs_api(&ir)?;
2578 assert_rs_matches!(
2579 rs_api,
2580 quote! {
2581 #[inline(always)]
2582 pub fn get_ref_to_func() -> extern "C" fn (f32, f64) -> i32 {
2583 unsafe { crate::detail::__rust_thunk___Z15get_ref_to_funcv() }
2584 }
2585 }
2586 );
2587 Ok(())
2588 }
2589
2590 #[test]
Lukasz Anforowiczcf230fd2022-02-18 19:20:39 +00002591 fn test_func_ptr_with_non_static_lifetime() -> Result<()> {
2592 let ir = ir_from_cc(
2593 r#"
Googler53f65942022-02-23 11:23:30 +00002594 [[clang::annotate("lifetimes", "-> a")]]
Lukasz Anforowiczcf230fd2022-02-18 19:20:39 +00002595 int (*get_ptr_to_func())(float, double); "#,
2596 )?;
2597 let rs_api = generate_rs_api(&ir)?;
2598 assert_rs_matches!(
2599 rs_api,
2600 quote! {
2601 // Error while generating bindings for item 'get_ptr_to_func':
2602 // Return type is not supported: Function pointers with non-'static lifetimes are not supported: int (*)(float, double)
2603 }
2604 );
2605 Ok(())
2606 }
2607
2608 #[test]
2609 fn test_func_ptr_where_params_are_raw_ptrs() -> Result<()> {
2610 let ir = ir_from_cc(r#" const int* (*get_ptr_to_func())(const int*); "#)?;
2611 let rs_api = generate_rs_api(&ir)?;
2612 let rs_api_impl = generate_rs_api_impl(&ir)?;
2613 assert_rs_matches!(
2614 rs_api,
2615 quote! {
2616 #[inline(always)]
2617 pub fn get_ptr_to_func() -> Option<extern "C" fn (*const i32) -> *const i32> {
2618 unsafe { crate::detail::__rust_thunk___Z15get_ptr_to_funcv() }
2619 }
2620 }
2621 );
2622 assert_rs_matches!(
2623 rs_api,
2624 quote! {
2625 mod detail {
2626 #[allow(unused_imports)]
2627 use super::*;
2628 extern "C" {
2629 #[link_name = "_Z15get_ptr_to_funcv"]
2630 pub(crate) fn __rust_thunk___Z15get_ptr_to_funcv()
2631 -> Option<extern "C" fn(*const i32) -> *const i32>;
2632 }
2633 }
2634 }
2635 );
2636 // Verify that no C++ thunk got generated.
2637 assert_cc_not_matches!(rs_api_impl, quote! { __rust_thunk___Z15get_ptr_to_funcv });
2638
2639 // TODO(b/217419782): Add another test where params (and the return
2640 // type) are references with lifetimes. Something like this:
2641 // #pragma clang lifetime_elision
2642 // const int& (*get_ptr_to_func())(const int&, const int&); "#)?;
2643 // 1) Need to investigate why this fails - seeing raw pointers in Rust
2644 // seems to indicate that no lifetimes are present at the `importer.cc`
2645 // level. Maybe lifetime elision doesn't support this scenario? Unclear
Googler53f65942022-02-23 11:23:30 +00002646 // how to explicitly apply [[clang::annotate("lifetimes", "a, b -> a")]]
Lukasz Anforowiczcf230fd2022-02-18 19:20:39 +00002647 // to the _inner_ function.
2648 // 2) It is important to have 2 reference parameters, so see if the problem
2649 // of passing `lifetimes` by value would have been caught - see:
2650 // cl/428079010/depot/rs_bindings_from_cc/
2651 // importer.cc?version=s6#823
2652
2653 // TODO(b/217419782): Decide what to do if the C++ pointer is *not*
2654 // annotated with a lifetime - emit `unsafe fn(...) -> ...` in that
2655 // case?
2656
2657 Ok(())
2658 }
2659
2660 #[test]
Lukasz Anforowicz71e9b592022-03-04 19:03:02 +00002661 fn test_func_ptr_with_custom_abi() -> Result<()> {
2662 let ir = ir_from_cc(r#" int (*get_ptr_to_func())(float, double) [[clang::vectorcall]]; "#)?;
2663
2664 // Verify that the test input correctly represents what we intend to
2665 // test - we want [[clang::vectorcall]] to apply to the returned
2666 // function pointer, but *not* apply to the `get_ptr_to_func` function.
Lukasz Anforowicz71e9b592022-03-04 19:03:02 +00002667 assert_ir_matches!(
2668 ir,
2669 quote! {
2670 Func(Func {
2671 name: "get_ptr_to_func", ...
2672 return_type: MappedType {
2673 rs_type: RsType {
2674 name: Some("Option"), ...
2675 type_args: [RsType { name: Some("#funcPtr vectorcall"), ... }], ...
2676 },
2677 cc_type: CcType {
2678 name: Some("*"), ...
2679 type_args: [CcType { name: Some("#funcValue vectorcall"), ... }], ...
2680 },
Lukasz Anforowicz0b6a6ac2022-03-22 22:32:23 +00002681 }, ...
2682 has_c_calling_convention: true, ...
Lukasz Anforowicz71e9b592022-03-04 19:03:02 +00002683 }),
2684 }
2685 );
2686
2687 // Check that the custom "vectorcall" ABI gets propagated into the
2688 // return type (i.e. into `extern "vectorcall" fn`).
2689 let rs_api = generate_rs_api(&ir)?;
2690 assert_rs_matches!(
2691 rs_api,
2692 quote! {
2693 #[inline(always)]
2694 pub fn get_ptr_to_func() -> Option<extern "vectorcall" fn (f32, f64) -> i32> {
2695 unsafe { crate::detail::__rust_thunk___Z15get_ptr_to_funcv() }
2696 }
2697 }
2698 );
2699
2700 // The usual `extern "C"` ABI should be used for "get_ptr_to_func".
2701 assert_rs_matches!(
2702 rs_api,
2703 quote! {
2704 mod detail {
2705 #[allow(unused_imports)]
2706 use super::*;
2707 extern "C" {
2708 #[link_name = "_Z15get_ptr_to_funcv"]
2709 pub(crate) fn __rust_thunk___Z15get_ptr_to_funcv()
2710 -> Option<extern "vectorcall" fn(f32, f64) -> i32>;
2711 }
2712 }
2713 }
2714 );
2715
2716 // Verify that no C++ thunk got generated.
2717 let rs_api_impl = generate_rs_api_impl(&ir)?;
2718 assert_cc_not_matches!(rs_api_impl, quote! { __rust_thunk___Z15get_ptr_to_funcv });
2719 Ok(())
2720 }
2721
2722 #[test]
2723 fn test_func_ptr_thunk() -> Result<()> {
2724 // Using an `inline` keyword forces generation of a C++ thunk in
2725 // `rs_api_impl` (i.e. exercises `format_cc_type` and similar code).
2726 let ir = ir_from_cc(
2727 r#"
2728 int multiply(int x, int y);
2729 inline int (*inline_get_pointer_to_function())(int, int) {
2730 return multiply;
2731 }
2732 "#,
2733 )?;
2734 let rs_api_impl = generate_rs_api_impl(&ir)?;
2735 assert_cc_matches!(
2736 rs_api_impl,
2737 quote! {
2738 extern "C" rs_api_impl_support::type_identity_t<int(int , int)>*
2739 __rust_thunk___Z30inline_get_pointer_to_functionv() {
2740 return inline_get_pointer_to_function();
2741 }
2742 }
2743 );
2744 Ok(())
2745 }
2746
2747 #[test]
Lukasz Anforowicz0b6a6ac2022-03-22 22:32:23 +00002748 fn test_func_ptr_with_custom_abi_thunk() -> Result<()> {
2749 // Using an `inline` keyword forces generation of a C++ thunk in
2750 // `rs_api_impl` (i.e. exercises `format_cc_type`,
2751 // `format_cc_call_conv_as_clang_attribute` and similar code).
2752 let ir = ir_from_cc(
2753 r#"
2754 inline int (*inline_get_ptr_to_func())(float, double) [[clang::vectorcall]];
2755 "#,
2756 )?;
2757
2758 // Verify that the test input correctly represents what we intend to
2759 // test - we want [[clang::vectorcall]] to apply to the returned
2760 // function pointer, but *not* apply to the `get_ptr_to_func` function.
2761 assert_ir_matches!(
2762 ir,
2763 quote! {
2764 Func(Func {
2765 name: "inline_get_ptr_to_func", ...
2766 return_type: MappedType {
2767 rs_type: RsType {
2768 name: Some("Option"), ...
2769 type_args: [RsType { name: Some("#funcPtr vectorcall"), ... }], ...
2770 },
2771 cc_type: CcType {
2772 name: Some("*"), ...
2773 type_args: [CcType { name: Some("#funcValue vectorcall"), ... }], ...
2774 },
2775 }, ...
2776 has_c_calling_convention: true, ...
2777 }),
2778 }
2779 );
2780
2781 // This test is quite similar to `test_func_ptr_thunk` - the main
2782 // difference is verification of the `__attribute__((vectorcall))` in
2783 // the expected signature of the generated thunk below.
2784 let rs_api_impl = generate_rs_api_impl(&ir)?;
2785 assert_cc_matches!(
2786 rs_api_impl,
2787 quote! {
2788 extern "C" rs_api_impl_support::type_identity_t<
2789 int(float , double) __attribute__((vectorcall))
2790 >* __rust_thunk___Z22inline_get_ptr_to_funcv() {
2791 return inline_get_ptr_to_func();
2792 }
2793 }
2794 );
2795 Ok(())
2796 }
2797
2798 #[test]
Michael Forstered642022021-10-04 09:48:25 +00002799 fn test_item_order() -> Result<()> {
Marcel Hlopko89547752021-12-10 09:39:41 +00002800 let ir = ir_from_cc(
2801 "int first_func();
2802 struct FirstStruct {};
2803 int second_func();
2804 struct SecondStruct {};",
2805 )?;
Michael Forstered642022021-10-04 09:48:25 +00002806
Marcel Hlopko89547752021-12-10 09:39:41 +00002807 let rs_api = rs_tokens_to_formatted_string(generate_rs_api(&ir)?)?;
2808
Lukasz Anforowiczdd9ae0f2022-02-17 15:52:53 +00002809 let idx = |s: &str| rs_api.find(s).ok_or_else(|| anyhow!("'{}' missing", s));
Michael Forstered642022021-10-04 09:48:25 +00002810
2811 let f1 = idx("fn first_func")?;
2812 let f2 = idx("fn second_func")?;
2813 let s1 = idx("struct FirstStruct")?;
2814 let s2 = idx("struct SecondStruct")?;
Googlera675ae02021-12-07 08:04:59 +00002815 let t1 = idx("fn __rust_thunk___Z10first_funcv")?;
2816 let t2 = idx("fn __rust_thunk___Z11second_funcv")?;
Michael Forstered642022021-10-04 09:48:25 +00002817
2818 assert!(f1 < s1);
2819 assert!(s1 < f2);
2820 assert!(f2 < s2);
2821 assert!(s2 < t1);
2822 assert!(t1 < t2);
2823
2824 Ok(())
2825 }
Michael Forster028800b2021-10-05 12:39:59 +00002826
2827 #[test]
Devin Jeanpierrec80e6242022-02-03 01:56:40 +00002828 fn test_base_class_subobject_layout() -> Result<()> {
2829 let ir = ir_from_cc(
2830 r#"
2831 // We use a class here to force `Derived::z` to live inside the tail padding of `Base`.
2832 // On the Itanium ABI, this would not happen if `Base` were a POD type.
Devin Jeanpierre56777022022-02-03 01:57:15 +00002833 class Base {__INT64_TYPE__ x; char y;};
2834 struct Derived final : Base {__INT16_TYPE__ z;};
Devin Jeanpierrec80e6242022-02-03 01:56:40 +00002835 "#,
2836 )?;
2837 let rs_api = generate_rs_api(&ir)?;
2838 assert_rs_matches!(
2839 rs_api,
2840 quote! {
2841 #[repr(C, align(8))]
2842 pub struct Derived {
Rosica Dejanovskaaccf00b2022-04-01 13:28:04 -07002843 __base_class_subobjects: [rust_std::mem::MaybeUninit<u8>; 10],
Devin Jeanpierrec80e6242022-02-03 01:56:40 +00002844 pub z: i16,
2845 }
2846 }
2847 );
2848 Ok(())
2849 }
2850
2851 /// The same as test_base_class_subobject_layout, but with multiple
2852 /// inheritance.
2853 #[test]
2854 fn test_base_class_multiple_inheritance_subobject_layout() -> Result<()> {
2855 let ir = ir_from_cc(
2856 r#"
Devin Jeanpierre56777022022-02-03 01:57:15 +00002857 class Base1 {__INT64_TYPE__ x;};
Devin Jeanpierrec80e6242022-02-03 01:56:40 +00002858 class Base2 {char y;};
Devin Jeanpierre56777022022-02-03 01:57:15 +00002859 struct Derived final : Base1, Base2 {__INT16_TYPE__ z;};
Devin Jeanpierrec80e6242022-02-03 01:56:40 +00002860 "#,
2861 )?;
2862 let rs_api = generate_rs_api(&ir)?;
2863 assert_rs_matches!(
2864 rs_api,
2865 quote! {
2866 #[repr(C, align(8))]
2867 pub struct Derived {
Rosica Dejanovskaaccf00b2022-04-01 13:28:04 -07002868 __base_class_subobjects: [rust_std::mem::MaybeUninit<u8>; 10],
Devin Jeanpierrec80e6242022-02-03 01:56:40 +00002869 pub z: i16,
2870 }
2871 }
2872 );
2873 Ok(())
2874 }
2875
2876 /// The same as test_base_class_subobject_layout, but with a chain of
2877 /// inheritance.
2878 #[test]
2879 fn test_base_class_deep_inheritance_subobject_layout() -> Result<()> {
2880 let ir = ir_from_cc(
2881 r#"
Devin Jeanpierre56777022022-02-03 01:57:15 +00002882 class Base1 {__INT64_TYPE__ x;};
Devin Jeanpierrec80e6242022-02-03 01:56:40 +00002883 class Base2 : Base1 {char y;};
Devin Jeanpierre56777022022-02-03 01:57:15 +00002884 struct Derived final : Base2 {__INT16_TYPE__ z;};
Devin Jeanpierrec80e6242022-02-03 01:56:40 +00002885 "#,
2886 )?;
2887 let rs_api = generate_rs_api(&ir)?;
2888 assert_rs_matches!(
2889 rs_api,
2890 quote! {
2891 #[repr(C, align(8))]
2892 pub struct Derived {
Rosica Dejanovskaaccf00b2022-04-01 13:28:04 -07002893 __base_class_subobjects: [rust_std::mem::MaybeUninit<u8>; 10],
Devin Jeanpierrec80e6242022-02-03 01:56:40 +00002894 pub z: i16,
2895 }
2896 }
2897 );
2898 Ok(())
2899 }
2900
2901 /// For derived classes with no data members, we can't use the offset of the
2902 /// first member to determine the size of the base class subobjects.
2903 #[test]
2904 fn test_base_class_subobject_fieldless_layout() -> Result<()> {
2905 let ir = ir_from_cc(
2906 r#"
Devin Jeanpierre56777022022-02-03 01:57:15 +00002907 class Base {__INT64_TYPE__ x; char y;};
Devin Jeanpierrec80e6242022-02-03 01:56:40 +00002908 struct Derived final : Base {};
2909 "#,
2910 )?;
2911 let rs_api = generate_rs_api(&ir)?;
2912 assert_rs_matches!(
2913 rs_api,
2914 quote! {
2915 #[repr(C, align(8))]
2916 pub struct Derived {
Rosica Dejanovskaaccf00b2022-04-01 13:28:04 -07002917 __base_class_subobjects: [rust_std::mem::MaybeUninit<u8>; 9],
Devin Jeanpierrec80e6242022-02-03 01:56:40 +00002918 }
2919 }
2920 );
2921 Ok(())
2922 }
2923
2924 #[test]
2925 fn test_base_class_subobject_empty_fieldless() -> Result<()> {
2926 let ir = ir_from_cc(
2927 r#"
2928 class Base {};
2929 struct Derived final : Base {};
2930 "#,
2931 )?;
2932 let rs_api = generate_rs_api(&ir)?;
2933 assert_rs_matches!(
2934 rs_api,
2935 quote! {
2936 #[repr(C)]
2937 pub struct Derived {
Rosica Dejanovskaaccf00b2022-04-01 13:28:04 -07002938 __base_class_subobjects: [rust_std::mem::MaybeUninit<u8>; 0],
Devin Jeanpierrec80e6242022-02-03 01:56:40 +00002939 /// Prevent empty C++ struct being zero-size in Rust.
Rosica Dejanovskaaccf00b2022-04-01 13:28:04 -07002940 placeholder: rust_std::mem::MaybeUninit<u8>,
Devin Jeanpierrec80e6242022-02-03 01:56:40 +00002941 }
2942 }
2943 );
2944 Ok(())
2945 }
2946
2947 #[test]
2948 fn test_base_class_subobject_empty() -> Result<()> {
2949 let ir = ir_from_cc(
2950 r#"
2951 class Base {};
2952 struct Derived final : Base {};
2953 "#,
2954 )?;
2955 let rs_api = generate_rs_api(&ir)?;
2956 assert_rs_matches!(
2957 rs_api,
2958 quote! {
2959 #[repr(C)]
2960 pub struct Derived {
Rosica Dejanovskaaccf00b2022-04-01 13:28:04 -07002961 __base_class_subobjects: [rust_std::mem::MaybeUninit<u8>; 0],
Devin Jeanpierrec80e6242022-02-03 01:56:40 +00002962 /// Prevent empty C++ struct being zero-size in Rust.
Rosica Dejanovskaaccf00b2022-04-01 13:28:04 -07002963 placeholder: rust_std::mem::MaybeUninit<u8>,
Devin Jeanpierrec80e6242022-02-03 01:56:40 +00002964 }
2965 }
2966 );
2967 Ok(())
2968 }
2969
Devin Jeanpierreb69bcae2022-02-03 09:45:50 +00002970 /// When a field is [[no_unique_address]], it occupies the space up to the
2971 /// next field.
2972 #[test]
2973 fn test_no_unique_address() -> Result<()> {
2974 let ir = ir_from_cc(
2975 r#"
2976 class Field1 {__INT64_TYPE__ x;};
2977 class Field2 {char y;};
2978 struct Struct final {
2979 [[no_unique_address]] Field1 field1;
2980 [[no_unique_address]] Field2 field2;
2981 __INT16_TYPE__ z;
2982 };
2983 "#,
2984 )?;
2985 let rs_api = generate_rs_api(&ir)?;
2986 assert_rs_matches!(
2987 rs_api,
2988 quote! {
2989 #[derive(Clone, Copy)]
2990 #[repr(C, align(8))]
2991 pub struct Struct {
Rosica Dejanovskaaccf00b2022-04-01 13:28:04 -07002992 field1: [rust_std::mem::MaybeUninit<u8>; 8],
2993 field2: [rust_std::mem::MaybeUninit<u8>; 2],
Devin Jeanpierreb69bcae2022-02-03 09:45:50 +00002994 pub z: i16,
2995 }
Devin Jeanpierre58181ac2022-02-14 21:30:05 +00002996
2997 impl Struct {
2998 pub fn field1(&self) -> &Field1 {
2999 unsafe {&* (&self.field1 as *const _ as *const Field1)}
3000 }
3001 pub fn field2(&self) -> &Field2 {
3002 unsafe {&* (&self.field2 as *const _ as *const Field2)}
3003 }
3004 }
Devin Jeanpierreb69bcae2022-02-03 09:45:50 +00003005 }
3006 );
3007 Ok(())
3008 }
3009
3010 /// When a [[no_unique_address]] field is the last one, it occupies the rest
3011 /// of the object.
3012 #[test]
3013 fn test_no_unique_address_last_field() -> Result<()> {
3014 let ir = ir_from_cc(
3015 r#"
3016 class Field1 {__INT64_TYPE__ x;};
3017 class Field2 {char y;};
3018 struct Struct final {
3019 [[no_unique_address]] Field1 field1;
3020 [[no_unique_address]] Field2 field2;
3021 };
3022 "#,
3023 )?;
3024 let rs_api = generate_rs_api(&ir)?;
3025 assert_rs_matches!(
3026 rs_api,
3027 quote! {
3028 #[derive(Clone, Copy)]
3029 #[repr(C, align(8))]
3030 pub struct Struct {
Rosica Dejanovskaaccf00b2022-04-01 13:28:04 -07003031 field1: [rust_std::mem::MaybeUninit<u8>; 8],
3032 field2: [rust_std::mem::MaybeUninit<u8>; 8],
Devin Jeanpierreb69bcae2022-02-03 09:45:50 +00003033 }
3034 }
3035 );
3036 Ok(())
3037 }
3038
3039 #[test]
3040 fn test_no_unique_address_empty() -> Result<()> {
3041 let ir = ir_from_cc(
3042 r#"
3043 class Field {};
3044 struct Struct final {
3045 [[no_unique_address]] Field field;
3046 int x;
3047 };
3048 "#,
3049 )?;
3050 let rs_api = generate_rs_api(&ir)?;
3051 assert_rs_matches!(
3052 rs_api,
3053 quote! {
3054 #[repr(C, align(4))]
3055 pub struct Struct {
Rosica Dejanovskaaccf00b2022-04-01 13:28:04 -07003056 field: [rust_std::mem::MaybeUninit<u8>; 0],
Devin Jeanpierreb69bcae2022-02-03 09:45:50 +00003057 pub x: i32,
3058 }
3059 }
3060 );
3061 Ok(())
3062 }
3063
3064 #[test]
3065 fn test_base_class_subobject_empty_last_field() -> Result<()> {
3066 let ir = ir_from_cc(
3067 r#"
3068 class Field {};
3069 struct Struct final {
3070 [[no_unique_address]] Field field;
3071 };
3072 "#,
3073 )?;
3074 let rs_api = generate_rs_api(&ir)?;
3075 assert_rs_matches!(
3076 rs_api,
3077 quote! {
3078 #[repr(C)]
3079 pub struct Struct {
Rosica Dejanovskaaccf00b2022-04-01 13:28:04 -07003080 field: [rust_std::mem::MaybeUninit<u8>; 1],
Devin Jeanpierreb69bcae2022-02-03 09:45:50 +00003081 }
3082 }
3083 );
3084 Ok(())
3085 }
3086
Devin Jeanpierrec80e6242022-02-03 01:56:40 +00003087 #[test]
Teddy Katz76fa42b2022-02-23 01:22:56 +00003088 fn test_generate_enum_basic() -> Result<()> {
3089 let ir = ir_from_cc("enum Color { kRed = 5, kBlue };")?;
3090 let rs_api = generate_rs_api(&ir)?;
3091 assert_rs_matches!(
3092 rs_api,
3093 quote! {
3094 #[repr(transparent)]
3095 #[derive(Debug, PartialEq, Eq, Copy, Clone, Hash, PartialOrd, Ord)]
3096 pub struct Color(u32);
3097 impl Color {
3098 pub const kRed: Color = Color(5);
3099 pub const kBlue: Color = Color(6);
3100 }
3101 impl From<u32> for Color {
3102 fn from(value: u32) -> Color {
3103 Color(v)
3104 }
3105 }
3106 impl From<Color> for u32 {
3107 fn from(value: Color) -> u32 {
3108 v.0
3109 }
3110 }
3111 }
3112 );
3113 Ok(())
3114 }
3115
3116 #[test]
3117 fn test_generate_scoped_enum_basic() -> Result<()> {
3118 let ir = ir_from_cc("enum class Color { kRed = -5, kBlue };")?;
3119 let rs_api = generate_rs_api(&ir)?;
3120 assert_rs_matches!(
3121 rs_api,
3122 quote! {
3123 #[repr(transparent)]
3124 #[derive(Debug, PartialEq, Eq, Copy, Clone, Hash, PartialOrd, Ord)]
3125 pub struct Color(i32);
3126 impl Color {
3127 pub const kRed: Color = Color(-5);
3128 pub const kBlue: Color = Color(-4);
3129 }
3130 impl From<i32> for Color {
3131 fn from(value: i32) -> Color {
3132 Color(v)
3133 }
3134 }
3135 impl From<Color> for i32 {
3136 fn from(value: Color) -> i32 {
3137 v.0
3138 }
3139 }
3140 }
3141 );
3142 Ok(())
3143 }
3144
3145 #[test]
3146 fn test_generate_enum_with_64_bit_signed_vals() -> Result<()> {
3147 let ir = ir_from_cc(
3148 "enum Color : long { kViolet = -9223372036854775807 - 1LL, kRed = -5, kBlue, kGreen = 3, kMagenta = 9223372036854775807 };",
3149 )?;
3150 let rs_api = generate_rs_api(&ir)?;
3151 assert_rs_matches!(
3152 rs_api,
3153 quote! {
3154 #[repr(transparent)]
3155 #[derive(Debug, PartialEq, Eq, Copy, Clone, Hash, PartialOrd, Ord)]
3156 pub struct Color(i64);
3157 impl Color {
3158 pub const kViolet: Color = Color(-9223372036854775808);
3159 pub const kRed: Color = Color(-5);
3160 pub const kBlue: Color = Color(-4);
3161 pub const kGreen: Color = Color(3);
3162 pub const kMagenta: Color = Color(9223372036854775807);
3163 }
3164 impl From<i64> for Color {
3165 fn from(value: i64) -> Color {
3166 Color(v)
3167 }
3168 }
3169 impl From<Color> for i64 {
3170 fn from(value: Color) -> i64 {
3171 v.0
3172 }
3173 }
3174 }
3175 );
3176 Ok(())
3177 }
3178
3179 #[test]
3180 fn test_generate_enum_with_64_bit_unsigned_vals() -> Result<()> {
3181 let ir = ir_from_cc(
3182 "enum Color: unsigned long { kRed, kBlue, kLimeGreen = 18446744073709551615 };",
3183 )?;
3184 let rs_api = generate_rs_api(&ir)?;
3185 assert_rs_matches!(
3186 rs_api,
3187 quote! {
3188 #[repr(transparent)]
3189 #[derive(Debug, PartialEq, Eq, Copy, Clone, Hash, PartialOrd, Ord)]
3190 pub struct Color(u64);
3191 impl Color {
3192 pub const kRed: Color = Color(0);
3193 pub const kBlue: Color = Color(1);
3194 pub const kLimeGreen: Color = Color(18446744073709551615);
3195 }
3196 impl From<u64> for Color {
3197 fn from(value: u64) -> Color {
3198 Color(v)
3199 }
3200 }
3201 impl From<Color> for u64 {
3202 fn from(value: Color) -> u64 {
3203 v.0
3204 }
3205 }
3206 }
3207 );
3208 Ok(())
3209 }
3210
3211 #[test]
3212 fn test_generate_enum_with_32_bit_signed_vals() -> Result<()> {
3213 let ir = ir_from_cc(
3214 "enum Color { kViolet = -2147483647 - 1, kRed = -5, kBlue, kGreen = 3, kMagenta = 2147483647 };",
3215 )?;
3216 let rs_api = generate_rs_api(&ir)?;
3217 assert_rs_matches!(
3218 rs_api,
3219 quote! {
3220 #[repr(transparent)]
3221 #[derive(Debug, PartialEq, Eq, Copy, Clone, Hash, PartialOrd, Ord)]
3222 pub struct Color(i32);
3223 impl Color {
3224 pub const kViolet: Color = Color(-2147483648);
3225 pub const kRed: Color = Color(-5);
3226 pub const kBlue: Color = Color(-4);
3227 pub const kGreen: Color = Color(3);
3228 pub const kMagenta: Color = Color(2147483647);
3229 }
3230 impl From<i32> for Color {
3231 fn from(value: i32) -> Color {
3232 Color(v)
3233 }
3234 }
3235 impl From<Color> for i32 {
3236 fn from(value: Color) -> i32 {
3237 v.0
3238 }
3239 }
3240 }
3241 );
3242 Ok(())
3243 }
3244
3245 #[test]
3246 fn test_generate_enum_with_32_bit_unsigned_vals() -> Result<()> {
3247 let ir = ir_from_cc("enum Color: unsigned int { kRed, kBlue, kLimeGreen = 4294967295 };")?;
3248 let rs_api = generate_rs_api(&ir)?;
3249 assert_rs_matches!(
3250 rs_api,
3251 quote! {
3252 #[repr(transparent)]
3253 #[derive(Debug, PartialEq, Eq, Copy, Clone, Hash, PartialOrd, Ord)]
3254 pub struct Color(u32);
3255 impl Color {
3256 pub const kRed: Color = Color(0);
3257 pub const kBlue: Color = Color(1);
3258 pub const kLimeGreen: Color = Color(4294967295);
3259 }
3260 impl From<u32> for Color {
3261 fn from(value: u32) -> Color {
3262 Color(v)
3263 }
3264 }
3265 impl From<Color> for u32 {
3266 fn from(value: Color) -> u32 {
3267 v.0
3268 }
3269 }
3270 }
3271 );
3272 Ok(())
3273 }
3274
3275 #[test]
Michael Forster409d9412021-10-07 08:35:29 +00003276 fn test_doc_comment_func() -> Result<()> {
Marcel Hlopko89547752021-12-10 09:39:41 +00003277 let ir = ir_from_cc(
3278 "
3279 // Doc Comment
3280 // with two lines
3281 int func();",
3282 )?;
Michael Forster409d9412021-10-07 08:35:29 +00003283
Marcel Hlopko89547752021-12-10 09:39:41 +00003284 assert_rs_matches!(
3285 generate_rs_api(&ir)?,
3286 // leading space is intentional so there is a space between /// and the text of the
3287 // comment
3288 quote! {
3289 #[doc = " Doc Comment\n with two lines"]
3290 #[inline(always)]
3291 pub fn func
3292 }
Michael Forster409d9412021-10-07 08:35:29 +00003293 );
3294
3295 Ok(())
3296 }
3297
3298 #[test]
3299 fn test_doc_comment_record() -> Result<()> {
Marcel Hlopko89547752021-12-10 09:39:41 +00003300 let ir = ir_from_cc(
3301 "// Doc Comment\n\
3302 //\n\
3303 // * with bullet\n\
Devin Jeanpierre88343c72022-01-15 01:10:23 +00003304 struct SomeStruct final {\n\
Marcel Hlopko89547752021-12-10 09:39:41 +00003305 // Field doc\n\
3306 int field;\
3307 };",
3308 )?;
Michael Forster028800b2021-10-05 12:39:59 +00003309
Marcel Hlopko89547752021-12-10 09:39:41 +00003310 assert_rs_matches!(
3311 generate_rs_api(&ir)?,
3312 quote! {
3313 #[doc = " Doc Comment\n \n * with bullet"]
3314 #[derive(Clone, Copy)]
3315 #[repr(C)]
3316 pub struct SomeStruct {
3317 # [doc = " Field doc"]
3318 pub field: i32,
3319 }
3320 }
Michael Forstercc5941a2021-10-07 07:12:24 +00003321 );
Michael Forster028800b2021-10-05 12:39:59 +00003322 Ok(())
3323 }
Devin Jeanpierre91de7012021-10-21 12:53:51 +00003324
Devin Jeanpierre96839c12021-12-14 00:27:38 +00003325 #[test]
Teddy Katzd2cd1422022-04-04 09:41:33 -07003326 fn test_basic_union() -> Result<()> {
3327 let ir = ir_from_cc(
3328 r#"
3329 union SomeUnion {
3330 int some_field;
3331 long long some_bigger_field;
3332 };
3333 "#,
3334 )?;
3335 let rs_api = generate_rs_api(&ir)?;
3336
3337 assert_rs_matches!(
3338 rs_api,
3339 quote! {
3340 #[derive(Clone, Copy)]
3341 #[repr(C)]
3342 pub union SomeUnion {
3343 pub some_field: i32,
3344 pub some_bigger_field: i64,
3345 }
3346 }
3347 );
3348 Ok(())
3349 }
3350
3351 #[test]
3352 fn test_union_with_private_fields() -> Result<()> {
3353 let ir = ir_from_cc(
3354 r#"
3355 union SomeUnionWithPrivateFields {
3356 public:
3357 int public_field;
3358 private:
3359 long long private_field;
3360 };
3361 "#,
3362 )?;
3363 let rs_api = generate_rs_api(&ir)?;
3364
3365 assert_rs_matches!(
3366 rs_api,
3367 quote! {
3368 #[derive(Clone, Copy)]
3369 #[repr(C)]
3370 pub union SomeUnionWithPrivateFields {
3371 pub public_field: i32,
3372 private_field: i64,
3373 }
3374 }
3375 );
3376
3377 assert_rs_matches!(
3378 rs_api,
3379 quote! {
3380 const _: () = assert!(rust_std::mem::size_of::<SomeUnionWithPrivateFields>() == 8usize);
3381 const _: () = assert!(rust_std::mem::align_of::<SomeUnionWithPrivateFields>() == 8usize);
3382 const _: () = {
3383 assert_impl_all!(SomeUnionWithPrivateFields: Clone);
3384 };
3385 const _: () = {
3386 assert_impl_all!(SomeUnionWithPrivateFields: Copy);
3387 };
3388 const _: () = {
3389 assert_not_impl_all!(SomeUnionWithPrivateFields: Drop);
3390 };
3391 const _: () = assert!(offset_of!(SomeUnionWithPrivateFields, public_field) * 8 == 0usize);
3392 const _: () = assert!(offset_of!(SomeUnionWithPrivateFields, private_field) * 8 == 0usize);
3393 }
3394 );
3395 Ok(())
3396 }
3397
3398 #[test]
3399 fn test_empty_union() -> Result<()> {
3400 let ir = ir_from_cc(
3401 r#"
3402 union EmptyUnion {};
3403 "#,
3404 )?;
3405 let rs_api = generate_rs_api(&ir)?;
3406
3407 assert_rs_matches!(
3408 rs_api,
3409 quote! {
3410 #[derive(Clone, Copy)]
3411 #[repr(C)]
3412 pub union EmptyUnion {
3413 /// Prevent empty C++ struct being zero-size in Rust.
3414 placeholder: rust_std::mem::MaybeUninit<u8>,
3415 }
3416 }
3417 );
3418
3419 assert_rs_matches!(
3420 rs_api,
3421 quote! {
3422 const _: () = assert!(rust_std::mem::size_of::<EmptyUnion>() == 1usize);
3423 const _: () = assert!(rust_std::mem::align_of::<EmptyUnion>() == 1usize);
3424 }
3425 );
3426
3427 Ok(())
3428 }
3429
3430 #[test]
3431 fn test_union_field_with_nontrivial_destructor() -> Result<()> {
3432 let ir = ir_from_cc(
3433 r#"
3434 struct NontrivialStruct { ~NontrivialStruct(); };
3435 union UnionWithNontrivialField {
3436 int trivial_field;
3437 NontrivialStruct nontrivial_field;
3438 };
3439 "#,
3440 )?;
3441 let rs_api = generate_rs_api(&ir)?;
3442
3443 assert_rs_matches!(
3444 rs_api,
3445 quote! {
3446 #[derive(Clone, Copy)]
3447 #[repr(C)]
3448 pub union UnionWithNontrivialField {
3449 pub trivial_field: i32,
3450 pub nontrivial_field: rust_std::mem::ManuallyDrop<NontrivialStruct>,
3451 }
3452 }
3453 );
3454
3455 assert_rs_matches!(
3456 rs_api,
3457 quote! {
3458 const _: () = assert!(rust_std::mem::size_of::<UnionWithNontrivialField>() == 4usize);
3459 const _: () = assert!(rust_std::mem::align_of::<UnionWithNontrivialField>() == 4usize);
3460 }
3461 );
3462 Ok(())
3463 }
3464
3465 #[test]
3466 fn test_union_with_constructors() -> Result<()> {
3467 let ir = ir_from_cc(
3468 r#"
3469 #pragma clang lifetime_elision
3470 union UnionWithDefaultConstructors {
3471 int a;
3472 };
3473 "#,
3474 )?;
3475 let rs_api = generate_rs_api(&ir)?;
3476
3477 assert_rs_matches!(
3478 rs_api,
3479 quote! {
3480 #[derive(Clone, Copy)]
3481 #[repr(C)]
3482 pub union UnionWithDefaultConstructors {
3483 pub a: i32,
3484 }
3485 }
3486 );
3487
3488 assert_rs_matches!(
3489 rs_api,
3490 quote! {
3491 impl Default for UnionWithDefaultConstructors {
3492 #[inline(always)]
3493 fn default() -> Self {
3494 let mut tmp = rust_std::mem::MaybeUninit::<Self>::zeroed();
3495 unsafe {
3496 crate::detail::__rust_thunk___ZN28UnionWithDefaultConstructorsC1Ev(&mut tmp);
3497 tmp.assume_init()
3498 }
3499 }
3500 }
3501 }
3502 );
3503
3504 assert_rs_matches!(
3505 rs_api,
3506 quote! {
3507 impl<'b> From<ctor::RvalueReference<'b, UnionWithDefaultConstructors>> for UnionWithDefaultConstructors {
3508 #[inline(always)]
3509 fn from(__param_0: ctor::RvalueReference<'b, UnionWithDefaultConstructors>) -> Self {
3510 let mut tmp = rust_std::mem::MaybeUninit::<Self>::zeroed();
3511 unsafe {
3512 crate::detail::__rust_thunk___ZN28UnionWithDefaultConstructorsC1EOS_(&mut tmp, __param_0);
3513 tmp.assume_init()
3514 }
3515 }
3516 }
3517 }
3518 );
3519
3520 Ok(())
3521 }
3522
3523 #[test]
Devin Jeanpierre56777022022-02-03 01:57:15 +00003524 fn test_unambiguous_public_bases() -> Result<()> {
3525 let ir = ir_from_cc_dependency(
3526 "
3527 struct VirtualBase {};
3528 struct PrivateBase {};
3529 struct ProtectedBase {};
3530 struct UnambiguousPublicBase {};
3531 struct AmbiguousPublicBase {};
3532 struct MultipleInheritance : UnambiguousPublicBase, AmbiguousPublicBase {};
3533 struct Derived : private PrivateBase, protected ProtectedBase, MultipleInheritance, AmbiguousPublicBase, virtual VirtualBase {};
3534 ",
3535 "",
3536 )?;
3537 let rs_api = generate_rs_api(&ir)?;
3538 // TODO(b/216195042): virtual bases.
3539 assert_rs_not_matches!(rs_api, quote! { From<&'a Derived> for &'a VirtualBase });
3540 assert_rs_matches!(rs_api, quote! { From<&'a Derived> for &'a UnambiguousPublicBase });
3541 assert_rs_matches!(rs_api, quote! { From<&'a Derived> for &'a MultipleInheritance });
3542 assert_rs_not_matches!(rs_api, quote! {From<&'a Derived> for &'a PrivateBase});
3543 assert_rs_not_matches!(rs_api, quote! {From<&'a Derived> for &'a ProtectedBase});
3544 assert_rs_not_matches!(rs_api, quote! {From<&'a Derived> for &'a AmbiguousPublicBase});
3545 Ok(())
3546 }
3547
3548 /// Contrary to intuitions: a base class conversion is ambiguous even if the
3549 /// ambiguity is from a private base class cast that you can't even
3550 /// perform.
3551 ///
3552 /// Explanation (courtesy James Dennett):
3553 ///
3554 /// > Once upon a time, there was a rule in C++ that changing all access
3555 /// > specifiers to "public" would not change the meaning of code.
3556 /// > That's no longer true, but some of its effects can still be seen.
3557 ///
3558 /// So, we need to be sure to not allow casting to privately-ambiguous
3559 /// bases.
3560 #[test]
3561 fn test_unambiguous_public_bases_private_ambiguity() -> Result<()> {
3562 let ir = ir_from_cc_dependency(
3563 "
3564 struct Base {};
3565 struct Intermediate : public Base {};
3566 struct Derived : Base, private Intermediate {};
3567 ",
3568 "",
3569 )?;
3570 let rs_api = generate_rs_api(&ir)?;
3571 assert_rs_not_matches!(rs_api, quote! { From<&'a Derived> for &'a Base });
3572 Ok(())
3573 }
3574
3575 #[test]
Devin Jeanpierre96839c12021-12-14 00:27:38 +00003576 fn test_virtual_thunk() -> Result<()> {
3577 let ir = ir_from_cc("struct Polymorphic { virtual void Foo(); };")?;
3578
3579 assert_cc_matches!(
3580 generate_rs_api_impl(&ir)?,
3581 quote! {
Googler972d3582022-01-11 10:17:22 +00003582 extern "C" void __rust_thunk___ZN11Polymorphic3FooEv(class Polymorphic * __this)
Devin Jeanpierre96839c12021-12-14 00:27:38 +00003583 }
3584 );
3585 Ok(())
3586 }
3587
Lukasz Anforowicz0b6a6ac2022-03-22 22:32:23 +00003588 #[test]
3589 fn test_custom_abi_thunk() -> Result<()> {
3590 let ir = ir_from_cc(
3591 r#"
3592 float f_vectorcall_calling_convention(float p1, float p2) [[clang::vectorcall]];
3593 double f_c_calling_convention(double p1, double p2);
3594 "#,
3595 )?;
3596 let rs_api = generate_rs_api(&ir)?;
3597 let rs_api_impl = generate_rs_api_impl(&ir)?;
3598 assert_rs_matches!(
3599 rs_api,
3600 quote! {
3601 #[inline(always)]
3602 pub fn f_vectorcall_calling_convention(p1: f32, p2: f32) -> f32 {
3603 unsafe {
3604 crate::detail::__rust_thunk___Z31f_vectorcall_calling_conventionff(p1, p2)
3605 }
3606 }
3607 }
3608 );
3609 assert_rs_matches!(
3610 rs_api,
3611 quote! {
3612 #[inline(always)]
3613 pub fn f_c_calling_convention(p1: f64, p2: f64) -> f64 {
3614 unsafe { crate::detail::__rust_thunk___Z22f_c_calling_conventiondd(p1, p2) }
3615 }
3616 }
3617 );
3618 // `link_name` (i.e. no thunk) for `f_c_calling_convention`. No
3619 // `link_name` (i.e. indicates presence of a thunk) for
3620 // `f_vectorcall_calling_convention`.
3621 assert_rs_matches!(
3622 rs_api,
3623 quote! {
3624 mod detail {
3625 #[allow(unused_imports)]
3626 use super::*;
3627 extern "C" {
3628 pub(crate) fn __rust_thunk___Z31f_vectorcall_calling_conventionff(
3629 p1: f32, p2: f32) -> f32;
3630 #[link_name = "_Z22f_c_calling_conventiondd"]
3631 pub(crate) fn __rust_thunk___Z22f_c_calling_conventiondd(
3632 p1: f64, p2: f64) -> f64;
3633 }
3634 }
3635 }
3636 );
3637 // C++ thunk needed for `f_vectorcall_calling_convention`.
3638 assert_cc_matches!(
3639 rs_api_impl,
3640 quote! {
3641 extern "C" float __rust_thunk___Z31f_vectorcall_calling_conventionff(
3642 float p1, float p2) {
Devin Jeanpierredeea7892022-03-29 02:13:31 -07003643 return f_vectorcall_calling_convention (std::forward<decltype(p1)>(p1), std::forward<decltype(p2)>(p2));
Lukasz Anforowicz0b6a6ac2022-03-22 22:32:23 +00003644 }
3645 }
3646 );
3647 // No C++ thunk expected for `f_c_calling_convention`.
3648 assert_cc_not_matches!(rs_api_impl, quote! { f_c_calling_convention });
3649 Ok(())
3650 }
3651
Devin Jeanpierree6e16652021-12-22 15:54:46 +00003652 /// A trivially relocatable final struct is safe to use in Rust as normal,
3653 /// and is Unpin.
3654 #[test]
3655 fn test_no_negative_impl_unpin() -> Result<()> {
3656 let ir = ir_from_cc("struct Trivial final {};")?;
3657 let rs_api = generate_rs_api(&ir)?;
3658 assert_rs_not_matches!(rs_api, quote! {impl !Unpin});
3659 Ok(())
3660 }
3661
3662 /// A non-final struct, even if it's trivial, is not usable by mut
3663 /// reference, and so is !Unpin.
3664 #[test]
3665 fn test_negative_impl_unpin_nonfinal() -> Result<()> {
3666 let ir = ir_from_cc("struct Nonfinal {};")?;
3667 let rs_api = generate_rs_api(&ir)?;
3668 assert_rs_matches!(rs_api, quote! {impl !Unpin for Nonfinal {}});
3669 Ok(())
3670 }
3671
Devin Jeanpierre91de7012021-10-21 12:53:51 +00003672 /// At the least, a trivial type should have no drop impl if or until we add
3673 /// empty drop impls.
3674 #[test]
3675 fn test_no_impl_drop() -> Result<()> {
Googler7cced422021-12-06 11:58:39 +00003676 let ir = ir_from_cc("struct Trivial {};")?;
Marcel Hlopko89547752021-12-10 09:39:41 +00003677 let rs_api = rs_tokens_to_formatted_string(generate_rs_api(&ir)?)?;
Devin Jeanpierre91de7012021-10-21 12:53:51 +00003678 assert!(!rs_api.contains("impl Drop"));
3679 Ok(())
3680 }
3681
Devin Jeanpierre7e9a1de2021-12-03 08:04:22 +00003682 /// User-defined destructors *must* become Drop impls with ManuallyDrop
3683 /// fields
Devin Jeanpierre91de7012021-10-21 12:53:51 +00003684 #[test]
Devin Jeanpierre7e9a1de2021-12-03 08:04:22 +00003685 fn test_impl_drop_user_defined_destructor() -> Result<()> {
Googler7cced422021-12-06 11:58:39 +00003686 let ir = ir_from_cc(
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00003687 r#" struct NontrivialStruct { ~NontrivialStruct(); };
3688 struct UserDefinedDestructor {
Devin Jeanpierre91de7012021-10-21 12:53:51 +00003689 ~UserDefinedDestructor();
Devin Jeanpierre7e9a1de2021-12-03 08:04:22 +00003690 int x;
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00003691 NontrivialStruct nts;
Devin Jeanpierre91de7012021-10-21 12:53:51 +00003692 };"#,
3693 )?;
3694 let rs_api = generate_rs_api(&ir)?;
Lukasz Anforowicz6d553632022-01-06 21:36:14 +00003695 assert_rs_matches!(
3696 rs_api,
3697 quote! {
3698 impl Drop for UserDefinedDestructor {
3699 #[inline(always)]
3700 fn drop(&mut self) {
3701 unsafe { crate::detail::__rust_thunk___ZN21UserDefinedDestructorD1Ev(self) }
3702 }
3703 }
3704 }
3705 );
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00003706 assert_rs_matches!(rs_api, quote! {pub x: i32,});
Rosica Dejanovskaaccf00b2022-04-01 13:28:04 -07003707 assert_rs_matches!(
3708 rs_api,
3709 quote! {pub nts: rust_std::mem::ManuallyDrop<NontrivialStruct>,}
3710 );
Devin Jeanpierre7e9a1de2021-12-03 08:04:22 +00003711 Ok(())
3712 }
3713
Lukasz Anforowicz6d553632022-01-06 21:36:14 +00003714 /// nontrivial types without user-defined destructors should invoke
3715 /// the C++ destructor to preserve the order of field destructions.
Devin Jeanpierre7e9a1de2021-12-03 08:04:22 +00003716 #[test]
3717 fn test_impl_drop_nontrivial_member_destructor() -> Result<()> {
3718 // TODO(jeanpierreda): This would be cleaner if the UserDefinedDestructor code were
3719 // omitted. For example, we simulate it so that UserDefinedDestructor
3720 // comes from another library.
Googler7cced422021-12-06 11:58:39 +00003721 let ir = ir_from_cc(
Devin Jeanpierre88343c72022-01-15 01:10:23 +00003722 r#"struct UserDefinedDestructor final {
Devin Jeanpierre7e9a1de2021-12-03 08:04:22 +00003723 ~UserDefinedDestructor();
3724 };
Devin Jeanpierre88343c72022-01-15 01:10:23 +00003725 struct TrivialStruct final { int i; };
3726 struct NontrivialMembers final {
Devin Jeanpierre7e9a1de2021-12-03 08:04:22 +00003727 UserDefinedDestructor udd;
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00003728 TrivialStruct ts;
Devin Jeanpierre7e9a1de2021-12-03 08:04:22 +00003729 int x;
3730 };"#,
3731 )?;
3732 let rs_api = generate_rs_api(&ir)?;
Lukasz Anforowicz6d553632022-01-06 21:36:14 +00003733 assert_rs_matches!(
3734 rs_api,
3735 quote! {
3736 impl Drop for NontrivialMembers {
3737 #[inline(always)]
3738 fn drop(&mut self) {
3739 unsafe { crate::detail::__rust_thunk___ZN17NontrivialMembersD1Ev(self) }
3740 }
3741 }
3742 }
3743 );
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00003744 assert_rs_matches!(rs_api, quote! {pub x: i32,});
3745 assert_rs_matches!(rs_api, quote! {pub ts: TrivialStruct,});
Lukasz Anforowicz6d553632022-01-06 21:36:14 +00003746 assert_rs_matches!(
3747 rs_api,
Rosica Dejanovskaaccf00b2022-04-01 13:28:04 -07003748 quote! {pub udd: rust_std::mem::ManuallyDrop<UserDefinedDestructor>,}
Lukasz Anforowicz6d553632022-01-06 21:36:14 +00003749 );
Devin Jeanpierre7e9a1de2021-12-03 08:04:22 +00003750 Ok(())
3751 }
3752
3753 /// Trivial types (at least those that are mapped to Copy rust types) do not
3754 /// get a Drop impl.
3755 #[test]
3756 fn test_impl_drop_trivial() -> Result<()> {
Googler7cced422021-12-06 11:58:39 +00003757 let ir = ir_from_cc(
Devin Jeanpierre88343c72022-01-15 01:10:23 +00003758 r#"struct Trivial final {
Devin Jeanpierre7e9a1de2021-12-03 08:04:22 +00003759 ~Trivial() = default;
3760 int x;
3761 };"#,
3762 )?;
3763 let rs_api = generate_rs_api(&ir)?;
Marcel Hlopko89547752021-12-10 09:39:41 +00003764 assert_rs_not_matches!(rs_api, quote! {impl Drop});
3765 assert_rs_matches!(rs_api, quote! {pub x: i32});
Lukasz Anforowicz2f074162022-01-06 22:50:51 +00003766 let rs_api_impl = generate_rs_api_impl(&ir)?;
3767 // TODO(b/213326125): Avoid generating thunk impls that are never called.
3768 // (The test assertion below should be reversed once this bug is fixed.)
3769 assert_cc_matches!(rs_api_impl, quote! { std::destroy_at });
Devin Jeanpierre91de7012021-10-21 12:53:51 +00003770 Ok(())
3771 }
Devin Jeanpierre45cb1162021-10-27 10:54:28 +00003772
3773 #[test]
Lukasz Anforowicze643ec92021-12-22 15:45:15 +00003774 fn test_impl_default_explicitly_defaulted_constructor() -> Result<()> {
3775 let ir = ir_from_cc(
Lukasz Anforowicz95551272022-01-20 00:02:24 +00003776 r#"#pragma clang lifetime_elision
3777 struct DefaultedConstructor final {
Lukasz Anforowicze643ec92021-12-22 15:45:15 +00003778 DefaultedConstructor() = default;
3779 };"#,
3780 )?;
3781 let rs_api = generate_rs_api(&ir)?;
3782 assert_rs_matches!(
3783 rs_api,
3784 quote! {
3785 impl Default for DefaultedConstructor {
3786 #[inline(always)]
3787 fn default() -> Self {
Rosica Dejanovskaaccf00b2022-04-01 13:28:04 -07003788 let mut tmp = rust_std::mem::MaybeUninit::<Self>::zeroed();
Lukasz Anforowicze643ec92021-12-22 15:45:15 +00003789 unsafe {
Lukasz Anforowicz40c2eb82022-01-11 18:22:31 +00003790 crate::detail::__rust_thunk___ZN20DefaultedConstructorC1Ev(&mut tmp);
Lukasz Anforowicze643ec92021-12-22 15:45:15 +00003791 tmp.assume_init()
3792 }
3793 }
3794 }
3795 }
3796 );
3797 let rs_api_impl = generate_rs_api_impl(&ir)?;
3798 assert_cc_matches!(
3799 rs_api_impl,
3800 quote! {
3801 extern "C" void __rust_thunk___ZN20DefaultedConstructorC1Ev(
Googler972d3582022-01-11 10:17:22 +00003802 class DefaultedConstructor* __this) {
Devin Jeanpierredeea7892022-03-29 02:13:31 -07003803 rs_api_impl_support::construct_at (std::forward<decltype(__this)>(__this)) ;
Lukasz Anforowicze643ec92021-12-22 15:45:15 +00003804 }
3805 }
3806 );
3807 Ok(())
3808 }
3809
3810 #[test]
Lukasz Anforowicz326c4e42022-01-27 14:43:00 +00003811 fn test_impl_clone_that_propagates_lifetime() -> Result<()> {
3812 // This test covers the case where a single lifetime applies to 1)
3813 // the `__this` parameter and 2) other constructor parameters. For
3814 // example, maybe the newly constructed object needs to have the
3815 // same lifetime as the constructor's parameter. (This might require
3816 // annotating the whole C++ struct with a lifetime, so maybe the
3817 // example below is not fully realistic/accurate...).
3818 let mut ir = ir_from_cc(
3819 r#"#pragma clang lifetime_elision
3820 struct Foo final {
Googler53f65942022-02-23 11:23:30 +00003821 [[clang::annotate("lifetimes", "a: a")]]
Lukasz Anforowicz326c4e42022-01-27 14:43:00 +00003822 Foo(const int& i);
3823 };"#,
3824 )?;
3825 let ctor: &mut Func = ir
3826 .items_mut()
3827 .filter_map(|item| match item {
3828 Item::Func(func) => Some(func),
3829 _ => None,
3830 })
3831 .find(|f| {
3832 matches!(&f.name, UnqualifiedIdentifier::Constructor)
3833 && f.params.get(1).map(|p| p.identifier.identifier == "i").unwrap_or_default()
3834 })
3835 .unwrap();
3836 {
3837 // Double-check that the test scenario set up above uses the same lifetime
3838 // for both of the constructor's parameters: `__this` and `i`.
3839 assert_eq!(ctor.params.len(), 2);
3840 let this_lifetime: LifetimeId =
3841 *ctor.params[0].type_.rs_type.lifetime_args.first().unwrap();
3842 let i_lifetime: LifetimeId =
3843 *ctor.params[1].type_.rs_type.lifetime_args.first_mut().unwrap();
3844 assert_eq!(i_lifetime, this_lifetime);
3845 }
3846
3847 // Before cl/423346348 the generated Rust code would incorrectly look
3848 // like this (note the mismatched 'a and 'b lifetimes):
3849 // fn from<'b>(i: &'a i32) -> Self
3850 // After this CL, this scenario will result in an explicit error.
Devin Jeanpierred7b48102022-03-31 04:15:03 -07003851 let rs_api = generate_rs_api(&ir)?;
3852 assert_rs_not_matches!(rs_api, quote! {impl From});
3853 let rs_api_str = tokens_to_string(rs_api)?;
3854 assert!(rs_api_str.contains(
3855 "// The lifetime of `__this` is unexpectedly also used by another parameter"
3856 ));
Lukasz Anforowicz326c4e42022-01-27 14:43:00 +00003857 Ok(())
3858 }
3859
3860 #[test]
Lukasz Anforowicz9bab8352021-12-22 17:35:31 +00003861 fn test_impl_default_non_trivial_struct() -> Result<()> {
3862 let ir = ir_from_cc(
Lukasz Anforowicz71716b72022-01-26 17:05:05 +00003863 r#"#pragma clang lifetime_elision
3864 struct NonTrivialStructWithConstructors final {
Lukasz Anforowicz9bab8352021-12-22 17:35:31 +00003865 NonTrivialStructWithConstructors();
3866 ~NonTrivialStructWithConstructors(); // Non-trivial
3867 };"#,
3868 )?;
3869 let rs_api = generate_rs_api(&ir)?;
3870 assert_rs_not_matches!(rs_api, quote! {impl Default});
3871 Ok(())
3872 }
3873
3874 #[test]
Lukasz Anforowicz71716b72022-01-26 17:05:05 +00003875 fn test_impl_from_for_explicit_conversion_constructor() -> Result<()> {
3876 let ir = ir_from_cc(
3877 r#"#pragma clang lifetime_elision
3878 struct SomeStruct final {
3879 explicit SomeStruct(int i);
3880 };"#,
3881 )?;
3882 let rs_api = generate_rs_api(&ir)?;
3883 // As discussed in b/214020567 for now we only generate `From::from` bindings
3884 // for *implicit* C++ conversion constructors.
3885 assert_rs_not_matches!(rs_api, quote! {impl From});
3886 Ok(())
3887 }
3888
3889 #[test]
3890 fn test_impl_from_for_implicit_conversion_constructor() -> Result<()> {
3891 let ir = ir_from_cc(
3892 r#"#pragma clang lifetime_elision
3893 struct SomeStruct final {
3894 SomeStruct(int i); // implicit - no `explicit` keyword
3895 };"#,
3896 )?;
3897 let rs_api = generate_rs_api(&ir)?;
3898 // As discussed in b/214020567 we generate `From::from` bindings for
3899 // *implicit* C++ conversion constructors.
3900 assert_rs_matches!(
3901 rs_api,
3902 quote! {
3903 impl From<i32> for SomeStruct {
3904 #[inline(always)]
3905 fn from(i: i32) -> Self {
Rosica Dejanovskaaccf00b2022-04-01 13:28:04 -07003906 let mut tmp = rust_std::mem::MaybeUninit::<Self>::zeroed();
Lukasz Anforowicz71716b72022-01-26 17:05:05 +00003907 unsafe {
3908 crate::detail::__rust_thunk___ZN10SomeStructC1Ei(&mut tmp, i);
3909 tmp.assume_init()
3910 }
3911 }
3912 }
3913 }
3914 );
3915 Ok(())
3916 }
3917
3918 #[test]
Lukasz Anforowicz5e623782022-03-14 16:52:23 +00003919 fn test_impl_from_for_implicit_conversion_from_reference() -> Result<()> {
3920 let ir = ir_from_cc(
3921 r#"#pragma clang lifetime_elision
3922 struct SomeOtherStruct final { int i; };
3923 struct StructUnderTest final {
3924 StructUnderTest(const SomeOtherStruct& other); // implicit - no `explicit` keyword
3925 };"#,
3926 )?;
3927 let rs_api = generate_rs_api(&ir)?;
3928 // This is a regression test for b/223800038: We want to ensure that the
3929 // code says `impl<'b>` (instead of incorrectly declaring that lifetime
3930 // in `fn from<'b>`).
3931 assert_rs_matches!(
3932 rs_api,
3933 quote! {
3934 impl<'b> From<&'b SomeOtherStruct> for StructUnderTest {
3935 #[inline(always)]
3936 fn from(other: &'b SomeOtherStruct) -> Self {
Rosica Dejanovskaaccf00b2022-04-01 13:28:04 -07003937 let mut tmp = rust_std::mem::MaybeUninit::<Self>::zeroed();
Lukasz Anforowicz5e623782022-03-14 16:52:23 +00003938 unsafe {
3939 crate::detail::__rust_thunk___ZN15StructUnderTestC1ERK15SomeOtherStruct(
3940 &mut tmp, other);
3941 tmp.assume_init()
3942 }
3943 }
3944 }
3945 },
3946 );
3947 Ok(())
3948 }
3949
3950 #[test]
Lukasz Anforowicz732ca642022-02-03 20:58:38 +00003951 fn test_impl_eq_for_member_function() -> Result<()> {
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +00003952 let ir = ir_from_cc(
3953 r#"#pragma clang lifetime_elision
3954 struct SomeStruct final {
3955 inline bool operator==(const SomeStruct& other) const {
3956 return i == other.i;
3957 }
3958 int i;
3959 };"#,
3960 )?;
3961 let rs_api = generate_rs_api(&ir)?;
3962 assert_rs_matches!(
3963 rs_api,
3964 quote! {
3965 impl PartialEq<SomeStruct> for SomeStruct {
3966 #[inline(always)]
3967 fn eq<'a, 'b>(&'a self, other: &'b SomeStruct) -> bool {
3968 unsafe { crate::detail::__rust_thunk___ZNK10SomeStructeqERKS_(self, other) }
3969 }
3970 }
3971 }
3972 );
3973 let rs_api_impl = generate_rs_api_impl(&ir)?;
3974 assert_cc_matches!(
3975 rs_api_impl,
3976 quote! {
3977 extern "C" bool __rust_thunk___ZNK10SomeStructeqERKS_(
3978 const class SomeStruct* __this, const class SomeStruct& other) {
Devin Jeanpierredeea7892022-03-29 02:13:31 -07003979 return __this->operator==(std::forward<decltype(other)>(other));
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +00003980 }
3981 }
3982 );
3983 Ok(())
3984 }
3985
3986 #[test]
Lukasz Anforowicz732ca642022-02-03 20:58:38 +00003987 fn test_impl_eq_for_free_function() -> Result<()> {
3988 let ir = ir_from_cc(
3989 r#"#pragma clang lifetime_elision
3990 struct SomeStruct final { int i; };
3991 bool operator==(const SomeStruct& lhs, const SomeStruct& rhs) {
3992 return lhs.i == rhs.i;
3993 }"#,
3994 )?;
3995 let rs_api = generate_rs_api(&ir)?;
3996 assert_rs_matches!(
3997 rs_api,
3998 quote! {
3999 impl PartialEq<SomeStruct> for SomeStruct {
4000 #[inline(always)]
4001 fn eq<'a, 'b>(&'a self, rhs: &'b SomeStruct) -> bool {
4002 unsafe { crate::detail::__rust_thunk___ZeqRK10SomeStructS1_(self, rhs) }
4003 }
4004 }
4005 }
4006 );
4007 Ok(())
4008 }
4009
4010 #[test]
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +00004011 fn test_impl_eq_non_const_member_function() -> Result<()> {
4012 let ir = ir_from_cc(
4013 r#"#pragma clang lifetime_elision
4014 struct SomeStruct final {
4015 bool operator==(const SomeStruct& other) /* no `const` here */;
4016 };"#,
4017 )?;
4018 let rs_api = generate_rs_api(&ir)?;
4019 assert_rs_not_matches!(rs_api, quote! {impl PartialEq});
4020 Ok(())
4021 }
4022
4023 #[test]
4024 fn test_impl_eq_rhs_by_value() -> Result<()> {
4025 let ir = ir_from_cc(
4026 r#"#pragma clang lifetime_elision
4027 struct SomeStruct final {
4028 bool operator==(SomeStruct other) const;
4029 };"#,
4030 )?;
4031 let rs_api = generate_rs_api(&ir)?;
4032 assert_rs_not_matches!(rs_api, quote! {impl PartialEq});
4033 Ok(())
4034 }
4035
4036 #[test]
Dmitri Gribenko67cbfd22022-03-24 13:39:34 +00004037 fn test_thunk_ident_function() -> Result<()> {
4038 let ir = ir_from_cc("inline int foo() {}")?;
4039 let func = retrieve_func(&ir, "foo");
4040 assert_eq!(thunk_ident(func), make_rs_ident("__rust_thunk___Z3foov"));
4041 Ok(())
Devin Jeanpierre45cb1162021-10-27 10:54:28 +00004042 }
4043
4044 #[test]
4045 fn test_thunk_ident_special_names() {
Marcel Hlopko4b13b962021-12-06 12:40:56 +00004046 let ir = ir_from_cc("struct Class {};").unwrap();
Devin Jeanpierre45cb1162021-10-27 10:54:28 +00004047
Googler45ad2752021-12-06 12:12:35 +00004048 let destructor =
4049 ir.functions().find(|f| f.name == UnqualifiedIdentifier::Destructor).unwrap();
Lukasz Anforowiczdd9ae0f2022-02-17 15:52:53 +00004050 assert_eq!(thunk_ident(destructor), make_rs_ident("__rust_thunk___ZN5ClassD1Ev"));
Devin Jeanpierre45cb1162021-10-27 10:54:28 +00004051
Lukasz Anforowicz49b5bbc2022-02-04 23:40:10 +00004052 let default_constructor = ir
4053 .functions()
4054 .find(|f| f.name == UnqualifiedIdentifier::Constructor && f.params.len() == 1)
4055 .unwrap();
Lukasz Anforowiczdd9ae0f2022-02-17 15:52:53 +00004056 assert_eq!(thunk_ident(default_constructor), make_rs_ident("__rust_thunk___ZN5ClassC1Ev"));
Devin Jeanpierre45cb1162021-10-27 10:54:28 +00004057 }
Googler7cced422021-12-06 11:58:39 +00004058
4059 #[test]
Marcel Hlopko89547752021-12-10 09:39:41 +00004060 fn test_elided_lifetimes() -> Result<()> {
Googler7cced422021-12-06 11:58:39 +00004061 let ir = ir_from_cc(
4062 r#"#pragma clang lifetime_elision
Devin Jeanpierre88343c72022-01-15 01:10:23 +00004063 struct S final {
Googler7cced422021-12-06 11:58:39 +00004064 int& f(int& i);
4065 };"#,
Marcel Hlopko89547752021-12-10 09:39:41 +00004066 )?;
4067 let rs_api = generate_rs_api(&ir)?;
4068 assert_rs_matches!(
4069 rs_api,
4070 quote! {
Lukasz Anforowicz231a3bb2022-01-12 14:05:59 +00004071 pub fn f<'a, 'b>(&'a mut self, i: &'b mut i32) -> &'a mut i32 { ... }
Marcel Hlopko89547752021-12-10 09:39:41 +00004072 }
Googler7cced422021-12-06 11:58:39 +00004073 );
Marcel Hlopko89547752021-12-10 09:39:41 +00004074 assert_rs_matches!(
4075 rs_api,
4076 quote! {
Googler6804a012022-01-05 07:04:36 +00004077 pub(crate) fn __rust_thunk___ZN1S1fERi<'a, 'b>(__this: &'a mut S, i: &'b mut i32)
4078 -> &'a mut i32;
Marcel Hlopko89547752021-12-10 09:39:41 +00004079 }
Googler7cced422021-12-06 11:58:39 +00004080 );
Marcel Hlopko89547752021-12-10 09:39:41 +00004081 Ok(())
Googler7cced422021-12-06 11:58:39 +00004082 }
Lukasz Anforowiczdaff0402021-12-23 00:37:50 +00004083
4084 #[test]
Googler386e5942022-02-24 08:53:29 +00004085 fn test_annotated_lifetimes() -> Result<()> {
4086 let ir = ir_from_cc(
4087 r#"[[clang::annotate("lifetimes", "a, a -> a")]]
4088 int& f(int& i1, int& i2);
4089 "#,
4090 )?;
4091 let rs_api = generate_rs_api(&ir)?;
4092 assert_rs_matches!(
4093 rs_api,
4094 quote! {
4095 pub fn f<'a>(i1: &'a mut i32, i2: &'a mut i32) -> &'a mut i32 { ... }
4096 }
4097 );
4098 assert_rs_matches!(
4099 rs_api,
4100 quote! {
4101 pub(crate) fn __rust_thunk___Z1fRiS_<'a>(i1: &'a mut i32, i2: &'a mut i32)
4102 -> &'a mut i32;
4103 }
4104 );
4105 Ok(())
4106 }
4107
4108 #[test]
Lukasz Anforowiczdaff0402021-12-23 00:37:50 +00004109 fn test_format_generic_params() -> Result<()> {
4110 assert_rs_matches!(format_generic_params(std::iter::empty::<syn::Ident>()), quote! {});
4111
Marcel Hlopkoeaae9b72022-01-21 15:54:11 +00004112 let idents = ["T1", "T2"].iter().map(|s| make_rs_ident(s));
Lukasz Anforowiczdaff0402021-12-23 00:37:50 +00004113 assert_rs_matches!(format_generic_params(idents), quote! { < T1, T2 > });
4114
4115 let lifetimes = ["a", "b"]
4116 .iter()
4117 .map(|s| syn::Lifetime::new(&format!("'{}", s), proc_macro2::Span::call_site()));
4118 assert_rs_matches!(format_generic_params(lifetimes), quote! { < 'a, 'b > });
4119
4120 Ok(())
4121 }
Googlerd03d05b2022-01-07 10:10:57 +00004122
4123 #[test]
4124 fn test_overloaded_functions() -> Result<()> {
4125 // TODO(b/213280424): We don't support creating bindings for overloaded
4126 // functions yet, except in the case of overloaded constructors with a
4127 // single parameter.
4128 let ir = ir_from_cc(
Lukasz Anforowicz55673c92022-01-27 19:37:26 +00004129 r#" #pragma clang lifetime_elision
4130 void f();
Googlerd03d05b2022-01-07 10:10:57 +00004131 void f(int i);
Devin Jeanpierre88343c72022-01-15 01:10:23 +00004132 struct S1 final {
Googlerd03d05b2022-01-07 10:10:57 +00004133 void f();
4134 void f(int i);
4135 };
Devin Jeanpierre88343c72022-01-15 01:10:23 +00004136 struct S2 final {
Googlerd03d05b2022-01-07 10:10:57 +00004137 void f();
4138 };
Devin Jeanpierre88343c72022-01-15 01:10:23 +00004139 struct S3 final {
Googlerd03d05b2022-01-07 10:10:57 +00004140 S3(int i);
4141 S3(double d);
4142 };
4143 "#,
4144 )?;
4145 let rs_api = generate_rs_api(&ir)?;
4146 let rs_api_str = tokens_to_string(rs_api.clone())?;
4147
4148 // Cannot overload free functions.
4149 assert!(rs_api_str.contains("Error while generating bindings for item 'f'"));
4150 assert_rs_not_matches!(rs_api, quote! {pub fn f()});
4151 assert_rs_not_matches!(rs_api, quote! {pub fn f(i: i32)});
4152
4153 // Cannot overload member functions.
4154 assert!(rs_api_str.contains("Error while generating bindings for item 'S1::f'"));
4155 assert_rs_not_matches!(rs_api, quote! {pub fn f(... S1 ...)});
4156
4157 // But we can import member functions that have the same name as a free
4158 // function.
Lukasz Anforowicz55673c92022-01-27 19:37:26 +00004159 assert_rs_matches!(rs_api, quote! {pub fn f<'a>(&'a mut self)});
Googlerd03d05b2022-01-07 10:10:57 +00004160
4161 // We can also import overloaded single-parameter constructors.
4162 assert_rs_matches!(rs_api, quote! {impl From<i32> for S3});
4163 assert_rs_matches!(rs_api, quote! {impl From<f64> for S3});
4164 Ok(())
4165 }
Googlerdcca7f72022-01-10 12:30:43 +00004166
4167 #[test]
4168 fn test_type_alias() -> Result<()> {
4169 let ir = ir_from_cc(
4170 r#"
Lukasz Anforowiczc503af42022-03-04 23:18:15 +00004171 // MyTypedefDecl doc comment
Googlerdcca7f72022-01-10 12:30:43 +00004172 typedef int MyTypedefDecl;
Lukasz Anforowiczc503af42022-03-04 23:18:15 +00004173
Googlerdcca7f72022-01-10 12:30:43 +00004174 using MyTypeAliasDecl = int;
Googler6a0a5252022-01-11 14:08:09 +00004175 using MyTypeAliasDecl_Alias = MyTypeAliasDecl;
4176
Devin Jeanpierre88343c72022-01-15 01:10:23 +00004177 struct S final {};
Googler6a0a5252022-01-11 14:08:09 +00004178 using S_Alias = S;
4179 using S_Alias_Alias = S_Alias;
4180
4181 inline void f(MyTypedefDecl t) {}
Googlerdcca7f72022-01-10 12:30:43 +00004182 "#,
4183 )?;
4184 let rs_api = generate_rs_api(&ir)?;
Lukasz Anforowiczc503af42022-03-04 23:18:15 +00004185 assert_rs_matches!(
4186 rs_api,
4187 quote! {
4188 #[doc = " MyTypedefDecl doc comment"]
4189 pub type MyTypedefDecl = i32;
4190 }
4191 );
Googler6a0a5252022-01-11 14:08:09 +00004192 assert_rs_matches!(rs_api, quote! { pub type MyTypeAliasDecl = i32; });
4193 assert_rs_matches!(rs_api, quote! { pub type MyTypeAliasDecl_Alias = MyTypeAliasDecl; });
4194 assert_rs_matches!(rs_api, quote! { pub type S_Alias = S; });
4195 assert_rs_matches!(rs_api, quote! { pub type S_Alias_Alias = S_Alias; });
4196 assert_rs_matches!(rs_api, quote! { pub fn f(t: MyTypedefDecl) });
4197 assert_cc_matches!(
4198 generate_rs_api_impl(&ir)?,
4199 quote! {
Devin Jeanpierredeea7892022-03-29 02:13:31 -07004200 extern "C" void __rust_thunk___Z1fi(MyTypedefDecl t){ f (std::forward<decltype(t)>(t)) ; }
Googler6a0a5252022-01-11 14:08:09 +00004201 }
4202 );
Googlerdcca7f72022-01-10 12:30:43 +00004203 Ok(())
4204 }
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00004205
4206 #[test]
4207 fn test_rs_type_kind_implements_copy() -> Result<()> {
Lukasz Anforowicz20651e32022-02-10 14:52:15 +00004208 let template = r#" LIFETIMES
Devin Jeanpierre88343c72022-01-15 01:10:23 +00004209 struct [[clang::trivial_abi]] TrivialStruct final { int i; };
4210 struct [[clang::trivial_abi]] UserDefinedCopyConstructor final {
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00004211 UserDefinedCopyConstructor(const UserDefinedCopyConstructor&);
4212 };
4213 using IntAlias = int;
4214 using TrivialAlias = TrivialStruct;
4215 using NonTrivialAlias = UserDefinedCopyConstructor;
4216 void func(PARAM_TYPE some_param);
4217 "#;
4218 assert_impl_all!(i32: Copy);
4219 assert_impl_all!(&i32: Copy);
4220 assert_not_impl_all!(&mut i32: Copy);
Lukasz Anforowicz20651e32022-02-10 14:52:15 +00004221 assert_impl_all!(Option<&i32>: Copy);
4222 assert_not_impl_all!(Option<&mut i32>: Copy);
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00004223 assert_impl_all!(*const i32: Copy);
4224 assert_impl_all!(*mut i32: Copy);
Lukasz Anforowicz20651e32022-02-10 14:52:15 +00004225 struct Test {
4226 // Test inputs:
4227 cc: &'static str,
4228 lifetimes: bool,
4229 // Expected test outputs:
4230 rs: &'static str,
4231 is_copy: bool,
4232 }
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00004233 let tests = vec![
4234 // Validity of the next few tests is verified via
4235 // `assert_[not_]impl_all!` static assertions above.
Lukasz Anforowicz20651e32022-02-10 14:52:15 +00004236 Test { cc: "int", lifetimes: true, rs: "i32", is_copy: true },
4237 Test { cc: "const int&", lifetimes: true, rs: "&'a i32", is_copy: true },
4238 Test { cc: "int&", lifetimes: true, rs: "&'a mut i32", is_copy: false },
4239 Test { cc: "const int*", lifetimes: true, rs: "Option<&'a i32>", is_copy: true },
4240 Test { cc: "int*", lifetimes: true, rs: "Option<&'a mut i32>", is_copy: false },
4241 Test { cc: "const int*", lifetimes: false, rs: "*const i32", is_copy: true },
4242 Test { cc: "int*", lifetimes: false, rs: "*mut i32", is_copy: true },
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00004243 // Tests below have been thought-through and verified "manually".
Lukasz Anforowicz20651e32022-02-10 14:52:15 +00004244 // TrivialStruct is expected to derive Copy.
4245 Test { cc: "TrivialStruct", lifetimes: true, rs: "TrivialStruct", is_copy: true },
4246 Test {
4247 cc: "UserDefinedCopyConstructor",
4248 lifetimes: true,
4249 rs: "UserDefinedCopyConstructor",
4250 is_copy: false,
4251 },
4252 Test { cc: "IntAlias", lifetimes: true, rs: "IntAlias", is_copy: true },
4253 Test { cc: "TrivialAlias", lifetimes: true, rs: "TrivialAlias", is_copy: true },
4254 Test { cc: "NonTrivialAlias", lifetimes: true, rs: "NonTrivialAlias", is_copy: false },
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00004255 ];
Lukasz Anforowicz20651e32022-02-10 14:52:15 +00004256 for test in tests.iter() {
4257 let test_name = format!("cc='{}', lifetimes={}", test.cc, test.lifetimes);
4258 let cc_input = template.replace("PARAM_TYPE", test.cc).replace(
4259 "LIFETIMES",
4260 if test.lifetimes { "#pragma clang lifetime_elision" } else { "" },
4261 );
4262 let ir = ir_from_cc(&cc_input)?;
Lukasz Anforowicz9c663ca2022-02-09 01:33:31 +00004263 let f = retrieve_func(&ir, "func");
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00004264 let t = RsTypeKind::new(&f.params[0].type_.rs_type, &ir)?;
Lukasz Anforowicz20651e32022-02-10 14:52:15 +00004265
Devin Jeanpierre92ca2612022-04-06 11:35:13 -07004266 let fmt = tokens_to_string(t.to_token_stream())?;
Lukasz Anforowicz20651e32022-02-10 14:52:15 +00004267 assert_eq!(test.rs, fmt, "Testing: {}", test_name);
4268
4269 assert_eq!(test.is_copy, t.implements_copy(), "Testing: {}", test_name);
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00004270 }
4271 Ok(())
4272 }
Lukasz Anforowicza94ab702022-01-14 22:40:25 +00004273
4274 #[test]
4275 fn test_rs_type_kind_is_shared_ref_to_with_lifetimes() -> Result<()> {
4276 let ir = ir_from_cc(
4277 "#pragma clang lifetime_elision
4278 struct SomeStruct {};
4279 void foo(const SomeStruct& foo_param);
4280 void bar(SomeStruct& bar_param);",
4281 )?;
4282 let record = ir.records().next().unwrap();
Lukasz Anforowicz9c663ca2022-02-09 01:33:31 +00004283 let foo_func = retrieve_func(&ir, "foo");
4284 let bar_func = retrieve_func(&ir, "bar");
Lukasz Anforowicza94ab702022-01-14 22:40:25 +00004285
4286 // const-ref + lifetimes in C++ ===> shared-ref in Rust
4287 assert_eq!(foo_func.params.len(), 1);
4288 let foo_param = &foo_func.params[0];
4289 assert_eq!(&foo_param.identifier.identifier, "foo_param");
4290 let foo_type = RsTypeKind::new(&foo_param.type_.rs_type, &ir)?;
4291 assert!(foo_type.is_shared_ref_to(record));
4292 assert!(matches!(foo_type, RsTypeKind::Reference { mutability: Mutability::Const, .. }));
4293
4294 // non-const-ref + lifetimes in C++ ===> mutable-ref in Rust
4295 assert_eq!(bar_func.params.len(), 1);
4296 let bar_param = &bar_func.params[0];
4297 assert_eq!(&bar_param.identifier.identifier, "bar_param");
4298 let bar_type = RsTypeKind::new(&bar_param.type_.rs_type, &ir)?;
4299 assert!(!bar_type.is_shared_ref_to(record));
4300 assert!(matches!(bar_type, RsTypeKind::Reference { mutability: Mutability::Mut, .. }));
4301
4302 Ok(())
4303 }
4304
4305 #[test]
4306 fn test_rs_type_kind_is_shared_ref_to_without_lifetimes() -> Result<()> {
4307 let ir = ir_from_cc(
4308 "struct SomeStruct {};
4309 void foo(const SomeStruct& foo_param);",
4310 )?;
4311 let record = ir.records().next().unwrap();
Lukasz Anforowicz9c663ca2022-02-09 01:33:31 +00004312 let foo_func = retrieve_func(&ir, "foo");
Lukasz Anforowicza94ab702022-01-14 22:40:25 +00004313
4314 // const-ref + *no* lifetimes in C++ ===> const-pointer in Rust
4315 assert_eq!(foo_func.params.len(), 1);
4316 let foo_param = &foo_func.params[0];
4317 assert_eq!(&foo_param.identifier.identifier, "foo_param");
4318 let foo_type = RsTypeKind::new(&foo_param.type_.rs_type, &ir)?;
4319 assert!(!foo_type.is_shared_ref_to(record));
4320 assert!(matches!(foo_type, RsTypeKind::Pointer { mutability: Mutability::Const, .. }));
4321
4322 Ok(())
4323 }
Marcel Hlopkoeaae9b72022-01-21 15:54:11 +00004324
4325 #[test]
Lukasz Anforowicz90bdb962022-02-14 21:07:45 +00004326 fn test_rs_type_kind_dfs_iter_ordering() {
4327 // Set up a test input representing: A<B<C>, D<E>>.
4328 let a = {
4329 let b = {
4330 let c = RsTypeKind::Other { name: "C", type_args: vec![] };
4331 RsTypeKind::Other { name: "B", type_args: vec![c] }
4332 };
4333 let d = {
4334 let e = RsTypeKind::Other { name: "E", type_args: vec![] };
4335 RsTypeKind::Other { name: "D", type_args: vec![e] }
4336 };
4337 RsTypeKind::Other { name: "A", type_args: vec![b, d] }
4338 };
4339 let dfs_names = a
4340 .dfs_iter()
4341 .map(|t| match t {
4342 RsTypeKind::Other { name, .. } => *name,
4343 _ => unreachable!("Only 'other' types are used in this test"),
4344 })
4345 .collect_vec();
4346 assert_eq!(vec!["A", "B", "C", "D", "E"], dfs_names);
4347 }
4348
4349 #[test]
Lukasz Anforowiczcf230fd2022-02-18 19:20:39 +00004350 fn test_rs_type_kind_dfs_iter_ordering_for_func_ptr() {
4351 // Set up a test input representing: fn(A, B) -> C
4352 let f = {
4353 let a = RsTypeKind::Other { name: "A", type_args: vec![] };
4354 let b = RsTypeKind::Other { name: "B", type_args: vec![] };
4355 let c = RsTypeKind::Other { name: "C", type_args: vec![] };
4356 RsTypeKind::FuncPtr { abi: "blah", param_types: vec![a, b], return_type: Box::new(c) }
4357 };
4358 let dfs_names = f
4359 .dfs_iter()
4360 .map(|t| match t {
4361 RsTypeKind::FuncPtr { .. } => "fn",
4362 RsTypeKind::Other { name, .. } => *name,
4363 _ => unreachable!("Only FuncPtr and Other kinds are used in this test"),
4364 })
4365 .collect_vec();
4366 assert_eq!(vec!["fn", "A", "B", "C"], dfs_names);
4367 }
4368
4369 #[test]
Lukasz Anforowicz90bdb962022-02-14 21:07:45 +00004370 fn test_rs_type_kind_lifetimes() -> Result<()> {
4371 let ir = ir_from_cc(
4372 r#"
4373 #pragma clang lifetime_elision
4374 using TypeAlias = int&;
4375 struct SomeStruct {};
4376 void foo(int a, int& b, int* c, int** d, TypeAlias e, SomeStruct f); "#,
4377 )?;
4378 let f = retrieve_func(&ir, "foo");
4379 let ret = RsTypeKind::new(&f.return_type.rs_type, &ir)?;
4380 let a = RsTypeKind::new(&f.params[0].type_.rs_type, &ir)?;
4381 let b = RsTypeKind::new(&f.params[1].type_.rs_type, &ir)?;
4382 let c = RsTypeKind::new(&f.params[2].type_.rs_type, &ir)?;
4383 let d = RsTypeKind::new(&f.params[3].type_.rs_type, &ir)?;
4384 let e = RsTypeKind::new(&f.params[4].type_.rs_type, &ir)?;
4385 let f = RsTypeKind::new(&f.params[5].type_.rs_type, &ir)?;
4386
4387 assert_eq!(0, ret.lifetimes().count()); // No lifetimes on `void`.
4388 assert_eq!(0, a.lifetimes().count()); // No lifetimes on `int`.
4389 assert_eq!(1, b.lifetimes().count()); // `&'a i32` has a single lifetime.
4390 assert_eq!(1, c.lifetimes().count()); // `Option<&'b i32>` has a single lifetime.
4391 assert_eq!(2, d.lifetimes().count()); // `&'c Option<&'d i32>` has two lifetimes.
4392 assert_eq!(1, e.lifetimes().count()); // Lifetime of underlying type should show through.
4393 assert_eq!(0, f.lifetimes().count()); // No lifetimes on structs (yet).
4394 Ok(())
4395 }
4396
4397 #[test]
4398 fn test_rs_type_kind_lifetimes_raw_ptr() -> Result<()> {
4399 let ir = ir_from_cc("void foo(int* a);")?;
4400 let f = retrieve_func(&ir, "foo");
4401 let a = RsTypeKind::new(&f.params[0].type_.rs_type, &ir)?;
4402 assert_eq!(0, a.lifetimes().count()); // No lifetimes on `int*`.
4403 Ok(())
4404 }
4405
4406 #[test]
Marcel Hlopkoeaae9b72022-01-21 15:54:11 +00004407 fn test_rust_keywords_are_escaped_in_rs_api_file() -> Result<()> {
4408 let ir = ir_from_cc("struct type { int dyn; };")?;
4409 let rs_api = generate_rs_api(&ir)?;
4410 assert_rs_matches!(rs_api, quote! { struct r#type { ... r#dyn: i32 ... } });
4411 Ok(())
4412 }
4413
4414 #[test]
4415 fn test_rust_keywords_are_not_escaped_in_rs_api_impl_file() -> Result<()> {
4416 let ir = ir_from_cc("struct type { int dyn; };")?;
4417 let rs_api_impl = generate_rs_api_impl(&ir)?;
4418 assert_cc_matches!(rs_api_impl, quote! { static_assert(offsetof(class type, dyn) ... ) });
4419 Ok(())
4420 }
Marcel Hlopko14ee3c82022-02-09 09:46:23 +00004421
4422 #[test]
4423 fn test_no_aligned_attr() {
4424 let ir = ir_from_cc("struct SomeStruct {};").unwrap();
4425 let rs_api = generate_rs_api(&ir).unwrap();
4426
4427 assert_rs_matches! {rs_api, quote! {
4428 #[repr(C)]
4429 pub struct SomeStruct { ... }
4430 }};
4431 }
4432
4433 #[test]
4434 fn test_aligned_attr() {
4435 let ir = ir_from_cc("struct SomeStruct {} __attribute__((aligned(64)));").unwrap();
4436 let rs_api = generate_rs_api(&ir).unwrap();
4437
4438 assert_rs_matches! {rs_api, quote! {
4439 #[repr(C, align(64))]
4440 pub struct SomeStruct { ... }
4441 }
4442 };
4443 }
Devin Jeanpierre149950d2022-02-22 21:02:02 +00004444
4445 /// !Unpin references should not be pinned.
4446 #[test]
4447 fn test_nonunpin_ref_param() -> Result<()> {
4448 let rs_api_impl = generate_rs_api(&ir_from_cc(
4449 r#"
4450 #pragma clang lifetime_elision
4451 struct S {~S();};
4452 void Function(const S& s);
4453 "#,
4454 )?)?;
4455 assert_rs_matches!(
4456 rs_api_impl,
4457 quote! {
4458 fn Function<'a>(s: &'a S) { ... }
4459 }
4460 );
4461 Ok(())
4462 }
4463
4464 /// !Unpin mut references must be pinned.
4465 #[test]
4466 fn test_nonunpin_mut_param() -> Result<()> {
4467 let rs_api_impl = generate_rs_api(&ir_from_cc(
4468 r#"
4469 #pragma clang lifetime_elision
4470 struct S {~S();};
4471 void Function(S& s);
4472 "#,
4473 )?)?;
4474 assert_rs_matches!(
4475 rs_api_impl,
4476 quote! {
Rosica Dejanovskaaccf00b2022-04-01 13:28:04 -07004477 fn Function<'a>(s: rust_std::pin::Pin<&'a mut S>) { ... }
Devin Jeanpierre149950d2022-02-22 21:02:02 +00004478 }
4479 );
4480 Ok(())
4481 }
4482
4483 /// !Unpin &self should not be pinned.
4484 #[test]
4485 fn test_nonunpin_ref_self() -> Result<()> {
4486 let rs_api_impl = generate_rs_api(&ir_from_cc(
4487 r#"
4488 #pragma clang lifetime_elision
4489 struct S {
4490 ~S();
4491 void Function() const;
4492 };
4493 "#,
4494 )?)?;
4495 assert_rs_matches!(
4496 rs_api_impl,
4497 quote! {
4498 fn Function<'a>(&'a self) { ... }
4499 }
4500 );
4501 Ok(())
4502 }
4503
4504 /// !Unpin &mut self must be pinned.
4505 #[test]
4506 fn test_nonunpin_mut_self() -> Result<()> {
4507 let rs_api_impl = generate_rs_api(&ir_from_cc(
4508 r#"
4509 #pragma clang lifetime_elision
4510 struct S {
4511 ~S();
4512 void Function();
4513 };
4514 "#,
4515 )?)?;
4516 assert_rs_matches!(
4517 rs_api_impl,
4518 quote! {
Rosica Dejanovskaaccf00b2022-04-01 13:28:04 -07004519 fn Function<'a>(self: rust_std::pin::Pin<&'a mut Self>) { ... }
Devin Jeanpierre149950d2022-02-22 21:02:02 +00004520 }
4521 );
4522 Ok(())
4523 }
4524
4525 /// Drop::drop must not use self : Pin<...>.
4526 #[test]
4527 fn test_nonunpin_drop() -> Result<()> {
4528 let rs_api_impl = generate_rs_api(&ir_from_cc(
4529 r#"
4530 struct S {~S();};
4531 "#,
4532 )?)?;
4533 assert_rs_matches!(
4534 rs_api_impl,
4535 quote! {
4536 fn drop(&mut self) { ... }
4537 }
4538 );
4539 Ok(())
4540 }
Devin Jeanpierre6f607372022-03-22 21:34:38 +00004541
4542 #[test]
4543 fn test_nonunpin_one_arg_constructor() -> Result<()> {
4544 let ir = ir_from_cc(
4545 r#"#pragma clang lifetime_elision
4546 // This type must be `!Unpin`.
4547 struct HasConstructor {explicit HasConstructor(unsigned char input) {}};"#,
4548 )?;
4549 let rs_api = generate_rs_api(&ir)?;
4550 assert_rs_matches!(rs_api, quote! {impl !Unpin for HasConstructor {} });
4551 assert_rs_matches!(
4552 rs_api,
4553 quote! {
4554 impl ctor::CtorNew<u8> for HasConstructor {
4555 type CtorType = impl ctor::Ctor<Output = Self>;
4556
4557 #[inline (always)]
4558 fn ctor_new(input: u8) -> Self::CtorType {
Rosica Dejanovskaaccf00b2022-04-01 13:28:04 -07004559 ctor::FnCtor::new(move |dest: rust_std::pin::Pin<&mut rust_std::mem::MaybeUninit<Self>>| {
Devin Jeanpierre6f607372022-03-22 21:34:38 +00004560 unsafe {
Rosica Dejanovskaaccf00b2022-04-01 13:28:04 -07004561 crate::detail::__rust_thunk___ZN14HasConstructorC1Eh(rust_std::pin::Pin::into_inner_unchecked(dest), input);
Devin Jeanpierre6f607372022-03-22 21:34:38 +00004562 }
4563 })
4564 }
4565 }
4566 }
4567 );
4568 Ok(())
4569 }
Marcel Hlopko42abfc82021-08-09 07:03:17 +00004570}