blob: e7a0b305f6e5b30654e601137e0c74b2f328f298 [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
Michael VanBemmelf5cbdf42022-10-14 17:00:11 -07005use arc_anyhow::{Context, Result};
Lukasz Anforowicz434c4692022-11-01 14:05:24 -07006use code_gen_utils::{format_cc_includes, CcInclude};
Michael VanBemmelf5cbdf42022-10-14 17:00:11 -07007use error_report::{anyhow, bail, ensure, ErrorReport, ErrorReporting, IgnoreErrors};
Marcel Hlopko884ae7f2021-08-18 13:58:22 +00008use ffi_types::*;
Marcel Hlopko42abfc82021-08-09 07:03:17 +00009use ir::*;
10use itertools::Itertools;
Michael VanBemmel5014b3e2022-08-03 16:27:34 -070011use once_cell::sync::Lazy;
Googler5ea88642021-09-29 08:05:59 +000012use proc_macro2::{Ident, Literal, TokenStream};
Devin Jeanpierre92ca2612022-04-06 11:35:13 -070013use quote::{format_ident, quote, ToTokens};
Michael VanBemmel32c26df2022-08-03 16:08:58 -070014use std::collections::{BTreeSet, HashMap, HashSet};
Lukasz Anforowicz54ff3182022-05-06 07:17:58 -070015use std::ffi::{OsStr, OsString};
Devin Jeanpierre6bb81802022-08-10 02:08:47 -070016use std::fmt::Write as _;
Michael Forster82c02d32022-05-20 21:47:33 -070017use std::iter::{self, Iterator};
Marcel Hlopko42abfc82021-08-09 07:03:17 +000018use std::panic::catch_unwind;
19use std::process;
Michael VanBemmel7a4d4c02022-07-27 13:21:47 -070020use std::ptr;
Devin Jeanpierre8cd8ae72022-06-29 18:59:40 -070021use std::rc::Rc;
Lukasz Anforowiczae9be082022-10-05 07:33:57 -070022use token_stream_printer::{
23 cc_tokens_to_formatted_string, rs_tokens_to_formatted_string, RustfmtConfig,
24};
Marcel Hlopko42abfc82021-08-09 07:03:17 +000025
Marcel Hlopko45fba972021-08-23 19:52:20 +000026/// FFI equivalent of `Bindings`.
27#[repr(C)]
28pub struct FfiBindings {
29 rs_api: FfiU8SliceBox,
30 rs_api_impl: FfiU8SliceBox,
Michael VanBemmelf5cbdf42022-10-14 17:00:11 -070031 error_report: FfiU8SliceBox,
Marcel Hlopko45fba972021-08-23 19:52:20 +000032}
33
34/// Deserializes IR from `json` and generates bindings source code.
Marcel Hlopko42abfc82021-08-09 07:03:17 +000035///
36/// This function panics on error.
37///
Lukasz Anforowiczdd9ae0f2022-02-17 15:52:53 +000038/// # Safety
39///
40/// Expectations:
Lukasz Anforowicz54ff3182022-05-06 07:17:58 -070041/// * `json` should be a FfiU8Slice for a valid array of bytes with the given
42/// size.
Lukasz Anforowiczdd907702022-05-06 09:24:07 -070043/// * `crubit_support_path` should be a FfiU8Slice for a valid array of bytes
44/// representing an UTF8-encoded string
Lukasz Anforowiczd7d68f02022-05-26 07:41:02 -070045/// * `rustfmt_exe_path` and `rustfmt_config_path` should both be a
46/// FfiU8Slice for a valid array of bytes representing an UTF8-encoded
47/// string (without the UTF-8 requirement, it seems that Rust doesn't offer
48/// a way to convert to OsString on Windows)
49/// * `json`, `crubit_support_path`, `rustfmt_exe_path`, and
50/// `rustfmt_config_path` shouldn't change during the call.
Lukasz Anforowiczdd9ae0f2022-02-17 15:52:53 +000051///
Marcel Hlopko42abfc82021-08-09 07:03:17 +000052/// Ownership:
Michael Forsterbee84482021-10-13 08:35:38 +000053/// * function doesn't take ownership of (in other words it borrows) the
Lukasz Anforowiczd7d68f02022-05-26 07:41:02 -070054/// input params: `json`, `crubit_support_path`, `rustfmt_exe_path`, and
55/// `rustfmt_config_path`
Marcel Hlopko42abfc82021-08-09 07:03:17 +000056/// * function passes ownership of the returned value to the caller
Marcel Hlopko42abfc82021-08-09 07:03:17 +000057#[no_mangle]
Lukasz Anforowicz54ff3182022-05-06 07:17:58 -070058pub unsafe extern "C" fn GenerateBindingsImpl(
59 json: FfiU8Slice,
Lukasz Anforowiczdd907702022-05-06 09:24:07 -070060 crubit_support_path: FfiU8Slice,
Lukasz Anforowiczd7d68f02022-05-26 07:41:02 -070061 rustfmt_exe_path: FfiU8Slice,
Lukasz Anforowicz54ff3182022-05-06 07:17:58 -070062 rustfmt_config_path: FfiU8Slice,
Michael VanBemmelf5cbdf42022-10-14 17:00:11 -070063 generate_error_report: bool,
Lukasz Anforowicz54ff3182022-05-06 07:17:58 -070064) -> FfiBindings {
65 let json: &[u8] = json.as_slice();
Lukasz Anforowiczdd907702022-05-06 09:24:07 -070066 let crubit_support_path: &str = std::str::from_utf8(crubit_support_path.as_slice()).unwrap();
Lukasz Anforowiczd7d68f02022-05-26 07:41:02 -070067 let rustfmt_exe_path: OsString =
68 std::str::from_utf8(rustfmt_exe_path.as_slice()).unwrap().into();
Lukasz Anforowicz54ff3182022-05-06 07:17:58 -070069 let rustfmt_config_path: OsString =
70 std::str::from_utf8(rustfmt_config_path.as_slice()).unwrap().into();
Marcel Hlopko42abfc82021-08-09 07:03:17 +000071 catch_unwind(|| {
Marcel Hlopko45fba972021-08-23 19:52:20 +000072 // It is ok to abort here.
Michael VanBemmelf5cbdf42022-10-14 17:00:11 -070073 let mut error_report;
74 let mut ignore_errors;
75 let errors: &mut dyn ErrorReporting = if generate_error_report {
76 error_report = ErrorReport::new();
77 &mut error_report
78 } else {
79 ignore_errors = IgnoreErrors;
80 &mut ignore_errors
81 };
82 let Bindings { rs_api, rs_api_impl } = generate_bindings(
83 json,
84 crubit_support_path,
85 &rustfmt_exe_path,
86 &rustfmt_config_path,
87 errors,
88 )
89 .unwrap();
Marcel Hlopko45fba972021-08-23 19:52:20 +000090 FfiBindings {
91 rs_api: FfiU8SliceBox::from_boxed_slice(rs_api.into_bytes().into_boxed_slice()),
92 rs_api_impl: FfiU8SliceBox::from_boxed_slice(
93 rs_api_impl.into_bytes().into_boxed_slice(),
94 ),
Michael VanBemmelf5cbdf42022-10-14 17:00:11 -070095 error_report: FfiU8SliceBox::from_boxed_slice(
96 errors.serialize_to_vec().unwrap().into_boxed_slice(),
97 ),
Marcel Hlopko45fba972021-08-23 19:52:20 +000098 }
Marcel Hlopko42abfc82021-08-09 07:03:17 +000099 })
100 .unwrap_or_else(|_| process::abort())
101}
102
Devin Jeanpierre52a14c32022-06-29 19:12:11 -0700103#[salsa::query_group(BindingsGeneratorStorage)]
104trait BindingsGenerator {
105 #[salsa::input]
106 fn ir(&self) -> Rc<IR>;
107
Devin Jeanpierre3a0cc5a2022-07-12 09:36:34 -0700108 fn rs_type_kind(&self, rs_type: RsType) -> Result<RsTypeKind>;
Devin Jeanpierre409f6f62022-07-07 02:00:26 -0700109
Devin Jeanpierre52a14c32022-06-29 19:12:11 -0700110 fn generate_func(
111 &self,
112 func: Rc<Func>,
Lukasz Anforowiczf8cfa562022-09-22 11:06:43 -0700113 ) -> Result<Option<Rc<(RsSnippet, RsSnippet, Rc<FunctionId>)>>>;
Devin Jeanpierreab85d442022-06-29 19:16:41 -0700114
115 fn overloaded_funcs(&self) -> Rc<HashSet<Rc<FunctionId>>>;
Felipe de A. Mello Pereira69736292022-10-11 02:03:43 -0700116
Felipe de A. Mello Pereiraec5f22c2022-11-02 04:04:01 -0700117 fn is_record_clonable(&self, record: Rc<Record>) -> bool;
118
Felipe de A. Mello Pereira69736292022-10-11 02:03:43 -0700119 // TODO(b/236687702): convert the get_binding function into a query once
120 // ImplKind implements Eq.
Devin Jeanpierre52a14c32022-06-29 19:12:11 -0700121}
122
123#[salsa::database(BindingsGeneratorStorage)]
124#[derive(Default)]
125struct Database {
126 storage: salsa::Storage<Self>,
127}
128
129impl salsa::Database for Database {}
130
Marcel Hlopko45fba972021-08-23 19:52:20 +0000131/// Source code for generated bindings.
132struct Bindings {
133 // Rust source code.
134 rs_api: String,
135 // C++ source code.
136 rs_api_impl: String,
Marcel Hlopko42abfc82021-08-09 07:03:17 +0000137}
138
Devin Jeanpierre4f06f832022-04-26 15:51:30 -0700139/// Source code for generated bindings, as tokens.
140struct BindingsTokens {
141 // Rust source code.
142 rs_api: TokenStream,
143 // C++ source code.
144 rs_api_impl: TokenStream,
145}
146
Lukasz Anforowiczdd907702022-05-06 09:24:07 -0700147fn generate_bindings(
148 json: &[u8],
149 crubit_support_path: &str,
Lukasz Anforowiczd7d68f02022-05-26 07:41:02 -0700150 rustfmt_exe_path: &OsStr,
Lukasz Anforowiczdd907702022-05-06 09:24:07 -0700151 rustfmt_config_path: &OsStr,
Michael VanBemmelf5cbdf42022-10-14 17:00:11 -0700152 errors: &mut dyn ErrorReporting,
Lukasz Anforowiczdd907702022-05-06 09:24:07 -0700153) -> Result<Bindings> {
Devin Jeanpierree9850a72022-06-29 19:04:48 -0700154 let ir = Rc::new(deserialize_ir(json)?);
Marcel Hlopkoca84ff42021-12-09 14:15:14 +0000155
Lukasz Anforowiczdd907702022-05-06 09:24:07 -0700156 let BindingsTokens { rs_api, rs_api_impl } =
Michael VanBemmelf5cbdf42022-10-14 17:00:11 -0700157 generate_bindings_tokens(ir.clone(), crubit_support_path, errors)?;
Lukasz Anforowicz54ff3182022-05-06 07:17:58 -0700158 let rs_api = {
Lukasz Anforowiczd7d68f02022-05-26 07:41:02 -0700159 let rustfmt_config = RustfmtConfig::new(rustfmt_exe_path, rustfmt_config_path);
Lukasz Anforowicz54ff3182022-05-06 07:17:58 -0700160 rs_tokens_to_formatted_string(rs_api, &rustfmt_config)?
161 };
Lukasz Anforowicz19e057d2022-10-28 12:03:23 -0700162 let rs_api_impl = cc_tokens_to_formatted_string(rs_api_impl)?;
Lukasz Anforowicz54ff3182022-05-06 07:17:58 -0700163
Lukasz Anforowicz19e057d2022-10-28 12:03:23 -0700164 // Add top-level comments that help identify where the generated bindings came
165 // from.
166 let top_level_comment = {
167 // The "@generated" marker is an informal convention for identifying
168 // automatically generated code. This marker is recognized by `rustfmt`
169 // (see the `format_generated_files` option [1]) and some other tools.
170 // For more info see https://generated.at/.
171 //
172 // [1]
173 // https://rust-lang.github.io/rustfmt/?version=v1.4.38&search=#format_generated_files
174 //
175 // TODO(b/255784681): It would be nice to include "by $argv[0]"" in the
176 // @generated comment below. OTOH, `std::env::current_exe()` in our
177 // current build environment returns a guid-like path... :-/
178 //
179 // TODO(b/255784681): Consider including cmdline arguments.
180 let target = &ir.current_target().0;
181 format!(
182 "// Automatically @generated Rust bindings for the following C++ target:\n\
183 // {target}\n"
184 )
185 };
Lukasz Anforowiczb46f5f42022-10-28 15:33:34 -0700186 let rs_api = format!("{top_level_comment}\n{rs_api}");
187 let rs_api_impl = format!("{top_level_comment}\n{rs_api_impl}");
Marcel Hlopkoca84ff42021-12-09 14:15:14 +0000188
Marcel Hlopko45fba972021-08-23 19:52:20 +0000189 Ok(Bindings { rs_api, rs_api_impl })
190}
191
Devin Jeanpierre6d5e7cc2021-10-21 12:56:07 +0000192/// Rust source code with attached information about how to modify the parent
193/// crate.
Devin Jeanpierre273eeae2021-10-06 13:29:35 +0000194///
Michael Forsterbee84482021-10-13 08:35:38 +0000195/// For example, the snippet `vec![].into_raw_parts()` is not valid unless the
196/// `vec_into_raw_parts` feature is enabled. So such a snippet should be
197/// represented as:
Devin Jeanpierre273eeae2021-10-06 13:29:35 +0000198///
199/// ```
200/// RsSnippet {
Marcel Hlopkoeaae9b72022-01-21 15:54:11 +0000201/// features: btree_set![make_rs_ident("vec_into_raw_parts")],
Devin Jeanpierre273eeae2021-10-06 13:29:35 +0000202/// tokens: quote!{vec![].into_raw_parts()},
203/// }
204/// ```
Lukasz Anforowiczeb19ac62022-02-05 00:10:16 +0000205#[derive(Clone, Debug)]
Devin Jeanpierre273eeae2021-10-06 13:29:35 +0000206struct RsSnippet {
207 /// Rust feature flags used by this snippet.
208 features: BTreeSet<Ident>,
209 /// The snippet itself, as a token stream.
210 tokens: TokenStream,
211}
212
213impl From<TokenStream> for RsSnippet {
214 fn from(tokens: TokenStream) -> Self {
215 RsSnippet { features: BTreeSet::new(), tokens }
216 }
217}
218
Lukasz Anforowiczf8cfa562022-09-22 11:06:43 -0700219impl Eq for RsSnippet {}
220
221impl PartialEq for RsSnippet {
222 fn eq(&self, other: &Self) -> bool {
223 fn to_comparable_tuple(_x: &RsSnippet) -> (&BTreeSet<Ident>, String) {
224 // TokenStream doesn't implement `PartialEq`, so we convert to an equivalent
225 // `String`. This is a bit expensive, but should be okay (especially
226 // given that this code doesn't execute at this point). Having a
227 // working `impl PartialEq` helps `salsa` reuse unchanged memoized
228 // results of previous computations (although this is a bit
229 // theoretical, since right now we don't re-set `salsa`'s inputs - we only call
230 // `set_ir` once).
231 //
232 // TODO(lukasza): If incremental `salsa` computations are ever used in the
233 // future, we may end up hitting the `panic!` below. At that point
234 // it should be okay to just remove the `panic!`, but we should also
235 // 1) think about improving performance of comparing `TokenStream`
236 // for equality and 2) add unit tests covering this `PartialEq` `impl`.
237 panic!("This code is not expected to execute in practice");
238 #[allow(unreachable_code)]
239 (&_x.features, _x.tokens.to_string())
240 }
241 to_comparable_tuple(self) == to_comparable_tuple(other)
242 }
243}
244
Michael Forsterbee84482021-10-13 08:35:38 +0000245/// If we know the original C++ function is codegenned and already compatible
246/// with `extern "C"` calling convention we skip creating/calling the C++ thunk
247/// since we can call the original C++ directly.
Devin Jeanpierreb8ce2c12022-07-13 10:34:01 -0700248fn can_skip_cc_thunk(db: &dyn BindingsGenerator, func: &Func) -> bool {
Devin Jeanpierre96839c12021-12-14 00:27:38 +0000249 // ## Inline functions
250 //
Michael Forsterbee84482021-10-13 08:35:38 +0000251 // Inline functions may not be codegenned in the C++ library since Clang doesn't
252 // know if Rust calls the function or not. Therefore in order to make inline
253 // functions callable from Rust we need to generate a C++ file that defines
254 // a thunk that delegates to the original inline function. When compiled,
255 // Clang will emit code for this thunk and Rust code will call the
Marcel Hlopko3164eee2021-08-24 20:09:22 +0000256 // thunk when the user wants to call the original inline function.
257 //
Michael Forsterbee84482021-10-13 08:35:38 +0000258 // This is not great runtime-performance-wise in regular builds (inline function
259 // will not be inlined, there will always be a function call), but it is
260 // correct. ThinLTO builds will be able to see through the thunk and inline
261 // code across the language boundary. For non-ThinLTO builds we plan to
262 // implement <internal link> which removes the runtime performance overhead.
Devin Jeanpierre96839c12021-12-14 00:27:38 +0000263 if func.is_inline {
264 return false;
265 }
Lukasz Anforowiczb1ff2e52022-05-16 10:54:23 -0700266 // ## Member functions (or descendants) of class templates
267 //
268 // A thunk is required to force/guarantee template instantiation.
269 if func.is_member_or_descendant_of_class_template {
270 return false;
271 }
Devin Jeanpierre96839c12021-12-14 00:27:38 +0000272 // ## Virtual functions
273 //
274 // When calling virtual `A::Method()`, it's not necessarily the case that we'll
275 // specifically call the concrete `A::Method` impl. For example, if this is
276 // called on something whose dynamic type is some subclass `B` with an
277 // overridden `B::Method`, then we'll call that.
278 //
279 // We must reuse the C++ dynamic dispatching system. In this case, the easiest
280 // way to do it is by resorting to a C++ thunk, whose implementation will do
281 // the lookup.
282 //
283 // In terms of runtime performance, since this only occurs for virtual function
284 // calls, which are already slow, it may not be such a big deal. We can
285 // benchmark it later. :)
286 if let Some(meta) = &func.member_func_metadata {
287 if let Some(inst_meta) = &meta.instance_method_metadata {
288 if inst_meta.is_virtual {
289 return false;
290 }
291 }
292 }
Lukasz Anforowicz0b6a6ac2022-03-22 22:32:23 +0000293 // ## Custom calling convention requires a thunk.
294 //
295 // The thunk has the "C" calling convention, and internally can call the
296 // C++ function using any of the calling conventions supported by the C++
297 // compiler (which might not always match the set supported by Rust - e.g.,
298 // abi.rs doesn't contain "swiftcall" from
299 // clang::FunctionType::getNameForCallConv)
300 if !func.has_c_calling_convention {
301 return false;
302 }
Devin Jeanpierre96839c12021-12-14 00:27:38 +0000303
Devin Jeanpierreb8ce2c12022-07-13 10:34:01 -0700304 // ## Nontrivial return types.
305 //
306 // If the function returns a value which is nontrivial for the purpose of calls,
307 // then in the underlying ABI, it is actually returned via a hidden pointer
308 // parameter not exposed anywhere in the Clang AST or the Crubit IR. For
309 // now, this is worked around via an _explicit_ output parameter, used in
310 // the thunk, which cannot be skipped anymore.
311 //
312 // Note: if the RsTypeKind cannot be parsed / rs_type_kind returns Err, then
313 // bindings generation will fail for this function, so it doesn't really matter
314 // what we do here.
315 if let Ok(return_type) = db.rs_type_kind(func.return_type.rs_type.clone()) {
316 if !return_type.is_unpin() {
317 return false;
318 }
319 }
Devin Jeanpierre11ce2b92022-07-27 07:54:21 -0700320 // ## Nontrivial parameter types.
321 //
322 // If the function accepts a value which is nontrivial for the purpose of calls,
323 // then in the underlying ABI, it is actually passed by pointer.
324 //
325 // Because there's no way to upgrade an lvalue (e.g. pointer) to a prvalue, we
326 // cannot implement guaranteed copy/move elision for inline functions for
327 // now: any thunk we generate would need to invoke the correct function as
328 // if by magic.
329 //
330 // And so for now, we always use C++11 semantics, via an intermediate thunk.
331 //
332 // (As a side effect, this, like return values, means that support is
333 // ABI-agnostic.)
334 for param in &func.params {
335 if let Ok(param_type) = db.rs_type_kind(param.type_.rs_type.clone()) {
336 if !param_type.is_unpin() {
337 return false;
338 }
339 }
340 }
Devin Jeanpierreb8ce2c12022-07-13 10:34:01 -0700341
Devin Jeanpierre96839c12021-12-14 00:27:38 +0000342 true
Marcel Hlopko3164eee2021-08-24 20:09:22 +0000343}
344
Googlerd03d05b2022-01-07 10:10:57 +0000345/// Uniquely identifies a generated Rust function.
Lukasz Anforowiczeb19ac62022-02-05 00:10:16 +0000346#[derive(Clone, Debug, PartialEq, Eq, Hash)]
Googlerd03d05b2022-01-07 10:10:57 +0000347struct FunctionId {
348 // If the function is on a trait impl, contains the name of the Self type for
349 // which the trait is being implemented.
350 self_type: Option<syn::Path>,
351 // Fully qualified path of the function. For functions in impl blocks, this
352 // includes the name of the type or trait on which the function is being
353 // implemented, e.g. `Default::default`.
354 function_path: syn::Path,
355}
356
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +0000357/// Returns the name of `func` in C++ syntax.
Googlerd03d05b2022-01-07 10:10:57 +0000358fn cxx_function_name(func: &Func, ir: &IR) -> Result<String> {
Devin Jeanpierrebdfb4d92022-06-17 01:17:01 -0700359 let record: Option<&str> = ir.record_for_member_func(func)?.map(|r| &*r.cc_name);
Googlerd03d05b2022-01-07 10:10:57 +0000360
361 let func_name = match &func.name {
362 UnqualifiedIdentifier::Identifier(id) => id.identifier.clone(),
Lukasz Anforowicz9c663ca2022-02-09 01:33:31 +0000363 UnqualifiedIdentifier::Operator(op) => op.cc_name(),
Googlerd03d05b2022-01-07 10:10:57 +0000364 UnqualifiedIdentifier::Destructor => {
365 format!("~{}", record.expect("destructor must be associated with a record"))
366 }
367 UnqualifiedIdentifier::Constructor => {
Lukasz Anforowiczeb19ac62022-02-05 00:10:16 +0000368 record.expect("constructor must be associated with a record").to_string()
Googlerd03d05b2022-01-07 10:10:57 +0000369 }
370 };
371
372 if let Some(record_name) = record {
373 Ok(format!("{}::{}", record_name, func_name))
374 } else {
375 Ok(func_name)
376 }
377}
378
Lukasz Anforowiczeb19ac62022-02-05 00:10:16 +0000379fn make_unsupported_fn(func: &Func, ir: &IR, message: impl ToString) -> Result<UnsupportedItem> {
Michael VanBemmelf5cbdf42022-10-14 17:00:11 -0700380 Ok(UnsupportedItem::new_with_message(
381 cxx_function_name(func, ir)?,
382 message.to_string(),
383 func.source_loc.clone(),
384 func.id,
385 ))
Lukasz Anforowiczeb19ac62022-02-05 00:10:16 +0000386}
387
Devin Jeanpierre5fecde52022-09-14 06:53:39 -0700388fn make_unsupported_nested_type_alias(type_alias: &TypeAlias) -> Result<UnsupportedItem> {
Michael VanBemmelf5cbdf42022-10-14 17:00:11 -0700389 Ok(UnsupportedItem::new_with_message(
Devin Jeanpierre5fecde52022-09-14 06:53:39 -0700390 // TODO(jeanpierreda): It would be nice to include the enclosing record name here too.
Michael VanBemmelf5cbdf42022-10-14 17:00:11 -0700391 type_alias.identifier.identifier.to_string(),
392 "Typedefs nested in classes are not supported yet".to_string(),
393 type_alias.source_loc.clone(),
394 type_alias.id,
395 ))
Devin Jeanpierre5fecde52022-09-14 06:53:39 -0700396}
397
Devin Jeanpierre215ee8e2022-05-16 16:09:41 -0700398/// The name of a one-function trait, with extra entries for
399/// specially-understood traits and families of traits.
Devin Jeanpierre8cd8ae72022-06-29 18:59:40 -0700400enum TraitName {
Devin Jeanpierread125742022-04-11 13:50:28 -0700401 /// The constructor trait for !Unpin types, with a list of parameter types.
402 /// For example, `CtorNew(vec![])` is the default constructor.
Devin Jeanpierre8cd8ae72022-06-29 18:59:40 -0700403 CtorNew(Vec<RsTypeKind>),
Devin Jeanpierree77fa7e2022-06-02 14:05:58 -0700404 /// An Unpin constructor trait, e.g. From or Clone, with a list of parameter
405 /// types.
Devin Jeanpierre8cd8ae72022-06-29 18:59:40 -0700406 UnpinConstructor { name: TokenStream, params: Vec<RsTypeKind> },
Felipe de A. Mello Pereira69736292022-10-11 02:03:43 -0700407 /// The PartialEq trait.
408 PartialEq { params: Vec<RsTypeKind> },
409 /// The PartialOrd trait.
410 PartialOrd { params: Vec<RsTypeKind> },
Devin Jeanpierre9c34da62022-03-31 04:57:16 -0700411 /// Any other trait, e.g. Eq.
Devin Jeanpierre8cd8ae72022-06-29 18:59:40 -0700412 Other { name: TokenStream, params: Vec<RsTypeKind>, is_unsafe_fn: bool },
Devin Jeanpierre9c34da62022-03-31 04:57:16 -0700413}
Devin Jeanpierree77fa7e2022-06-02 14:05:58 -0700414
Devin Jeanpierre8cd8ae72022-06-29 18:59:40 -0700415impl TraitName {
Devin Jeanpierree77fa7e2022-06-02 14:05:58 -0700416 /// Returns the generic parameters in this trait name.
Devin Jeanpierre8cd8ae72022-06-29 18:59:40 -0700417 fn params(&self) -> impl Iterator<Item = &RsTypeKind> {
Devin Jeanpierree77fa7e2022-06-02 14:05:58 -0700418 match self {
419 Self::CtorNew(params)
420 | Self::UnpinConstructor { params, .. }
Felipe de A. Mello Pereira69736292022-10-11 02:03:43 -0700421 | Self::PartialEq { params }
422 | Self::PartialOrd { params }
Devin Jeanpierree77fa7e2022-06-02 14:05:58 -0700423 | Self::Other { params, .. } => params.iter(),
424 }
425 }
426
427 /// Returns the lifetimes used in this trait name.
Devin Jeanpierre4e94a082022-08-10 01:58:35 -0700428 pub fn lifetimes(&self) -> impl Iterator<Item = Lifetime> + '_ {
Devin Jeanpierree77fa7e2022-06-02 14:05:58 -0700429 self.params().flat_map(|p| p.lifetimes())
430 }
431}
432
Devin Jeanpierre8cd8ae72022-06-29 18:59:40 -0700433impl ToTokens for TraitName {
Devin Jeanpierre9c34da62022-03-31 04:57:16 -0700434 fn to_tokens(&self, tokens: &mut TokenStream) {
435 match self {
Devin Jeanpierree77fa7e2022-06-02 14:05:58 -0700436 Self::UnpinConstructor { name, params } | Self::Other { name, params, .. } => {
Felipe de A. Mello Pereiraec5f22c2022-11-02 04:04:01 -0700437 let params = format_generic_params(/* lifetimes= */ &[], params);
Devin Jeanpierree77fa7e2022-06-02 14:05:58 -0700438 quote! {#name #params}.to_tokens(tokens)
439 }
Felipe de A. Mello Pereira69736292022-10-11 02:03:43 -0700440 Self::PartialEq { params } => {
Felipe de A. Mello Pereiraec5f22c2022-11-02 04:04:01 -0700441 let params = format_generic_params(/* lifetimes= */ &[], params);
Felipe de A. Mello Pereira69736292022-10-11 02:03:43 -0700442 quote! {PartialEq #params}.to_tokens(tokens)
443 }
444 Self::PartialOrd { params } => {
Felipe de A. Mello Pereiraec5f22c2022-11-02 04:04:01 -0700445 let params = format_generic_params(/* lifetimes= */ &[], params);
Felipe de A. Mello Pereira69736292022-10-11 02:03:43 -0700446 quote! {PartialOrd #params}.to_tokens(tokens)
447 }
Devin Jeanpierread125742022-04-11 13:50:28 -0700448 Self::CtorNew(arg_types) => {
449 let arg_types = format_tuple_except_singleton(arg_types);
Devin Jeanpierrecd5ebf32022-07-08 05:31:55 -0700450 quote! { ::ctor::CtorNew < #arg_types > }.to_tokens(tokens)
Devin Jeanpierread125742022-04-11 13:50:28 -0700451 }
Devin Jeanpierre9c34da62022-03-31 04:57:16 -0700452 }
453 }
454}
455
456/// The kind of the `impl` block the function needs to be generated in.
Devin Jeanpierre8cd8ae72022-06-29 18:59:40 -0700457enum ImplKind {
Devin Jeanpierre9c34da62022-03-31 04:57:16 -0700458 /// Used for free functions for which we don't want the `impl` block.
459 None { is_unsafe: bool },
460 /// Used for inherent methods for which we need an `impl SomeStruct { ... }`
461 /// block.
462 Struct {
463 /// For example, `SomeStruct`. Retrieved from
464 /// `func.member_func_metadata`.
465 record_name: Ident,
466 is_unsafe: bool,
467 /// Whether to format the first parameter as "self" (e.g. `__this:
468 /// &mut T` -> `&mut self`)
469 format_first_param_as_self: bool,
470 },
471 /// Used for trait methods for which we need an `impl TraitName for
472 /// SomeStruct { ... }` block.
473 Trait {
474 /// For example, `SomeStruct`.
475 /// Note that `record_name` might *not* be from
476 /// `func.member_func_metadata`.
477 record_name: Ident,
Lukasz Anforowicz92be29f2022-09-02 17:12:29 -0700478 record_qualifier: NamespaceQualifier,
Devin Jeanpierre9c34da62022-03-31 04:57:16 -0700479 /// For example, `quote!{ From<i32> }`.
Devin Jeanpierre8cd8ae72022-06-29 18:59:40 -0700480 trait_name: TraitName,
Michael VanBemmel106f66c2022-07-13 09:48:48 -0700481 /// Reference style for the `impl` block and self parameters.
482 impl_for: ImplFor,
Devin Jeanpierre9c34da62022-03-31 04:57:16 -0700483
Devin Jeanpierrea04bce12022-08-01 13:39:48 -0700484 /// The generic params of trait `impl` (e.g. `vec![quote!{'b}]`). These
485 /// start empty and only later are mutated into the correct value.
486 trait_generic_params: Vec<TokenStream>,
487
Devin Jeanpierre9c34da62022-03-31 04:57:16 -0700488 /// Whether to format the first parameter as "self" (e.g. `__this:
489 /// &mut T` -> `&mut self`)
490 format_first_param_as_self: bool,
Michael VanBemmelf6651302022-08-03 15:56:59 -0700491 /// Whether to drop the C++ function's return value and return unit
492 /// instead.
493 drop_return: bool,
Michael VanBemmel106f66c2022-07-13 09:48:48 -0700494
Devin Jeanpierrefd4ff822022-07-15 01:43:55 -0700495 /// If this trait's method returns an associated type, it has this name.
496 /// For example, this is `Output` on
497 /// [`Add`](https://doc.rust-lang.org/std/ops/trait.Add.html).
498 associated_return_type: Option<Ident>,
Felipe de A. Mello Pereiraec5f22c2022-11-02 04:04:01 -0700499
500 /// Whether args should always be const references in Rust, even if they
501 /// are by value in C++.
502 ///
503 /// For example, the traits for == and < only accept const reference
504 /// parameters, but C++ allows values.
505 force_const_reference_params: bool,
Devin Jeanpierre9c34da62022-03-31 04:57:16 -0700506 },
507}
Devin Jeanpierre8cd8ae72022-06-29 18:59:40 -0700508impl ImplKind {
Devin Jeanpierre9c34da62022-03-31 04:57:16 -0700509 fn new_trait(
Devin Jeanpierre8cd8ae72022-06-29 18:59:40 -0700510 trait_name: TraitName,
Lukasz Anforowicz92be29f2022-09-02 17:12:29 -0700511 record: &Record,
512 ir: &IR,
Devin Jeanpierre9c34da62022-03-31 04:57:16 -0700513 format_first_param_as_self: bool,
Felipe de A. Mello Pereiraec5f22c2022-11-02 04:04:01 -0700514 force_const_reference_params: bool,
Lukasz Anforowicz92be29f2022-09-02 17:12:29 -0700515 ) -> Result<Self> {
516 Ok(ImplKind::Trait {
Devin Jeanpierre9c34da62022-03-31 04:57:16 -0700517 trait_name,
Lukasz Anforowicz92be29f2022-09-02 17:12:29 -0700518 record_name: make_rs_ident(&record.rs_name),
519 record_qualifier: NamespaceQualifier::new(record.id, ir)?,
Michael VanBemmel106f66c2022-07-13 09:48:48 -0700520 impl_for: ImplFor::T,
Devin Jeanpierrea04bce12022-08-01 13:39:48 -0700521 trait_generic_params: vec![],
Devin Jeanpierre9c34da62022-03-31 04:57:16 -0700522 format_first_param_as_self,
Michael VanBemmelf6651302022-08-03 15:56:59 -0700523 drop_return: false,
Devin Jeanpierrefd4ff822022-07-15 01:43:55 -0700524 associated_return_type: None,
Felipe de A. Mello Pereiraec5f22c2022-11-02 04:04:01 -0700525 force_const_reference_params,
Lukasz Anforowicz92be29f2022-09-02 17:12:29 -0700526 })
Devin Jeanpierre9c34da62022-03-31 04:57:16 -0700527 }
528 fn format_first_param_as_self(&self) -> bool {
529 matches!(
530 self,
531 Self::Trait { format_first_param_as_self: true, .. }
532 | Self::Struct { format_first_param_as_self: true, .. }
533 )
534 }
535 /// Returns whether the function is defined as `unsafe fn ...`.
536 fn is_unsafe(&self) -> bool {
Devin Jeanpierre215ee8e2022-05-16 16:09:41 -0700537 matches!(
538 self,
539 Self::None { is_unsafe: true, .. }
540 | Self::Struct { is_unsafe: true, .. }
541 | Self::Trait { trait_name: TraitName::Other { is_unsafe_fn: true, .. }, .. }
542 )
Devin Jeanpierre9c34da62022-03-31 04:57:16 -0700543 }
544}
545
Michael VanBemmel106f66c2022-07-13 09:48:48 -0700546/// Whether the impl block is for T, and the receivers take self by reference,
547/// or the impl block is for a reference to T, and the method receivers take
548/// self by value.
549enum ImplFor {
550 /// Implement the trait for `T` directly.
551 ///
552 /// ```
553 /// impl Trait for T {
554 /// fn const_method<'a>(&'a self);
555 /// fn mut_method<'a>(&'a mut self);
556 /// fn pin_method<'a>(Pin<&'a mut self>);
557 /// }
558 /// ```
559 T,
560 /// Implement the trait for `&T`, `&mut T`, or `Pin<&mut T>`, depending on
561 /// the Rust type of the self parameter.
562 ///
563 /// ```
564 /// impl<'a> Trait for &'a T {
565 /// fn const_method(self);
566 /// }
567 /// impl<'a> Trait for &'a mut UnpinT {
568 /// fn mut_method(self);
569 /// }
570 /// impl<'a> Trait for Pin<&'a mut NonUnpinT> {
571 /// fn pin_method(self);
572 /// }
573 /// ```
574 RefT,
575}
576
Michael VanBemmel7a4d4c02022-07-27 13:21:47 -0700577/// Returns whether an argument of this type causes ADL to include the `record`.
578fn adl_expands_to(record: &Record, rs_type_kind: &RsTypeKind) -> bool {
579 match rs_type_kind {
580 RsTypeKind::Record { record: nested_record, .. } => ptr::eq(record, &**nested_record),
581 RsTypeKind::Reference { referent, .. } => adl_expands_to(record, &**referent),
582 RsTypeKind::RvalueReference { referent, .. } => adl_expands_to(record, &**referent),
583 _ => false,
584 }
585}
586
587/// Returns whether any type in `param_types` causes ADL to include `record`.
588///
589/// This is an under-approximation. Things not considered include class template
590/// arguments and the parameters and return type of function types.
591///
592/// See https://en.cppreference.com/w/cpp/language/adl
593fn is_visible_by_adl(enclosing_record: &Record, param_types: &[RsTypeKind]) -> bool {
594 param_types.iter().any(|param_type| adl_expands_to(enclosing_record, param_type))
595}
596
Michael VanBemmel32c26df2022-08-03 16:08:58 -0700597#[derive(Debug)]
598struct OperatorMetadata {
Michael VanBemmel5bde56f2022-08-17 12:05:37 -0700599 by_cc_name_and_params: HashMap<(&'static str, usize), OperatorMetadataEntry>,
Michael VanBemmel32c26df2022-08-03 16:08:58 -0700600}
601
602#[derive(Clone, Copy, Debug)]
603struct OperatorMetadataEntry {
Michael VanBemmel5bde56f2022-08-17 12:05:37 -0700604 cc_name: &'static str,
605 cc_params: usize,
Michael VanBemmel32c26df2022-08-03 16:08:58 -0700606 trait_name: &'static str,
607 method_name: &'static str,
Michael VanBemmel5bde56f2022-08-17 12:05:37 -0700608 is_compound_assignment: bool,
609}
610
611impl OperatorMetadataEntry {
612 const fn unary(
613 cc_name: &'static str,
614 trait_name: &'static str,
615 method_name: &'static str,
616 ) -> Self {
617 Self { cc_name, cc_params: 1, trait_name, method_name, is_compound_assignment: false }
618 }
619
620 const fn binary(
621 cc_name: &'static str,
622 trait_name: &'static str,
623 method_name: &'static str,
624 ) -> Self {
625 Self { cc_name, cc_params: 2, trait_name, method_name, is_compound_assignment: false }
626 }
627
628 const fn assign(
629 cc_name: &'static str,
630 trait_name: &'static str,
631 method_name: &'static str,
632 ) -> Self {
633 Self { cc_name, cc_params: 2, trait_name, method_name, is_compound_assignment: true }
634 }
Michael VanBemmel32c26df2022-08-03 16:08:58 -0700635}
636
Michael VanBemmel5014b3e2022-08-03 16:27:34 -0700637static OPERATOR_METADATA: Lazy<OperatorMetadata> = Lazy::new(|| {
Michael VanBemmel5bde56f2022-08-17 12:05:37 -0700638 const ENTRIES: &[OperatorMetadataEntry] = &[
639 OperatorMetadataEntry::unary("-", "Neg", "neg"),
640 // The Rust `Not` trait matches with both the C++ `!` and `~` operators to some extent. The
641 // two operators appear with similar frequency in our target codebase so it's not clear
642 // which is better to map here. Mapping `operator!` to `Not` as chosen here means that a
643 // C++ `!` matches up with a Rust `!`.
644 OperatorMetadataEntry::unary("!", "Not", "not"),
645 OperatorMetadataEntry::binary("+", "Add", "add"),
646 OperatorMetadataEntry::binary("-", "Sub", "sub"),
647 OperatorMetadataEntry::binary("*", "Mul", "mul"),
648 OperatorMetadataEntry::binary("/", "Div", "div"),
649 OperatorMetadataEntry::binary("%", "Rem", "rem"),
650 OperatorMetadataEntry::binary("&", "BitAnd", "bitand"),
651 OperatorMetadataEntry::binary("|", "BitOr", "bitor"),
652 OperatorMetadataEntry::binary("^", "BitXor", "bitxor"),
653 OperatorMetadataEntry::binary("<<", "Shl", "shl"),
654 OperatorMetadataEntry::binary(">>", "Shr", "shr"),
655 OperatorMetadataEntry::assign("+=", "AddAssign", "add_assign"),
656 OperatorMetadataEntry::assign("-=", "SubAssign", "sub_assign"),
657 OperatorMetadataEntry::assign("*=", "MulAssign", "mul_assign"),
658 OperatorMetadataEntry::assign("/=", "DivAssign", "div_assign"),
659 OperatorMetadataEntry::assign("%=", "RemAssign", "rem_assign"),
660 OperatorMetadataEntry::assign("&=", "BitAndAssign", "bitand_assign"),
661 OperatorMetadataEntry::assign("|=", "BitOrAssign", "bitor_assign"),
662 OperatorMetadataEntry::assign("^=", "BitXorAssign", "bitxor_assign"),
663 OperatorMetadataEntry::assign("<<=", "ShlAssign", "shl_assign"),
664 OperatorMetadataEntry::assign(">>=", "ShrAssign", "shr_assign"),
Michael VanBemmel32c26df2022-08-03 16:08:58 -0700665 ];
Michael VanBemmel5014b3e2022-08-03 16:27:34 -0700666 OperatorMetadata {
Michael VanBemmel5bde56f2022-08-17 12:05:37 -0700667 by_cc_name_and_params: ENTRIES.iter().map(|e| ((e.cc_name, e.cc_params), *e)).collect(),
Michael VanBemmel5014b3e2022-08-03 16:27:34 -0700668 }
669});
Michael VanBemmel32c26df2022-08-03 16:08:58 -0700670
Devin Jeanpierre9c34da62022-03-31 04:57:16 -0700671/// Returns the shape of the generated Rust API for a given function definition.
Devin Jeanpierred7b48102022-03-31 04:15:03 -0700672///
Devin Jeanpierre2b1e46d2022-08-01 13:42:12 -0700673/// If the shape is a trait, this also mutates the parameter types to be
674/// trait-compatible. In particular, types which would be `impl Ctor<Output=T>`
675/// become a `RvalueReference<'_, T>`.
676///
Devin Jeanpierre9c34da62022-03-31 04:57:16 -0700677/// Returns:
Devin Jeanpierred7b48102022-03-31 04:15:03 -0700678///
679/// * `Err(_)`: something went wrong importing this function.
680/// * `Ok(None)`: the function imported as "nothing". (For example, a defaulted
681/// destructor might be mapped to no `Drop` impl at all.)
Devin Jeanpierre9c34da62022-03-31 04:57:16 -0700682/// * `Ok((func_name, impl_kind))`: The function name and ImplKind.
Devin Jeanpierre8cd8ae72022-06-29 18:59:40 -0700683fn api_func_shape(
Michael VanBemmel32c26df2022-08-03 16:08:58 -0700684 db: &dyn BindingsGenerator,
Devin Jeanpierre9c34da62022-03-31 04:57:16 -0700685 func: &Func,
Devin Jeanpierre2b1e46d2022-08-01 13:42:12 -0700686 param_types: &mut [RsTypeKind],
Devin Jeanpierre8cd8ae72022-06-29 18:59:40 -0700687) -> Result<Option<(Ident, ImplKind)>> {
Michael VanBemmel32c26df2022-08-03 16:08:58 -0700688 let ir = db.ir();
Michael VanBemmel5014b3e2022-08-03 16:27:34 -0700689 let op_meta = &*OPERATOR_METADATA;
Michael VanBemmel32c26df2022-08-03 16:08:58 -0700690
Devin Jeanpierre8cd8ae72022-06-29 18:59:40 -0700691 let maybe_record: Option<&Rc<Record>> = ir.record_for_member_func(func)?;
Devin Jeanpierre22f91492022-04-06 13:01:23 -0700692 let has_pointer_params = param_types.iter().any(|p| matches!(p, RsTypeKind::Pointer { .. }));
Devin Jeanpierre9c34da62022-03-31 04:57:16 -0700693 let impl_kind: ImplKind;
Lukasz Anforowiczab65e292022-01-14 23:04:21 +0000694 let func_name: syn::Ident;
Michael VanBemmelf6651302022-08-03 15:56:59 -0700695
696 let adl_check_required_and_failed = if let Some(decl_id) = func.adl_enclosing_record {
Lukasz Anforowicz42ab93b2022-08-31 11:17:59 -0700697 let adl_enclosing_record = ir
698 .find_decl::<Rc<Record>>(decl_id)
699 .with_context(|| format!("Failed to look up `adl_enclosing_record` of {:?}", func))?;
Michael VanBemmelf6651302022-08-03 15:56:59 -0700700 !is_visible_by_adl(adl_enclosing_record, param_types)
701 } else {
702 false
703 };
704
Googlerd03d05b2022-01-07 10:10:57 +0000705 match &func.name {
Michael VanBemmelf6651302022-08-03 15:56:59 -0700706 UnqualifiedIdentifier::Operator(_) | UnqualifiedIdentifier::Identifier(_)
707 if adl_check_required_and_failed =>
708 {
709 return Ok(None);
710 }
Lukasz Anforowicz9c663ca2022-02-09 01:33:31 +0000711 UnqualifiedIdentifier::Operator(op) if op.name == "==" => {
Devin Jeanpierre8dd193a2022-06-03 10:57:21 -0700712 assert_eq!(
713 param_types.len(),
714 2,
715 "Unexpected number of parameters in operator==: {func:?}"
716 );
Felipe de A. Mello Pereiraec5f22c2022-11-02 04:04:01 -0700717 let lhs_record = match &param_types[0] {
718 RsTypeKind::Reference { referent: lhs, mutability: Mutability::Const, .. } => {
719 if let RsTypeKind::Record { record: lhs_record, .. } = &**lhs {
720 lhs_record
721 } else {
722 bail!(
723 "operator== where lhs param is reference that doesn't refer to a record",
724 );
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +0000725 }
Marcel Hlopko14ee3c82022-02-09 09:46:23 +0000726 }
Felipe de A. Mello Pereiraec5f22c2022-11-02 04:04:01 -0700727 RsTypeKind::Record { record: lhs_record, .. } => lhs_record,
728 _ => bail!(
729 "operator== where lhs operand is not record nor const reference to record"
730 ),
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +0000731 };
Felipe de A. Mello Pereiraec5f22c2022-11-02 04:04:01 -0700732 let params = match &param_types[1] {
733 RsTypeKind::Reference { referent: rhs, mutability: Mutability::Const, .. } => {
734 if let RsTypeKind::Record { .. } = &**rhs {
735 vec![(**rhs).clone()]
736 } else {
737 bail!(
738 "operator== where rhs param is reference that doesn't refer to a record",
739 );
740 }
741 }
742 record @ RsTypeKind::Record { .. } => vec![record.clone()],
743 _ => bail!(
744 "operator== where rhs operand is not record nor const reference to record"
745 ),
746 };
747 func_name = make_rs_ident("eq");
748 impl_kind = ImplKind::new_trait(
749 TraitName::PartialEq { params },
750 lhs_record,
751 &ir,
752 /* format_first_param_as_self= */ true,
753 /* force_const_reference_params= */ true,
754 )?;
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +0000755 }
Felipe de A. Mello Pereira69736292022-10-11 02:03:43 -0700756 UnqualifiedIdentifier::Operator(op) if op.name == "<=>" => {
757 bail!("Three-way comparison operator not yet supported (b/219827738)");
758 }
759 UnqualifiedIdentifier::Operator(op) if op.name == "<" => {
760 assert_eq!(
761 param_types.len(),
762 2,
763 "Unexpected number of parameters in operator<: {func:?}"
764 );
Felipe de A. Mello Pereiraec5f22c2022-11-02 04:04:01 -0700765 let lhs_record = match &param_types[0] {
766 RsTypeKind::Reference { referent: lhs, mutability: Mutability::Const, .. } => {
767 if let RsTypeKind::Record { record: lhs_record, .. } = &**lhs {
768 lhs_record
769 } else {
770 bail!(
771 "operator== where lhs param is reference that doesn't refer to a record",
772 );
Felipe de A. Mello Pereira69736292022-10-11 02:03:43 -0700773 }
Felipe de A. Mello Pereiraec5f22c2022-11-02 04:04:01 -0700774 }
775 RsTypeKind::Record { record: lhs_record, .. } => lhs_record,
Felipe de A. Mello Pereira69736292022-10-11 02:03:43 -0700776 _ => {
Felipe de A. Mello Pereiraec5f22c2022-11-02 04:04:01 -0700777 bail!("operator< where lhs operand is not record nor const reference to record")
Felipe de A. Mello Pereira69736292022-10-11 02:03:43 -0700778 }
779 };
Felipe de A. Mello Pereiraec5f22c2022-11-02 04:04:01 -0700780 let (rhs_record, params) = match &param_types[1] {
781 RsTypeKind::Reference { referent: rhs, mutability: Mutability::Const, .. } => {
782 if let RsTypeKind::Record { record: rhs_record, .. } = &**rhs {
783 (rhs_record, vec![(**rhs).clone()])
784 } else {
785 bail!(
786 "operator== where rhs param is reference that doesn't refer to a record",
787 );
788 }
789 }
790 record @ RsTypeKind::Record { record: rhs_record, .. } => {
791 (rhs_record, vec![record.clone()])
792 }
793 _ => {
794 bail!("operator< where rhs operand is not record nor const reference to record")
795 }
796 };
797 // Even though Rust and C++ allow operator< to be implemented on different
798 // types, we don't generate bindings for them at this moment. The
799 // issue is that our canonical implementation of partial_cmp relies
800 // on transitivity. This would require checking that both lt(&T1,
801 // &T2) and lt(&T2, &T1) are implemented. In other words, both lt
802 // implementations would need to query for the existence of the other, which
803 // would create a cyclic dependency.
804 if lhs_record != rhs_record {
805 bail!("operator< where lhs and rhs are not the same type.");
806 }
807 // PartialOrd requires PartialEq, so we need to make sure operator== is
808 // implemented for this Record type.
809 match get_binding(
810 db,
811 UnqualifiedIdentifier::Operator(Operator { name: "==".to_string() }),
812 param_types,
813 ) {
814 Some((_, ImplKind::Trait { trait_name: TraitName::PartialEq { .. }, .. })) => {
815 func_name = make_rs_ident("lt");
816 impl_kind = ImplKind::new_trait(
817 TraitName::PartialOrd { params },
818 lhs_record,
819 &ir,
820 /* format_first_param_as_self= */
821 true,
822 /* force_const_reference_params= */ true,
823 )?;
824 }
825 _ => bail!("operator< where operator== is missing."),
826 }
Felipe de A. Mello Pereira69736292022-10-11 02:03:43 -0700827 }
Devin Jeanpierre9ced4ef2022-06-08 12:39:10 -0700828 UnqualifiedIdentifier::Operator(op) if op.name == "=" => {
829 assert_eq!(
830 param_types.len(),
831 2,
832 "Unexpected number of parameters in operator=: {func:?}"
833 );
834 let record =
835 maybe_record.ok_or_else(|| anyhow!("operator= must be a member function."))?;
836 if record.is_unpin() {
837 bail!("operator= for Unpin types is not yet supported.");
838 }
Devin Jeanpierre6bb81802022-08-10 02:08:47 -0700839 materialize_ctor_in_caller(func, param_types);
Devin Jeanpierre9ced4ef2022-06-08 12:39:10 -0700840 let rhs = &param_types[1];
Michael VanBemmelf6651302022-08-03 15:56:59 -0700841 impl_kind = {
842 ImplKind::Trait {
843 trait_name: TraitName::Other {
844 name: quote! {::ctor::Assign},
845 params: vec![rhs.clone()],
846 is_unsafe_fn: false,
847 },
848 record_name: make_rs_ident(&record.rs_name),
Lukasz Anforowicz92be29f2022-09-02 17:12:29 -0700849 record_qualifier: NamespaceQualifier::new(record.id, &ir)?,
Michael VanBemmelf6651302022-08-03 15:56:59 -0700850 impl_for: ImplFor::T,
851 trait_generic_params: vec![],
852 format_first_param_as_self: true,
853 drop_return: true,
854 associated_return_type: None,
Felipe de A. Mello Pereiraec5f22c2022-11-02 04:04:01 -0700855 force_const_reference_params: false,
Michael VanBemmelf6651302022-08-03 15:56:59 -0700856 }
857 };
Devin Jeanpierre9ced4ef2022-06-08 12:39:10 -0700858 func_name = make_rs_ident("assign");
859 }
Michael VanBemmel5bde56f2022-08-17 12:05:37 -0700860 UnqualifiedIdentifier::Operator(op) => match op_meta
861 .by_cc_name_and_params
862 .get(&(op.name.as_str(), param_types.len()))
Michael VanBemmel32c26df2022-08-03 16:08:58 -0700863 {
Michael VanBemmel5bde56f2022-08-17 12:05:37 -0700864 Some(OperatorMetadataEntry {
865 trait_name,
866 method_name,
867 is_compound_assignment: false,
868 ..
869 }) => {
870 materialize_ctor_in_caller(func, param_types);
Lukasz Anforowiczcb1f9c22022-09-02 12:40:01 -0700871 let (record, impl_for) = match &param_types[0] {
872 RsTypeKind::Record { record, .. } => (&**record, ImplFor::T),
873 RsTypeKind::Reference { referent, .. } => (
874 match &**referent {
875 RsTypeKind::Record { record, .. } => &**record,
876 _ => bail!("Expected first parameter referent to be a record"),
877 },
878 ImplFor::RefT,
879 ),
880 RsTypeKind::RvalueReference { .. } => {
881 bail!("Not yet supported for rvalue references (b/219826128)")
Michael VanBemmel5bde56f2022-08-17 12:05:37 -0700882 }
Lukasz Anforowiczcb1f9c22022-09-02 12:40:01 -0700883 _ => bail!("Expected first parameter to be a record or reference"),
Michael VanBemmel5bde56f2022-08-17 12:05:37 -0700884 };
885
886 let trait_name = make_rs_ident(trait_name);
887 impl_kind = ImplKind::Trait {
888 record_name: make_rs_ident(&record.rs_name),
Lukasz Anforowicz92be29f2022-09-02 17:12:29 -0700889 record_qualifier: NamespaceQualifier::new(record.id, &ir)?,
Michael VanBemmel5bde56f2022-08-17 12:05:37 -0700890 trait_name: TraitName::Other {
891 name: quote! {::std::ops::#trait_name},
892 params: param_types[1..].to_vec(),
893 is_unsafe_fn: false,
894 },
895 impl_for,
896 trait_generic_params: vec![],
897 format_first_param_as_self: true,
898 drop_return: false,
899 associated_return_type: Some(make_rs_ident("Output")),
Felipe de A. Mello Pereiraec5f22c2022-11-02 04:04:01 -0700900 force_const_reference_params: false,
Michael VanBemmel5bde56f2022-08-17 12:05:37 -0700901 };
902 func_name = make_rs_ident(method_name);
903 }
904 Some(OperatorMetadataEntry {
905 trait_name,
906 method_name,
907 is_compound_assignment: true,
908 ..
909 }) => {
910 materialize_ctor_in_caller(func, param_types);
911 let record = match &param_types[0] {
912 RsTypeKind::Record { .. } => {
913 bail!("Compound assignment with by-value left-hand side is not supported")
914 }
915 RsTypeKind::Reference { mutability: Mutability::Const, .. } => {
916 bail!("Compound assignment with const left-hand side is not supported")
917 }
918 RsTypeKind::Reference { referent, mutability: Mutability::Mut, .. } => {
Michael VanBemmel83eca6b2022-07-20 10:16:38 -0700919 match &**referent {
Michael VanBemmel5bde56f2022-08-17 12:05:37 -0700920 RsTypeKind::Record { record, .. } => &**maybe_record.unwrap_or(record),
Michael VanBemmel83eca6b2022-07-20 10:16:38 -0700921 _ => bail!("Expected first parameter referent to be a record"),
Michael VanBemmel5bde56f2022-08-17 12:05:37 -0700922 }
923 }
Michael VanBemmel83eca6b2022-07-20 10:16:38 -0700924 RsTypeKind::RvalueReference { .. } => {
925 bail!("Not yet supported for rvalue references (b/219826128)")
926 }
Michael VanBemmel5bde56f2022-08-17 12:05:37 -0700927 RsTypeKind::Pointer { .. } => {
928 bail!("Not yet supported for pointers with unknown lifetime (b/219826128)")
Michael VanBemmelf6651302022-08-03 15:56:59 -0700929 }
Michael VanBemmel5bde56f2022-08-17 12:05:37 -0700930 _ => bail!("Expected first parameter to be a record or reference"),
931 };
Michael VanBemmelf6651302022-08-03 15:56:59 -0700932
Michael VanBemmel5bde56f2022-08-17 12:05:37 -0700933 let trait_name = make_rs_ident(trait_name);
934 impl_kind = ImplKind::Trait {
935 record_name: make_rs_ident(&record.rs_name),
Lukasz Anforowicz92be29f2022-09-02 17:12:29 -0700936 record_qualifier: NamespaceQualifier::new(record.id, &ir)?,
Michael VanBemmel5bde56f2022-08-17 12:05:37 -0700937 trait_name: TraitName::Other {
938 name: quote! {::std::ops::#trait_name},
939 params: param_types[1..].to_vec(),
940 is_unsafe_fn: false,
941 },
942 impl_for: ImplFor::T,
943 trait_generic_params: vec![],
944 format_first_param_as_self: true,
945 drop_return: true,
946 associated_return_type: None,
Felipe de A. Mello Pereiraec5f22c2022-11-02 04:04:01 -0700947 force_const_reference_params: false,
Michael VanBemmel5bde56f2022-08-17 12:05:37 -0700948 };
949 func_name = make_rs_ident(method_name);
950 }
951 None => {
952 bail!(
953 "Bindings for this kind of operator (operator {op} with {n} parameter(s)) are not supported",
954 op = &op.name,
955 n = param_types.len(),
956 );
957 }
958 },
Devin Jeanpierref2ec8712021-10-13 20:47:16 +0000959 UnqualifiedIdentifier::Identifier(id) => {
Marcel Hlopkoeaae9b72022-01-21 15:54:11 +0000960 func_name = make_rs_ident(&id.identifier);
Lukasz Anforowiczf9564622022-01-28 14:31:04 +0000961 match maybe_record {
962 None => {
Devin Jeanpierre7c3d8ed2022-03-29 03:02:04 -0700963 impl_kind = ImplKind::None { is_unsafe: has_pointer_params };
Lukasz Anforowiczf9564622022-01-28 14:31:04 +0000964 }
965 Some(record) => {
Devin Jeanpierred9a6e6c2022-03-29 02:55:41 -0700966 let format_first_param_as_self = if func.is_instance_method() {
Devin Jeanpierre22f91492022-04-06 13:01:23 -0700967 let first_param = param_types.first().ok_or_else(|| {
Lukasz Anforowiczf9564622022-01-28 14:31:04 +0000968 anyhow!("Missing `__this` parameter in an instance method: {:?}", func)
969 })?;
Devin Jeanpierred9a6e6c2022-03-29 02:55:41 -0700970 first_param.is_ref_to(record)
Lukasz Anforowiczf9564622022-01-28 14:31:04 +0000971 } else {
Devin Jeanpierred9a6e6c2022-03-29 02:55:41 -0700972 false
973 };
Devin Jeanpierre6784e5e2022-03-29 02:59:01 -0700974 impl_kind = ImplKind::Struct {
975 record_name: make_rs_ident(&record.rs_name),
976 format_first_param_as_self,
Devin Jeanpierre7c3d8ed2022-03-29 03:02:04 -0700977 is_unsafe: has_pointer_params,
Devin Jeanpierre6784e5e2022-03-29 02:59:01 -0700978 };
Lukasz Anforowiczf9564622022-01-28 14:31:04 +0000979 }
980 };
Michael Forstered642022021-10-04 09:48:25 +0000981 }
Devin Jeanpierre91de7012021-10-21 12:53:51 +0000982 UnqualifiedIdentifier::Destructor => {
Lukasz Anforowiczab65e292022-01-14 23:04:21 +0000983 // Note: to avoid double-destruction of the fields, they are all wrapped in
984 // ManuallyDrop in this case. See `generate_record`.
985 let record =
986 maybe_record.ok_or_else(|| anyhow!("Destructors must be member functions."))?;
Lukasz Anforowicze57215c2022-01-12 14:54:16 +0000987 if !should_implement_drop(record) {
Devin Jeanpierred7b48102022-03-31 04:15:03 -0700988 return Ok(None);
Devin Jeanpierre91de7012021-10-21 12:53:51 +0000989 }
Devin Jeanpierre215ee8e2022-05-16 16:09:41 -0700990 if record.is_unpin() {
991 impl_kind = ImplKind::new_trait(
Devin Jeanpierree77fa7e2022-06-02 14:05:58 -0700992 TraitName::Other { name: quote! {Drop}, params: vec![], is_unsafe_fn: false },
Devin Jeanpierre781eeb12022-09-06 19:23:25 -0700993 record,
994 &ir,
Devin Jeanpierre215ee8e2022-05-16 16:09:41 -0700995 /* format_first_param_as_self= */ true,
Felipe de A. Mello Pereiraec5f22c2022-11-02 04:04:01 -0700996 /* force_const_reference_params= */
997 false,
Lukasz Anforowicz92be29f2022-09-02 17:12:29 -0700998 )?;
Devin Jeanpierre215ee8e2022-05-16 16:09:41 -0700999 func_name = make_rs_ident("drop");
1000 } else {
Devin Jeanpierre6bb81802022-08-10 02:08:47 -07001001 materialize_ctor_in_caller(func, param_types);
Devin Jeanpierre215ee8e2022-05-16 16:09:41 -07001002 impl_kind = ImplKind::new_trait(
Devin Jeanpierree77fa7e2022-06-02 14:05:58 -07001003 TraitName::Other {
1004 name: quote! {::ctor::PinnedDrop},
1005 params: vec![],
1006 is_unsafe_fn: true,
1007 },
Devin Jeanpierre781eeb12022-09-06 19:23:25 -07001008 record,
1009 &ir,
Devin Jeanpierre215ee8e2022-05-16 16:09:41 -07001010 /* format_first_param_as_self= */ true,
Felipe de A. Mello Pereiraec5f22c2022-11-02 04:04:01 -07001011 /* force_const_reference_params= */ false,
Lukasz Anforowicz92be29f2022-09-02 17:12:29 -07001012 )?;
Devin Jeanpierre215ee8e2022-05-16 16:09:41 -07001013 func_name = make_rs_ident("pinned_drop");
1014 }
Devin Jeanpierre91de7012021-10-21 12:53:51 +00001015 }
Lukasz Anforowicz13cf7492021-12-22 15:29:52 +00001016 UnqualifiedIdentifier::Constructor => {
Lukasz Anforowicz71716b72022-01-26 17:05:05 +00001017 let member_func_metadata = func
1018 .member_func_metadata
1019 .as_ref()
1020 .ok_or_else(|| anyhow!("Constructors must be member functions."))?;
1021 let record = maybe_record
1022 .ok_or_else(|| anyhow!("Constructors must be associated with a record."))?;
1023 let instance_method_metadata =
1024 member_func_metadata
1025 .instance_method_metadata
1026 .as_ref()
1027 .ok_or_else(|| anyhow!("Constructors must be instance methods."))?;
Devin Jeanpierre7c3d8ed2022-03-29 03:02:04 -07001028 if has_pointer_params {
Lukasz Anforowicz55673c92022-01-27 19:37:26 +00001029 // TODO(b/216648347): Allow this outside of traits (e.g. after supporting
1030 // translating C++ constructors into static methods in Rust).
Devin Jeanpierred7b48102022-03-31 04:15:03 -07001031 bail!(
Lukasz Anforowiczeb19ac62022-02-05 00:10:16 +00001032 "Unsafe constructors (e.g. with no elided or explicit lifetimes) \
1033 are intentionally not supported",
1034 );
Lukasz Anforowicz55673c92022-01-27 19:37:26 +00001035 }
1036
Devin Jeanpierrea99e0a32022-08-17 01:36:58 -07001037 check_by_value(record)?;
Devin Jeanpierre6bb81802022-08-10 02:08:47 -07001038 materialize_ctor_in_caller(func, param_types);
Devin Jeanpierre6784e5e2022-03-29 02:59:01 -07001039 let record_name = make_rs_ident(&record.rs_name);
Devin Jeanpierre6f607372022-03-22 21:34:38 +00001040 if !record.is_unpin() {
1041 func_name = make_rs_ident("ctor_new");
Devin Jeanpierre6f607372022-03-22 21:34:38 +00001042
Devin Jeanpierread125742022-04-11 13:50:28 -07001043 match param_types {
1044 [] => bail!("Missing `__this` parameter in a constructor: {:?}", func),
1045 [_this, params @ ..] => {
Devin Jeanpierrefd4ff822022-07-15 01:43:55 -07001046 impl_kind = ImplKind::Trait {
Devin Jeanpierre1b8f4a12022-03-22 21:29:42 +00001047 record_name,
Lukasz Anforowicz92be29f2022-09-02 17:12:29 -07001048 record_qualifier: NamespaceQualifier::new(record.id, &ir)?,
Devin Jeanpierrefd4ff822022-07-15 01:43:55 -07001049 trait_name: TraitName::CtorNew(params.iter().cloned().collect()),
1050 impl_for: ImplFor::T,
Devin Jeanpierrea04bce12022-08-01 13:39:48 -07001051 trait_generic_params: vec![],
Devin Jeanpierrefd4ff822022-07-15 01:43:55 -07001052 format_first_param_as_self: false,
Michael VanBemmelf6651302022-08-03 15:56:59 -07001053 drop_return: false,
Devin Jeanpierrefd4ff822022-07-15 01:43:55 -07001054 associated_return_type: Some(make_rs_ident("CtorType")),
Felipe de A. Mello Pereiraec5f22c2022-11-02 04:04:01 -07001055 force_const_reference_params: false,
Devin Jeanpierrefd4ff822022-07-15 01:43:55 -07001056 };
Devin Jeanpierre6f607372022-03-22 21:34:38 +00001057 }
Lukasz Anforowicz73326af2022-01-05 01:13:10 +00001058 }
Devin Jeanpierre6f607372022-03-22 21:34:38 +00001059 } else {
1060 match func.params.len() {
1061 0 => bail!("Missing `__this` parameter in a constructor: {:?}", func),
1062 1 => {
1063 impl_kind = ImplKind::new_trait(
Devin Jeanpierree77fa7e2022-06-02 14:05:58 -07001064 TraitName::UnpinConstructor { name: quote! {Default}, params: vec![] },
Devin Jeanpierre781eeb12022-09-06 19:23:25 -07001065 record,
1066 &ir,
Devin Jeanpierred9a6e6c2022-03-29 02:55:41 -07001067 /* format_first_param_as_self= */ false,
Felipe de A. Mello Pereiraec5f22c2022-11-02 04:04:01 -07001068 /* force_const_reference_params= */ false,
Lukasz Anforowicz92be29f2022-09-02 17:12:29 -07001069 )?;
Devin Jeanpierre6f607372022-03-22 21:34:38 +00001070 func_name = make_rs_ident("default");
Devin Jeanpierre6f607372022-03-22 21:34:38 +00001071 }
1072 2 => {
Devin Jeanpierre22f91492022-04-06 13:01:23 -07001073 if param_types[1].is_shared_ref_to(record) {
Devin Jeanpierre6f607372022-03-22 21:34:38 +00001074 // Copy constructor
1075 if should_derive_clone(record) {
Devin Jeanpierred7b48102022-03-31 04:15:03 -07001076 return Ok(None);
Devin Jeanpierre6f607372022-03-22 21:34:38 +00001077 } else {
1078 impl_kind = ImplKind::new_trait(
Devin Jeanpierree77fa7e2022-06-02 14:05:58 -07001079 TraitName::UnpinConstructor {
1080 name: quote! {Clone},
1081 params: vec![],
1082 },
Devin Jeanpierre781eeb12022-09-06 19:23:25 -07001083 record,
1084 &ir,
Devin Jeanpierred9a6e6c2022-03-29 02:55:41 -07001085 /* format_first_param_as_self= */ true,
Felipe de A. Mello Pereiraec5f22c2022-11-02 04:04:01 -07001086 /* force_const_reference_params= */ false,
Lukasz Anforowicz92be29f2022-09-02 17:12:29 -07001087 )?;
Devin Jeanpierre6f607372022-03-22 21:34:38 +00001088 func_name = make_rs_ident("clone");
Devin Jeanpierre6f607372022-03-22 21:34:38 +00001089 }
1090 } else if !instance_method_metadata.is_explicit_ctor {
Devin Jeanpierre22f91492022-04-06 13:01:23 -07001091 let param_type = &param_types[1];
Devin Jeanpierree77fa7e2022-06-02 14:05:58 -07001092 impl_kind = ImplKind::new_trait(
1093 TraitName::UnpinConstructor {
1094 name: quote! {From},
1095 params: vec![param_type.clone()],
1096 },
Devin Jeanpierre781eeb12022-09-06 19:23:25 -07001097 record,
1098 &ir,
Devin Jeanpierred9a6e6c2022-03-29 02:55:41 -07001099 /* format_first_param_as_self= */ false,
Felipe de A. Mello Pereiraec5f22c2022-11-02 04:04:01 -07001100 /* force_const_reference_params= */
1101 false,
Lukasz Anforowicz92be29f2022-09-02 17:12:29 -07001102 )?;
Devin Jeanpierre6f607372022-03-22 21:34:38 +00001103 func_name = make_rs_ident("from");
Devin Jeanpierre6f607372022-03-22 21:34:38 +00001104 } else {
Devin Jeanpierred7b48102022-03-31 04:15:03 -07001105 bail!("Not yet supported type of constructor parameter",);
Devin Jeanpierre6f607372022-03-22 21:34:38 +00001106 }
1107 }
1108 _ => {
1109 // TODO(b/216648347): Support bindings for other constructors.
Devin Jeanpierred7b48102022-03-31 04:15:03 -07001110 bail!("More than 1 constructor parameter is not supported yet",);
Devin Jeanpierre6f607372022-03-22 21:34:38 +00001111 }
Lukasz Anforowicz73326af2022-01-05 01:13:10 +00001112 }
Lukasz Anforowiczab65e292022-01-14 23:04:21 +00001113 }
1114 }
1115 }
Devin Jeanpierre9c34da62022-03-31 04:57:16 -07001116 Ok(Some((func_name, impl_kind)))
1117}
1118
Felipe de A. Mello Pereira69736292022-10-11 02:03:43 -07001119/// Returns the generated bindings for a function with the given name and param
1120/// types. If none exists, returns None.
1121fn get_binding(
1122 db: &dyn BindingsGenerator,
1123 expected_function_name: UnqualifiedIdentifier,
1124 expected_param_types: &[RsTypeKind],
1125) -> Option<(Ident, ImplKind)> {
Felipe de A. Mello Pereiraec5f22c2022-11-02 04:04:01 -07001126 db.ir()
Felipe de A. Mello Pereira69736292022-10-11 02:03:43 -07001127 // TODO(jeanpierreda): make this O(1) using a hash table lookup.
1128 .functions()
1129 .filter(|function| {
1130 function.name == expected_function_name
1131 && generate_func(db, (*function).clone()).ok().flatten().is_some()
1132 })
1133 .find_map(|function| {
1134 let mut function_param_types = function
1135 .params
1136 .iter()
1137 .map(|param| db.rs_type_kind(param.type_.rs_type.clone()))
1138 .collect::<Result<Vec<_>>>()
1139 .ok()?;
1140 if !function_param_types.iter().eq(expected_param_types) {
1141 return None;
1142 }
1143 api_func_shape(db, function, &mut function_param_types).ok().flatten()
Felipe de A. Mello Pereiraec5f22c2022-11-02 04:04:01 -07001144 })
1145}
1146
1147/// Returns whether the given record either implements or derives the Clone
1148/// trait.
1149fn is_record_clonable(db: &dyn BindingsGenerator, record: Rc<Record>) -> bool {
1150 if !record.is_unpin() {
1151 return false;
1152 }
1153 should_derive_clone(&record)
1154 || db
1155 .ir()
1156 // TODO(jeanpierreda): make this O(1) using a hash table lookup.
1157 .functions()
1158 .filter(|function| {
1159 function.name == UnqualifiedIdentifier::Constructor
1160 // __this is always the first parameter of constructors
1161 && function.params.len() == 2
1162 })
1163 .any(|function| {
1164 let mut function_param_types = function
1165 .params
1166 .iter()
1167 .map(|param| db.rs_type_kind(param.type_.rs_type.clone()))
1168 .collect::<Result<Vec<_>>>()
1169 .ok()
1170 .unwrap_or_default();
1171 if function.params.len() != 2 || !function_param_types[1].is_shared_ref_to(&record)
1172 {
1173 return false;
1174 }
1175 api_func_shape(db, function, &mut function_param_types)
1176 .ok()
1177 .flatten()
1178 .map_or(false, |(func_name, _)| func_name == *"clone")
1179 })
Felipe de A. Mello Pereira69736292022-10-11 02:03:43 -07001180}
1181
Devin Jeanpierre2b1e46d2022-08-01 13:42:12 -07001182/// Mutates the provided parameters so that nontrivial by-value parameters are,
1183/// instead, materialized in the caller and passed by rvalue reference.
Devin Jeanpierre6bb81802022-08-10 02:08:47 -07001184fn materialize_ctor_in_caller(func: &Func, params: &mut [RsTypeKind]) {
1185 let mut existing_lifetime_params: HashSet<Rc<str>> =
1186 params.iter().flat_map(|param| param.lifetimes().map(|lifetime| lifetime.0)).collect();
1187 let mut new_lifetime_param = |mut lifetime_name: String| {
1188 let suffix_start = lifetime_name.len();
1189 let mut next_suffix = 2;
1190 loop {
1191 if !existing_lifetime_params.contains(&*lifetime_name) {
1192 let lifetime_name = <Rc<str>>::from(lifetime_name);
1193 existing_lifetime_params.insert(lifetime_name.clone());
1194 return Lifetime(lifetime_name);
1195 }
1196 lifetime_name.truncate(suffix_start);
1197 write!(lifetime_name, "_{next_suffix}").unwrap();
1198 next_suffix += 1;
1199 }
1200 };
1201 for (func_param, param) in func.params.iter().zip(params.iter_mut()) {
Devin Jeanpierre2b1e46d2022-08-01 13:42:12 -07001202 if param.is_unpin() {
1203 continue;
1204 }
1205 let value = std::mem::replace(param, RsTypeKind::Unit); // Temporarily swap in a garbage value.
1206 *param = RsTypeKind::RvalueReference {
1207 referent: Rc::new(value),
1208 mutability: Mutability::Mut,
Devin Jeanpierre6bb81802022-08-10 02:08:47 -07001209 lifetime: new_lifetime_param(func_param.identifier.identifier.clone()),
Devin Jeanpierre2b1e46d2022-08-01 13:42:12 -07001210 };
1211 }
1212}
1213
Devin Jeanpierre9c34da62022-03-31 04:57:16 -07001214/// Generates Rust source code for a given `Func`.
1215///
1216/// Returns:
1217///
1218/// * `Err(_)`: couldn't import the function, emit an `UnsupportedItem`.
1219/// * `Ok(None)`: the function imported as "nothing". (For example, a defaulted
1220/// destructor might be mapped to no `Drop` impl at all.)
1221/// * `Ok((rs_api, rs_thunk, function_id))`: The Rust function definition,
1222/// thunk FFI definition, and function ID.
Devin Jeanpierre52a14c32022-06-29 19:12:11 -07001223fn generate_func(
1224 db: &dyn BindingsGenerator,
1225 func: Rc<Func>,
Lukasz Anforowiczf8cfa562022-09-22 11:06:43 -07001226) -> Result<Option<Rc<(RsSnippet, RsSnippet, Rc<FunctionId>)>>> {
Lukasz Anforowicz8d064202022-09-01 07:31:06 -07001227 let ir = db.ir();
Devin Jeanpierrefd4ff822022-07-15 01:43:55 -07001228 let mut features = BTreeSet::new();
Devin Jeanpierre2b1e46d2022-08-01 13:42:12 -07001229 let mut param_types = func
Devin Jeanpierre9c34da62022-03-31 04:57:16 -07001230 .params
1231 .iter()
1232 .map(|p| {
Devin Jeanpierre409f6f62022-07-07 02:00:26 -07001233 db.rs_type_kind(p.type_.rs_type.clone()).with_context(|| {
Devin Jeanpierre9c34da62022-03-31 04:57:16 -07001234 format!("Failed to process type of parameter {:?} on {:?}", p, func)
1235 })
1236 })
1237 .collect::<Result<Vec<_>>>()?;
1238
Devin Jeanpierre2b1e46d2022-08-01 13:42:12 -07001239 let (func_name, mut impl_kind) =
Michael VanBemmel32c26df2022-08-03 16:08:58 -07001240 if let Some(values) = api_func_shape(db, &func, &mut param_types)? {
Devin Jeanpierre2b1e46d2022-08-01 13:42:12 -07001241 values
1242 } else {
1243 return Ok(None);
1244 };
Lukasz Anforowicz92be29f2022-09-02 17:12:29 -07001245 let namespace_qualifier = NamespaceQualifier::new(func.id, &ir)?.format_for_rs();
Lukasz Anforowiczab65e292022-01-14 23:04:21 +00001246
Devin Jeanpierrefd4ff822022-07-15 01:43:55 -07001247 let mut return_type = db
Devin Jeanpierre409f6f62022-07-07 02:00:26 -07001248 .rs_type_kind(func.return_type.rs_type.clone())
Devin Jeanpierreb8ce2c12022-07-13 10:34:01 -07001249 .with_context(|| format!("Failed to format return type for {:?}", &func))?;
Devin Jeanpierrea99e0a32022-08-17 01:36:58 -07001250 return_type.check_by_value()?;
Devin Jeanpierred9cecff2022-03-29 02:53:58 -07001251 let param_idents =
1252 func.params.iter().map(|p| make_rs_ident(&p.identifier.identifier)).collect_vec();
Devin Jeanpierreb8ce2c12022-07-13 10:34:01 -07001253 let thunk = generate_func_thunk(db, &func, &param_idents, &param_types, &return_type)?;
Devin Jeanpierred9cecff2022-03-29 02:53:58 -07001254
Felipe de A. Mello Pereiraec5f22c2022-11-02 04:04:01 -07001255 // If the Rust trait require a function to take the params by const reference
1256 // and the thunk takes some of its params by value then we should add a const
1257 // reference around these Rust func params and clone the records when calling
1258 // the thunk. Since some params might require cloning while others don't, we
1259 // need to store this information for each param.
1260 let (mut param_types, clone_suffixes) = if let ImplKind::Trait {
1261 force_const_reference_params: true,
1262 ..
1263 } = impl_kind
1264 {
1265 let mut clone_suffixes = Vec::with_capacity(param_types.len());
1266 (
1267 param_types
1268 .into_iter()
1269 .map(|param_type|
1270 {if let RsTypeKind::Record { record: param_record, .. } = &param_type {
1271 if !is_record_clonable(db, param_record.clone()) {
1272 bail!(
1273 "function requires const ref params in Rust but C++ takes non-cloneable record {:?} by value {:?}",
1274 param_record,
1275 func,
1276 );
1277 }
1278 clone_suffixes.push(quote!{.clone()});
1279 Ok(RsTypeKind::Reference {
1280 referent: Rc::new(param_type.clone()),
1281 mutability: Mutability::Const,
1282 lifetime: Lifetime::new("_"),
1283 })
1284 } else {
1285 clone_suffixes.push(quote!{});
1286 Ok(param_type)
1287 }})
1288 .collect::<Result<Vec<_>>>()?,
1289 clone_suffixes,
1290 )
1291 } else {
1292 let param_len = param_types.len();
1293 (param_types, vec![quote! {}; param_len])
1294 };
1295
Devin Jeanpierref5013fb2022-08-01 13:56:27 -07001296 let BindingsSignature {
1297 lifetimes,
1298 params: api_params,
1299 return_type_fragment: mut quoted_return_type,
Devin Jeanpierre93927e82022-08-01 14:05:54 -07001300 thunk_prepare,
Devin Jeanpierref5013fb2022-08-01 13:56:27 -07001301 thunk_args,
1302 } = function_signature(
1303 &mut features,
1304 &func,
1305 &impl_kind,
1306 &param_idents,
1307 &mut param_types,
1308 &mut return_type,
1309 )?;
Devin Jeanpierrefd4ff822022-07-15 01:43:55 -07001310
1311 let api_func_def = {
Devin Jeanpierre6f607372022-03-22 21:34:38 +00001312 // TODO(b/200067242): the Pin-wrapping code doesn't know to wrap &mut
1313 // MaybeUninit<T> in Pin if T is !Unpin. It should understand
1314 // 'structural pinning', so that we do not need into_inner_unchecked()
1315 // here.
Devin Jeanpierre52a14c32022-06-29 19:12:11 -07001316 let thunk_ident = thunk_ident(&func);
Devin Jeanpierre7bddfdb2022-03-14 11:04:40 +00001317 let func_body = match &impl_kind {
Devin Jeanpierree77fa7e2022-06-02 14:05:58 -07001318 ImplKind::Trait { trait_name: TraitName::UnpinConstructor { .. }, .. } => {
Lukasz Anforowiczab65e292022-01-14 23:04:21 +00001319 // SAFETY: A user-defined constructor is not guaranteed to
1320 // initialize all the fields. To make the `assume_init()` call
1321 // below safe, the memory is zero-initialized first. This is a
1322 // bit safer, because zero-initialized memory represents a valid
1323 // value for the currently supported field types (this may
1324 // change once the bindings generator starts supporting
1325 // reference fields). TODO(b/213243309): Double-check if
1326 // zero-initialization is desirable here.
1327 quote! {
Devin Jeanpierrecd5ebf32022-07-08 05:31:55 -07001328 let mut tmp = ::std::mem::MaybeUninit::<Self>::zeroed();
Lukasz Anforowiczab65e292022-01-14 23:04:21 +00001329 unsafe {
Rosica Dejanovska68c11ac2022-05-12 03:48:59 -07001330 crate::detail::#thunk_ident( &mut tmp #( , #thunk_args )* );
Lukasz Anforowiczab65e292022-01-14 23:04:21 +00001331 tmp.assume_init()
Lukasz Anforowicz13cf7492021-12-22 15:29:52 +00001332 }
Lukasz Anforowicz13cf7492021-12-22 15:29:52 +00001333 }
Lukasz Anforowiczab65e292022-01-14 23:04:21 +00001334 }
Devin Jeanpierre7bddfdb2022-03-14 11:04:40 +00001335 _ => {
Devin Jeanpierreb8ce2c12022-07-13 10:34:01 -07001336 // Note: for the time being, all !Unpin values are treated as if they were not
1337 // trivially relocatable. We could, in the special case of trivial !Unpin types,
1338 // not generate the thunk at all, but this would be a bit of extra work.
1339 //
1340 // TODO(jeanpierreda): separately handle non-Unpin and non-trivial types.
1341 let mut body = if return_type.is_unpin() {
Felipe de A. Mello Pereiraec5f22c2022-11-02 04:04:01 -07001342 quote! { crate::detail::#thunk_ident( #( #thunk_args #clone_suffixes ),* ) }
Devin Jeanpierreb8ce2c12022-07-13 10:34:01 -07001343 } else {
1344 quote! {
1345 ::ctor::FnCtor::new(move |dest: ::std::pin::Pin<&mut ::std::mem::MaybeUninit<#return_type>>| {
1346 crate::detail::#thunk_ident(::std::pin::Pin::into_inner_unchecked(dest) #( , #thunk_args )*);
1347 })
1348 }
1349 };
Michael VanBemmelf6651302022-08-03 15:56:59 -07001350 // Discard the return value if requested (for example, when calling a C++
1351 // operator that returns a value from a Rust trait that returns
1352 // unit).
1353 if let ImplKind::Trait { drop_return: true, .. } = impl_kind {
1354 if return_type.is_unpin() {
1355 // If it's unpin, just discard it:
1356 body = quote! { #body; };
1357 } else {
1358 // Otherwise, in order to discard the return value and return void, we
1359 // need to run the constructor.
1360 body = quote! {let _ = ::ctor::emplace!(#body);};
Devin Jeanpierre9ced4ef2022-06-08 12:39:10 -07001361 }
Michael VanBemmelf6651302022-08-03 15:56:59 -07001362
1363 // We would need to do this, but it's no longer used:
1364 // return_type = RsTypeKind::Unit;
1365 let _ = return_type; // proof that we don't need to update it.
1366 quoted_return_type = quote! {};
Devin Jeanpierre9ced4ef2022-06-08 12:39:10 -07001367 }
Devin Jeanpierre7bddfdb2022-03-14 11:04:40 +00001368 // Only need to wrap everything in an `unsafe { ... }` block if
1369 // the *whole* api function is safe.
Devin Jeanpierre7c3d8ed2022-03-29 03:02:04 -07001370 if !impl_kind.is_unsafe() {
Devin Jeanpierre7bddfdb2022-03-14 11:04:40 +00001371 body = quote! { unsafe { #body } };
1372 }
Devin Jeanpierre93927e82022-08-01 14:05:54 -07001373 quote! {
1374 #thunk_prepare
1375 #body
1376 }
Devin Jeanpierre7bddfdb2022-03-14 11:04:40 +00001377 }
Lukasz Anforowiczab65e292022-01-14 23:04:21 +00001378 };
1379
Devin Jeanpierre7c3d8ed2022-03-29 03:02:04 -07001380 let pub_ = match impl_kind {
1381 ImplKind::None { .. } | ImplKind::Struct { .. } => quote! { pub },
1382 ImplKind::Trait { .. } => quote! {},
1383 };
1384 let unsafe_ = if impl_kind.is_unsafe() {
1385 quote! { unsafe }
1386 } else {
1387 quote! {}
Lukasz Anforowiczab65e292022-01-14 23:04:21 +00001388 };
1389
Lukasz Anforowicz5e623782022-03-14 16:52:23 +00001390 let fn_generic_params: TokenStream;
Devin Jeanpierre2b1e46d2022-08-01 13:42:12 -07001391 if let ImplKind::Trait { trait_name, trait_generic_params, impl_for, .. } = &mut impl_kind {
Michael VanBemmel106f66c2022-07-13 09:48:48 -07001392 // When the impl block is for some kind of reference to T, consider the lifetime
Devin Jeanpierreb8ce2c12022-07-13 10:34:01 -07001393 // parameters on the self parameter to be trait lifetimes so they can be
1394 // introduced before they are used.
Michael VanBemmel106f66c2022-07-13 09:48:48 -07001395 let first_param_lifetimes = match (impl_for, param_types.first()) {
1396 (ImplFor::RefT, Some(first_param)) => Some(first_param.lifetimes()),
1397 _ => None,
1398 };
1399
Devin Jeanpierre4e94a082022-08-10 01:58:35 -07001400 let trait_lifetimes: HashSet<Lifetime> =
Michael VanBemmel106f66c2022-07-13 09:48:48 -07001401 trait_name.lifetimes().chain(first_param_lifetimes.into_iter().flatten()).collect();
Devin Jeanpierree77fa7e2022-06-02 14:05:58 -07001402 fn_generic_params = format_generic_params(
Devin Jeanpierre4e94a082022-08-10 01:58:35 -07001403 lifetimes.iter().filter(|lifetime| !trait_lifetimes.contains(lifetime)),
Felipe de A. Mello Pereiraec5f22c2022-11-02 04:04:01 -07001404 std::iter::empty::<syn::Ident>(),
Devin Jeanpierree77fa7e2022-06-02 14:05:58 -07001405 );
Devin Jeanpierrea04bce12022-08-01 13:39:48 -07001406 *trait_generic_params = lifetimes
1407 .iter()
1408 .filter_map(|lifetime| {
Felipe de A. Mello Pereiraec5f22c2022-11-02 04:04:01 -07001409 if trait_lifetimes.contains(lifetime) { Some(quote! {#lifetime}) } else { None }
Devin Jeanpierrea04bce12022-08-01 13:39:48 -07001410 })
1411 .collect();
Lukasz Anforowicz5e623782022-03-14 16:52:23 +00001412 } else {
Felipe de A. Mello Pereiraec5f22c2022-11-02 04:04:01 -07001413 fn_generic_params = format_generic_params(&lifetimes, std::iter::empty::<syn::Ident>());
Lukasz Anforowicz5e623782022-03-14 16:52:23 +00001414 }
Lukasz Anforowicz95551272022-01-20 00:02:24 +00001415
Devin Jeanpierrefd4ff822022-07-15 01:43:55 -07001416 let function_return_type = match &impl_kind {
1417 ImplKind::Trait { associated_return_type: Some(ident), .. } => quote! {Self::#ident},
1418 _ => quoted_return_type.clone(),
1419 };
1420 let arrow = if !function_return_type.is_empty() {
1421 quote! {->}
1422 } else {
1423 quote! {}
1424 };
1425
Lukasz Anforowiczab65e292022-01-14 23:04:21 +00001426 quote! {
1427 #[inline(always)]
Lukasz Anforowicz5e623782022-03-14 16:52:23 +00001428 #pub_ #unsafe_ fn #func_name #fn_generic_params(
Devin Jeanpierrefd4ff822022-07-15 01:43:55 -07001429 #( #api_params ),* ) #arrow #function_return_type {
Lukasz Anforowiczab65e292022-01-14 23:04:21 +00001430 #func_body
1431 }
Lukasz Anforowicz13cf7492021-12-22 15:29:52 +00001432 }
Michael Forstered642022021-10-04 09:48:25 +00001433 };
1434
Devin Jeanpierred9cecff2022-03-29 02:53:58 -07001435 let doc_comment = generate_doc_comment(&func.doc_comment);
Lukasz Anforowiczab65e292022-01-14 23:04:21 +00001436 let api_func: TokenStream;
1437 let function_id: FunctionId;
Lukasz Anforowiczab65e292022-01-14 23:04:21 +00001438 match impl_kind {
Devin Jeanpierre7c3d8ed2022-03-29 03:02:04 -07001439 ImplKind::None { .. } => {
Lukasz Anforowiczab65e292022-01-14 23:04:21 +00001440 api_func = quote! { #doc_comment #api_func_def };
Lukasz Anforowicz8d064202022-09-01 07:31:06 -07001441 function_id = FunctionId {
1442 self_type: None,
1443 function_path: syn::parse2(quote! { #namespace_qualifier #func_name }).unwrap(),
1444 };
Lukasz Anforowiczab65e292022-01-14 23:04:21 +00001445 }
Devin Jeanpierre6784e5e2022-03-29 02:59:01 -07001446 ImplKind::Struct { record_name, .. } => {
Lukasz Anforowiczab65e292022-01-14 23:04:21 +00001447 api_func = quote! { impl #record_name { #doc_comment #api_func_def } };
1448 function_id = FunctionId {
1449 self_type: None,
Lukasz Anforowicz8d064202022-09-01 07:31:06 -07001450 function_path: syn::parse2(quote! {
1451 #namespace_qualifier #record_name :: #func_name
Devin Jeanpierre781eeb12022-09-06 19:23:25 -07001452 })
1453 .unwrap(),
Lukasz Anforowiczab65e292022-01-14 23:04:21 +00001454 };
1455 }
Michael VanBemmel106f66c2022-07-13 09:48:48 -07001456 ImplKind::Trait {
1457 trait_name,
1458 record_name,
Lukasz Anforowicz92be29f2022-09-02 17:12:29 -07001459 record_qualifier,
Michael VanBemmel106f66c2022-07-13 09:48:48 -07001460 impl_for,
1461 trait_generic_params,
Devin Jeanpierrefd4ff822022-07-15 01:43:55 -07001462 associated_return_type,
Michael VanBemmel106f66c2022-07-13 09:48:48 -07001463 ..
1464 } => {
Devin Jeanpierrefd4ff822022-07-15 01:43:55 -07001465 let extra_body = if let Some(name) = associated_return_type {
1466 let quoted_return_type = if quoted_return_type.is_empty() {
1467 quote! {()}
1468 } else {
1469 quoted_return_type
1470 };
1471 quote! {
1472 type #name = #quoted_return_type;
1473 }
Felipe de A. Mello Pereiraec5f22c2022-11-02 04:04:01 -07001474 } else if let TraitName::PartialOrd { ref params } = trait_name {
1475 let param = params.get(0).ok_or_else(|| anyhow!("No parameter to PartialOrd"))?;
Felipe de A. Mello Pereira69736292022-10-11 02:03:43 -07001476 quote! {
1477 #[inline(always)]
Felipe de A. Mello Pereiraec5f22c2022-11-02 04:04:01 -07001478 fn partial_cmp(&self, other: & #param) -> Option<core::cmp::Ordering> {
Felipe de A. Mello Pereira69736292022-10-11 02:03:43 -07001479 if self == other {
1480 return Some(core::cmp::Ordering::Equal);
1481 }
1482 if self < other {
1483 return Some(core::cmp::Ordering::Less);
1484 }
1485 if other < self {
1486 return Some(core::cmp::Ordering::Greater);
1487 }
1488 None
1489 }
1490 }
Devin Jeanpierrefd4ff822022-07-15 01:43:55 -07001491 } else {
1492 quote! {}
1493 };
1494
Devin Jeanpierre6f607372022-03-22 21:34:38 +00001495 let extra_items;
Felipe de A. Mello Pereiraec5f22c2022-11-02 04:04:01 -07001496 let trait_generic_params =
1497 format_generic_params(/* lifetimes= */ &[], trait_generic_params);
Devin Jeanpierre46d515c2022-05-03 02:36:54 -07001498 match &trait_name {
1499 TraitName::CtorNew(params) => {
Devin Jeanpierre46d515c2022-05-03 02:36:54 -07001500 if let [single_param] = params.as_slice() {
1501 extra_items = quote! {
Devin Jeanpierrecd5ebf32022-07-08 05:31:55 -07001502 impl #trait_generic_params ::ctor::CtorNew<(#single_param,)> for #record_name {
Devin Jeanpierre46d515c2022-05-03 02:36:54 -07001503 #extra_body
1504
1505 #[inline (always)]
1506 fn ctor_new(args: (#single_param,)) -> Self::CtorType {
1507 let (arg,) = args;
Devin Jeanpierrecd5ebf32022-07-08 05:31:55 -07001508 <Self as ::ctor::CtorNew<#single_param>>::ctor_new(arg)
Devin Jeanpierre46d515c2022-05-03 02:36:54 -07001509 }
1510 }
1511 }
1512 } else {
1513 extra_items = quote! {}
1514 }
Devin Jeanpierre6f607372022-03-22 21:34:38 +00001515 }
1516 _ => {
1517 extra_items = quote! {};
1518 }
1519 };
Michael VanBemmel106f66c2022-07-13 09:48:48 -07001520 let impl_for = match impl_for {
1521 ImplFor::T => quote! { #record_name },
1522 ImplFor::RefT => {
1523 let param = &param_types[0];
1524 quote! { #param }
1525 }
1526 };
Lukasz Anforowicz5e623782022-03-14 16:52:23 +00001527 api_func = quote! {
1528 #doc_comment
Michael VanBemmel106f66c2022-07-13 09:48:48 -07001529 impl #trait_generic_params #trait_name for #impl_for {
Devin Jeanpierre46d515c2022-05-03 02:36:54 -07001530 #extra_body
Lukasz Anforowicz5e623782022-03-14 16:52:23 +00001531 #api_func_def
1532 }
Devin Jeanpierre46d515c2022-05-03 02:36:54 -07001533 #extra_items
Lukasz Anforowicz5e623782022-03-14 16:52:23 +00001534 };
Lukasz Anforowicz92be29f2022-09-02 17:12:29 -07001535 let record_qualifier = record_qualifier.format_for_rs();
Lukasz Anforowiczab65e292022-01-14 23:04:21 +00001536 function_id = FunctionId {
Lukasz Anforowicz92be29f2022-09-02 17:12:29 -07001537 self_type: Some(syn::parse2(quote! { #record_qualifier #record_name }).unwrap()),
Devin Jeanpierre56fda1c2022-07-27 07:55:46 -07001538 function_path: syn::parse2(quote! { #trait_name :: #func_name }).unwrap(),
Lukasz Anforowiczab65e292022-01-14 23:04:21 +00001539 };
1540 }
1541 }
1542
Lukasz Anforowiczf8cfa562022-09-22 11:06:43 -07001543 Ok(Some(Rc::new((
Devin Jeanpierre52a14c32022-06-29 19:12:11 -07001544 RsSnippet { features, tokens: api_func },
1545 thunk.into(),
1546 Rc::new(function_id),
Devin Jeanpierrece549202022-07-25 08:54:34 -07001547 ))))
Devin Jeanpierre3eb5bbd2022-04-06 12:56:19 -07001548}
Devin Jeanpierre7e9a1de2021-12-03 08:04:22 +00001549
Devin Jeanpierref5013fb2022-08-01 13:56:27 -07001550/// The function signature for a function's bindings.
1551struct BindingsSignature {
1552 /// The lifetime parameters for the Rust function.
Devin Jeanpierre4e94a082022-08-10 01:58:35 -07001553 lifetimes: Vec<Lifetime>,
Devin Jeanpierref5013fb2022-08-01 13:56:27 -07001554
1555 /// The parameter list for the Rust function.
1556 ///
1557 /// For example, `vec![quote!{self}, quote!{x: &i32}]`.
1558 params: Vec<TokenStream>,
1559
1560 /// The return type fragment of the Rust function, as a token stream.
1561 ///
1562 /// This is the same as the actual return type, except that () is the empty
1563 /// tokens, non-Unpin by-value types are `impl Ctor<Output=#return_type> +
1564 /// ...`, and wherever the type is the type of `Self`, it gets replaced by
1565 /// literal `Self`.
1566 return_type_fragment: TokenStream,
1567
Devin Jeanpierre93927e82022-08-01 14:05:54 -07001568 /// Any preparation code to define the arguments in `thunk_args`.
1569 thunk_prepare: TokenStream,
1570
Devin Jeanpierref5013fb2022-08-01 13:56:27 -07001571 /// The arguments passed to the thunk, expressed in terms of `params`.
1572 thunk_args: Vec<TokenStream>,
1573}
1574
1575/// Reformats API parameters and return values to match Rust conventions and the
1576/// trait requirements.
1577///
1578/// For example:
1579///
1580/// * Use the `self` keyword for the this pointer.
1581/// * Use `Self` for the return value of constructor traits.
1582/// * For C++ constructors, remove `self` from the Rust side (as it becomes the
1583/// return value), retaining it on the C++ side / thunk args.
1584/// * serialize a `()` as the empty string.
1585fn function_signature(
1586 features: &mut BTreeSet<Ident>,
1587 func: &Func,
1588 impl_kind: &ImplKind,
1589 param_idents: &[Ident],
1590 param_types: &mut Vec<RsTypeKind>,
1591 return_type: &mut RsTypeKind,
1592) -> Result<BindingsSignature> {
1593 let mut api_params = Vec::with_capacity(func.params.len());
1594 let mut thunk_args = Vec::with_capacity(func.params.len());
Devin Jeanpierre93927e82022-08-01 14:05:54 -07001595 let mut thunk_prepare = quote! {};
Devin Jeanpierref5013fb2022-08-01 13:56:27 -07001596 for (i, (ident, type_)) in param_idents.iter().zip(param_types.iter()).enumerate() {
Devin Jeanpierrea99e0a32022-08-17 01:36:58 -07001597 type_.check_by_value()?;
Devin Jeanpierref5013fb2022-08-01 13:56:27 -07001598 if !type_.is_unpin() {
1599 // `impl Ctor` will fail to compile in a trait.
1600 // This will only be hit if there was a bug in api_func_shape.
1601 if let ImplKind::Trait { .. } = &impl_kind {
1602 panic!(
1603 "non-Unpin types cannot work by value in traits; this should have instead \
1604 become an rvalue reference to force the caller to materialize the Ctor."
1605 );
1606 }
1607 // The generated bindings require a move constructor.
1608 if !type_.is_move_constructible() {
1609 bail!("Non-movable, non-trivial_abi type '{type}' is not supported by value as parameter #{i}", type=quote!{#type_});
1610 }
1611 api_params.push(quote! {#ident: impl ::ctor::Ctor<Output=#type_>});
1612 thunk_args
1613 .push(quote! {::std::pin::Pin::into_inner_unchecked(::ctor::emplace!(#ident))});
1614 } else {
1615 api_params.push(quote! {#ident: #type_});
1616 thunk_args.push(quote! {#ident});
1617 }
1618 }
Devin Jeanpierre6bb81802022-08-10 02:08:47 -07001619
1620 let mut lifetimes: Vec<Lifetime> = unique_lifetimes(&*param_types).collect();
1621
Devin Jeanpierref5013fb2022-08-01 13:56:27 -07001622 let mut quoted_return_type = None;
1623 if let ImplKind::Trait {
1624 trait_name: trait_name @ (TraitName::UnpinConstructor { .. } | TraitName::CtorNew(..)),
1625 ..
1626 } = &impl_kind
1627 {
Devin Jeanpierre4e94a082022-08-10 01:58:35 -07001628 // For constructors, we move the output parameter to be the return value.
1629 // The return value is "really" void.
1630 ensure!(
1631 func.return_type.rs_type.is_unit_type(),
1632 "Unexpectedly non-void return type of a constructor"
1633 );
1634
Devin Jeanpierref5013fb2022-08-01 13:56:27 -07001635 // Presence of element #0 is indirectly verified by a `Constructor`-related
1636 // `match` branch a little bit above.
1637 *return_type = param_types[0]
1638 .referent()
1639 .ok_or_else(|| anyhow!("Expected pointer/reference for `__this` parameter"))?
1640 .clone();
1641 quoted_return_type = Some(quote! {Self});
1642
Devin Jeanpierre4e94a082022-08-10 01:58:35 -07001643 // Grab the `__this` lifetime to remove it from the lifetime parameters.
1644 let this_lifetime = param_types[0]
1645 .lifetime()
1646 .ok_or_else(|| anyhow!("Missing lifetime for `__this` parameter"))?;
1647
Devin Jeanpierref5013fb2022-08-01 13:56:27 -07001648 // Drop `__this` parameter from the public Rust API.
1649 api_params.remove(0);
1650 thunk_args.remove(0);
1651 param_types.remove(0);
1652
1653 // Remove the lifetime associated with `__this`.
Devin Jeanpierre4e94a082022-08-10 01:58:35 -07001654 lifetimes.retain(|l| l != &this_lifetime);
Devin Jeanpierref5013fb2022-08-01 13:56:27 -07001655 if let Some(type_still_dependent_on_removed_lifetime) = param_types
1656 .iter()
1657 .flat_map(|t| t.lifetimes())
Devin Jeanpierre4e94a082022-08-10 01:58:35 -07001658 .find(|lifetime| lifetime == &this_lifetime)
Devin Jeanpierref5013fb2022-08-01 13:56:27 -07001659 {
1660 bail!(
1661 "The lifetime of `__this` is unexpectedly also used by another \
Devin Jeanpierre4e94a082022-08-10 01:58:35 -07001662 parameter: {type_still_dependent_on_removed_lifetime:?}",
Devin Jeanpierref5013fb2022-08-01 13:56:27 -07001663 );
1664 }
1665
Devin Jeanpierre93927e82022-08-01 14:05:54 -07001666 // CtorNew groups parameters into a tuple.
Devin Jeanpierref5013fb2022-08-01 13:56:27 -07001667 if let TraitName::CtorNew(args_type) = trait_name {
1668 let args_type = format_tuple_except_singleton(args_type);
1669 api_params = vec![quote! {args: #args_type}];
Devin Jeanpierre93927e82022-08-01 14:05:54 -07001670 let thunk_vars = format_tuple_except_singleton(&thunk_args);
1671 thunk_prepare.extend(quote! {let #thunk_vars = args;});
Devin Jeanpierref5013fb2022-08-01 13:56:27 -07001672 }
1673 }
1674
1675 let return_type_fragment = if return_type == &RsTypeKind::Unit {
1676 quote! {}
1677 } else {
1678 let ty = quoted_return_type.unwrap_or_else(|| quote! {#return_type});
1679 if return_type.is_unpin() {
1680 quote! {#ty}
1681 } else {
1682 // This feature seems destined for stabilization, and makes the code
1683 // simpler. We don't need it for simple functions, but if the return type is
1684 // used as an associated type for a trait.
1685 features.insert(make_rs_ident("type_alias_impl_trait"));
1686 // The returned lazy FnCtor depends on all inputs.
1687 let extra_lifetimes = lifetimes.iter().map(|a| quote! {+ ::ctor::Captures<#a>});
1688 quote! {impl ::ctor::Ctor<Output=#ty> #(#extra_lifetimes)* }
1689 }
1690 };
1691
1692 // Change `__this: &'a SomeStruct` into `&'a self` if needed.
1693 if impl_kind.format_first_param_as_self() {
1694 let first_api_param = param_types
1695 .get(0)
1696 .ok_or_else(|| anyhow!("No parameter to format as 'self': {:?}", func))?;
1697 // If param_types[0] exists, so do api_params[0] and thunk_args[0].
1698 match impl_kind {
1699 ImplKind::None { .. } => unreachable!(),
1700 ImplKind::Struct { .. } | ImplKind::Trait { impl_for: ImplFor::T, .. } => {
1701 // In the ImplFor::T reference style (which is implied for ImplKind::Struct) the
1702 // impl block is for `T`. The `self` parameter has a type determined by the
1703 // first parameter (typically a reference of some kind) and can be passed to a
1704 // thunk via the expression `self`.
1705 api_params[0] = first_api_param.format_as_self_param()?;
1706 thunk_args[0] = quote! { self };
1707 }
1708 ImplKind::Trait { impl_for: ImplFor::RefT, .. } => {
1709 // In the ImplFor::RefT reference style the impl block is for a reference type
1710 // referring to T (`&T`, `&mut T`, or `Pin<&mut T>` so a bare `self` parameter
1711 // has that type and can be passed to a thunk via the expression `self`.
1712 api_params[0] = quote! { self };
1713 thunk_args[0] = quote! { self };
1714 }
1715 }
1716 }
1717
Devin Jeanpierre93927e82022-08-01 14:05:54 -07001718 Ok(BindingsSignature {
1719 lifetimes,
1720 params: api_params,
1721 return_type_fragment,
1722 thunk_prepare,
1723 thunk_args,
1724 })
Devin Jeanpierref5013fb2022-08-01 13:56:27 -07001725}
1726
Devin Jeanpierre3eb5bbd2022-04-06 12:56:19 -07001727fn generate_func_thunk(
Devin Jeanpierreb8ce2c12022-07-13 10:34:01 -07001728 db: &dyn BindingsGenerator,
Devin Jeanpierre3eb5bbd2022-04-06 12:56:19 -07001729 func: &Func,
1730 param_idents: &[Ident],
Devin Jeanpierre22f91492022-04-06 13:01:23 -07001731 param_types: &[RsTypeKind],
Devin Jeanpierreb8ce2c12022-07-13 10:34:01 -07001732 return_type: &RsTypeKind,
Devin Jeanpierre3eb5bbd2022-04-06 12:56:19 -07001733) -> Result<TokenStream> {
Devin Jeanpierreb8ce2c12022-07-13 10:34:01 -07001734 let thunk_attr = if can_skip_cc_thunk(db, func) {
Devin Jeanpierre3eb5bbd2022-04-06 12:56:19 -07001735 let mangled_name = &func.mangled_name;
1736 quote! {#[link_name = #mangled_name]}
1737 } else {
1738 quote! {}
Michael Forstered642022021-10-04 09:48:25 +00001739 };
Devin Jeanpierre6bb81802022-08-10 02:08:47 -07001740 let lifetimes: Vec<_> = unique_lifetimes(param_types).collect();
Michael Forstered642022021-10-04 09:48:25 +00001741
Devin Jeanpierreb8ce2c12022-07-13 10:34:01 -07001742 // The first parameter is the output parameter, if any.
Devin Jeanpierre22f91492022-04-06 13:01:23 -07001743 let mut param_types = param_types.into_iter();
Devin Jeanpierreb8ce2c12022-07-13 10:34:01 -07001744 let mut param_idents = param_idents.into_iter();
1745 let mut out_param = None;
1746 let mut out_param_ident = None;
1747 let mut return_type_fragment = return_type.format_as_return_type_fragment();
Devin Jeanpierre3eb5bbd2022-04-06 12:56:19 -07001748 if func.name == UnqualifiedIdentifier::Constructor {
Devin Jeanpierreb8ce2c12022-07-13 10:34:01 -07001749 // For constructors, inject MaybeUninit into the type of `__this_` parameter.
Devin Jeanpierre22f91492022-04-06 13:01:23 -07001750 let first_param = param_types
Devin Jeanpierre3eb5bbd2022-04-06 12:56:19 -07001751 .next()
1752 .ok_or_else(|| anyhow!("Constructors should have at least one parameter (__this)"))?;
Devin Jeanpierreb8ce2c12022-07-13 10:34:01 -07001753 out_param = Some(first_param.format_mut_ref_as_uninitialized().with_context(|| {
Devin Jeanpierre3eb5bbd2022-04-06 12:56:19 -07001754 format!(
1755 "Failed to format `__this` param for a constructor thunk: {:?}",
1756 func.params.get(0)
1757 )
1758 })?);
Devin Jeanpierreb8ce2c12022-07-13 10:34:01 -07001759 out_param_ident = Some(param_idents.next().unwrap().clone());
1760 } else if !return_type.is_unpin() {
1761 // For nontrivial return types, create a new out parameter.
1762 // The lifetime doesn't matter, so we can insert a new anonymous lifetime here.
1763 out_param = Some(quote! {
1764 &mut ::std::mem::MaybeUninit< #return_type >
1765 });
1766 out_param_ident = Some(make_rs_ident("__return"));
1767 return_type_fragment = quote! {};
Devin Jeanpierre3eb5bbd2022-04-06 12:56:19 -07001768 }
1769
Devin Jeanpierre52a14c32022-06-29 19:12:11 -07001770 let thunk_ident = thunk_ident(&func);
Devin Jeanpierre6bb81802022-08-10 02:08:47 -07001771
Felipe de A. Mello Pereiraec5f22c2022-11-02 04:04:01 -07001772 let generic_params = format_generic_params(&lifetimes, std::iter::empty::<syn::Ident>());
Devin Jeanpierreb8ce2c12022-07-13 10:34:01 -07001773 let param_idents = out_param_ident.as_ref().into_iter().chain(param_idents);
Devin Jeanpierre11ce2b92022-07-27 07:54:21 -07001774 let param_types = out_param.into_iter().chain(param_types.map(|t| {
1775 if !t.is_unpin() {
1776 quote! {&mut #t}
1777 } else {
1778 quote! {#t}
1779 }
1780 }));
Devin Jeanpierre3eb5bbd2022-04-06 12:56:19 -07001781
1782 Ok(quote! {
1783 #thunk_attr
1784 pub(crate) fn #thunk_ident #generic_params( #( #param_idents: #param_types ),*
1785 ) #return_type_fragment ;
1786 })
Michael Forstered642022021-10-04 09:48:25 +00001787}
1788
Michael Forstercc5941a2021-10-07 07:12:24 +00001789fn generate_doc_comment(comment: &Option<String>) -> TokenStream {
1790 match comment {
Michael Forster028800b2021-10-05 12:39:59 +00001791 Some(text) => {
Marcel Hlopko89547752021-12-10 09:39:41 +00001792 // token_stream_printer (and rustfmt) don't put a space between /// and the doc
1793 // comment, let's add it here so our comments are pretty.
Lukasz Anforowiczdd9ae0f2022-02-17 15:52:53 +00001794 let doc = format!(" {}", text.replace('\n', "\n "));
Michael Forster028800b2021-10-05 12:39:59 +00001795 quote! {#[doc=#doc]}
1796 }
1797 None => quote! {},
Michael Forstercc5941a2021-10-07 07:12:24 +00001798 }
1799}
Lukasz Anforowiczdaff0402021-12-23 00:37:50 +00001800
Felipe de A. Mello Pereiraec5f22c2022-11-02 04:04:01 -07001801fn format_generic_params<'a, T: ToTokens>(
1802 lifetimes: impl IntoIterator<Item = &'a Lifetime>,
1803 types: impl IntoIterator<Item = T>,
1804) -> TokenStream {
1805 let mut lifetimes = lifetimes.into_iter().filter(|lifetime| &*lifetime.0 != "_").peekable();
1806 let mut types = types.into_iter().peekable();
1807 if lifetimes.peek().is_none() && types.peek().is_none() {
Lukasz Anforowiczdaff0402021-12-23 00:37:50 +00001808 quote! {}
1809 } else {
Felipe de A. Mello Pereiraec5f22c2022-11-02 04:04:01 -07001810 quote! { < #( #lifetimes ),* #( #types ),*> }
Lukasz Anforowiczdaff0402021-12-23 00:37:50 +00001811 }
1812}
1813
Devin Jeanpierread125742022-04-11 13:50:28 -07001814/// Formats singletons as themselves, and collections of n!=1 items as a tuple.
1815///
1816/// In other words, this formats a collection of things as if via `#(#items),*`,
1817/// but without lint warnings.
1818///
1819/// For example:
1820///
1821/// * [] => ()
1822/// * [x] => x // equivalent to (x), but lint-free.
1823/// * [x, y] => (x, y)
1824fn format_tuple_except_singleton<T: ToTokens>(items: &[T]) -> TokenStream {
1825 match items {
1826 [singleton] => quote! {#singleton},
1827 items => quote! {(#(#items),*)},
1828 }
1829}
1830
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00001831fn should_implement_drop(record: &Record) -> bool {
Lukasz Anforowiczff7df4a2022-06-02 14:27:45 -07001832 match record.destructor {
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00001833 // TODO(b/202258760): Only omit destructor if `Copy` is specified.
Lukasz Anforowiczff7df4a2022-06-02 14:27:45 -07001834 SpecialMemberFunc::Trivial => false,
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00001835
1836 // TODO(b/212690698): Avoid calling into the C++ destructor (e.g. let
1837 // Rust drive `drop`-ing) to avoid (somewhat unergonomic) ManuallyDrop
1838 // if we can ask Rust to preserve C++ field destruction order in
1839 // NontrivialMembers case.
Lukasz Anforowiczff7df4a2022-06-02 14:27:45 -07001840 SpecialMemberFunc::NontrivialMembers => true,
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00001841
1842 // The `impl Drop` for NontrivialUserDefined needs to call into the
1843 // user-defined destructor on C++ side.
Lukasz Anforowiczff7df4a2022-06-02 14:27:45 -07001844 SpecialMemberFunc::NontrivialUserDefined => true,
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00001845
1846 // TODO(b/213516512): Today the IR doesn't contain Func entries for
1847 // deleted functions/destructors/etc. But, maybe we should generate
1848 // `impl Drop` in this case? With `unreachable!`? With
1849 // `std::mem::forget`?
Lukasz Anforowiczff7df4a2022-06-02 14:27:45 -07001850 SpecialMemberFunc::Unavailable => false,
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00001851 }
1852}
1853
1854/// Returns whether fields of type `ty` need to be wrapped in `ManuallyDrop<T>`
1855/// to prevent the fields from being destructed twice (once by the C++
1856/// destructor calkled from the `impl Drop` of the struct and once by `drop` on
1857/// the Rust side).
1858///
1859/// A type is safe to destroy twice if it implements `Copy`. Fields of such
1860/// don't need to be wrapped in `ManuallyDrop<T>` even if the struct
1861/// containing the fields provides an `impl Drop` that calles into a C++
1862/// destructor (in addition to dropping the fields on the Rust side).
1863///
1864/// Note that it is not enough to just be `!needs_drop<T>()`: Rust only
1865/// guarantees that it is safe to use-after-destroy for `Copy` types. See
1866/// e.g. the documentation for
1867/// [`drop_in_place`](https://doc.rust-lang.org/std/ptr/fn.drop_in_place.html):
1868///
1869/// > if `T` is not `Copy`, using the pointed-to value after calling
1870/// > `drop_in_place` can cause undefined behavior
Teddy Katzd2cd1422022-04-04 09:41:33 -07001871///
Devin Jeanpierrefec5bfe2022-04-05 10:59:10 -07001872/// For non-Copy union fields, failing to use `ManuallyDrop<T>` would
1873/// additionally cause a compile-time error until https://github.com/rust-lang/rust/issues/55149 is stabilized.
Devin Jeanpierre45b01962022-07-07 06:12:11 -07001874fn needs_manually_drop(db: &Database, ty: ir::RsType) -> Result<bool> {
Devin Jeanpierre409f6f62022-07-07 02:00:26 -07001875 let ty_implements_copy = db.rs_type_kind(ty)?.implements_copy();
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00001876 Ok(!ty_implements_copy)
1877}
1878
Lukasz Anforowicz92be29f2022-09-02 17:12:29 -07001879#[derive(Debug, PartialEq, Eq, Clone, Hash, PartialOrd, Ord)]
1880struct NamespaceQualifier(Vec<String>);
1881
1882impl NamespaceQualifier {
1883 fn new(item_id: ItemId, ir: &IR) -> Result<Self> {
1884 let mut namespaces = vec![];
1885 let item: &Item = ir.find_decl(item_id)?;
1886 let mut enclosing_namespace_id = item.enclosing_namespace_id();
1887 while let Some(parent_id) = enclosing_namespace_id {
1888 let namespace_item = ir.find_decl(parent_id)?;
1889 match namespace_item {
1890 Item::Namespace(ns) => {
1891 namespaces.push(ns.name.identifier.clone());
1892 enclosing_namespace_id = ns.enclosing_namespace_id;
1893 }
1894 _ => {
1895 bail!("Expected namespace");
1896 }
Rosica Dejanovska8283fe62022-05-09 10:04:28 -07001897 }
1898 }
Lukasz Anforowicz92be29f2022-09-02 17:12:29 -07001899 Ok(Self(namespaces.into_iter().rev().collect_vec()))
Rosica Dejanovska8283fe62022-05-09 10:04:28 -07001900 }
Rosica Dejanovska8283fe62022-05-09 10:04:28 -07001901
Lukasz Anforowicz92be29f2022-09-02 17:12:29 -07001902 fn format_for_rs(&self) -> TokenStream {
1903 let namespace_rs_idents = self.0.iter().map(|ns| make_rs_ident(ns));
1904 quote! { #(#namespace_rs_idents::)* }
1905 }
Lukasz Anforowicz4e2e0162022-09-01 07:07:32 -07001906
Lukasz Anforowicz92be29f2022-09-02 17:12:29 -07001907 fn format_for_cc(&self) -> TokenStream {
1908 let namespace_cc_idents = self.0.iter().map(|ns| format_cc_ident(ns));
1909 quote! { #(#namespace_cc_idents::)* }
1910 }
Lukasz Anforowicz4e2e0162022-09-01 07:07:32 -07001911}
1912
Devin Jeanpierrec0543eb2022-04-20 16:00:34 -07001913/// Generates Rust source code for a given incomplete record declaration.
1914fn generate_incomplete_record(incomplete_record: &IncompleteRecord) -> Result<TokenStream> {
Rosica Dejanovskae12d7172022-06-22 12:20:17 -07001915 let ident = make_rs_ident(&incomplete_record.rs_name);
1916 let name = &incomplete_record.rs_name;
Devin Jeanpierrec0543eb2022-04-20 16:00:34 -07001917 Ok(quote! {
1918 forward_declare::forward_declare!(
1919 pub #ident __SPACE__ = __SPACE__ forward_declare::symbol!(#name)
1920 );
1921 })
1922}
1923
Lukasz Anforowicz0dbcf0e2022-05-17 06:46:24 -07001924fn make_rs_field_ident(field: &Field, field_index: usize) -> Ident {
1925 match field.identifier.as_ref() {
1926 None => make_rs_ident(&format!("__unnamed_field{}", field_index)),
1927 Some(Identifier { identifier }) => make_rs_ident(identifier),
1928 }
1929}
1930
Lukasz Anforowiczfea0db92022-05-17 17:28:04 -07001931/// Gets the type of `field` for layout purposes.
1932///
1933/// Note that `get_field_rs_type_for_layout` may return Err (for
1934/// `is_no_unique_address` fields) even if `field.type_` is Ok.
1935fn get_field_rs_type_for_layout(field: &Field) -> Result<&RsType, &str> {
1936 // [[no_unique_address]] fields are replaced by a type-less, unaligned block of
1937 // memory which fills space up to the next field.
Lukasz Anforowicz5765bb82022-05-17 17:21:06 -07001938 // See: docs/struct_layout
Lukasz Anforowiczfea0db92022-05-17 17:28:04 -07001939 if field.is_no_unique_address {
1940 return Err("`[[no_unique_address]]` attribute was present.");
1941 }
1942
1943 field.type_.as_ref().map(|t| &t.rs_type).map_err(String::as_str)
Lukasz Anforowicz5765bb82022-05-17 17:21:06 -07001944}
1945
Michael Forster82c02d32022-05-20 21:47:33 -07001946/// Returns the type of a type-less, unaligned block of memory that can hold a
1947/// specified number of bits, rounded up to the next multiple of 8.
1948fn bit_padding(padding_size_in_bits: usize) -> TokenStream {
1949 let padding_size = Literal::usize_unsuffixed((padding_size_in_bits + 7) / 8);
Devin Jeanpierrecd5ebf32022-07-08 05:31:55 -07001950 quote! { [::std::mem::MaybeUninit<u8>; #padding_size] }
Michael Forster82c02d32022-05-20 21:47:33 -07001951}
1952
Michael Forsterbee84482021-10-13 08:35:38 +00001953/// Generates Rust source code for a given `Record` and associated assertions as
1954/// a tuple.
Michael VanBemmelf5cbdf42022-10-14 17:00:11 -07001955fn generate_record(
1956 db: &Database,
1957 record: &Rc<Record>,
1958 errors: &mut dyn ErrorReporting,
1959) -> Result<GeneratedItem> {
Devin Jeanpierre52a14c32022-06-29 19:12:11 -07001960 let ir = db.ir();
Lukasz Anforowicz4c3a2cc2022-03-11 00:24:49 +00001961 let ident = make_rs_ident(&record.rs_name);
Lukasz Anforowicz92be29f2022-09-02 17:12:29 -07001962 let namespace_qualifier = NamespaceQualifier::new(record.id, &ir)?.format_for_rs();
Rosica Dejanovska8283fe62022-05-09 10:04:28 -07001963 let qualified_ident = {
Lukasz Anforowicz4e2e0162022-09-01 07:07:32 -07001964 quote! { crate:: #namespace_qualifier #ident }
Rosica Dejanovska8283fe62022-05-09 10:04:28 -07001965 };
Michael Forstercc5941a2021-10-07 07:12:24 +00001966 let doc_comment = generate_doc_comment(&record.doc_comment);
Lukasz Anforowiczfed64e62022-03-22 22:39:04 +00001967
1968 let mut field_copy_trait_assertions: Vec<TokenStream> = vec![];
Michael Forster82c02d32022-05-20 21:47:33 -07001969
1970 let fields_with_bounds = (record.fields.iter())
1971 .map(|field| {
1972 (
1973 // We don't represent bitfields directly in Rust. We drop the field itself here
1974 // and only retain the offset information. Adjacent bitfields then get merged in
1975 // the next step.
1976 if field.is_bitfield { None } else { Some(field) },
1977 field.offset,
1978 // We retain the end offset of fields only if we have a matching Rust type
1979 // to represent them. Otherwise we'll fill up all the space to the next field.
1980 // See: docs/struct_layout
Marcel Hlopkof05621b2022-05-25 00:26:06 -07001981 match get_field_rs_type_for_layout(field) {
1982 // Regular field
1983 Ok(_rs_type) => Some(field.offset + field.size),
1984 // Opaque field
Michael VanBemmel106f66c2022-07-13 09:48:48 -07001985 Err(_error) => {
1986 if record.is_union() {
1987 Some(field.size)
1988 } else {
1989 None
1990 }
1991 }
Marcel Hlopkof05621b2022-05-25 00:26:06 -07001992 },
Michael Forster82c02d32022-05-20 21:47:33 -07001993 vec![format!(
1994 "{} : {} bits",
1995 field.identifier.as_ref().map(|i| i.identifier.clone()).unwrap_or("".into()),
1996 field.size
1997 )],
1998 )
1999 })
2000 // Merge consecutive bitfields. This is necessary, because they may share storage in the
2001 // same byte.
2002 .coalesce(|first, second| match (first, second) {
2003 ((None, offset, _, desc1), (None, _, end, desc2)) => {
2004 Ok((None, offset, end, [desc1, desc2].concat()))
2005 }
2006 pair => Err(pair),
2007 });
2008
2009 // Pair up fields with the preceeding and following fields (if any):
2010 // - the end offset of the previous field determines if we need to insert
2011 // padding.
2012 // - the start offset of the next field may be need to grow the current field to
2013 // there.
2014 // This uses two separate `map` invocations on purpose to limit available state.
2015 let field_definitions = iter::once(None)
2016 .chain(fields_with_bounds.clone().map(Some))
2017 .chain(iter::once(None))
2018 .tuple_windows()
2019 .map(|(prev, cur, next)| {
2020 let (field, offset, end, desc) = cur.unwrap();
2021 let prev_end = prev.as_ref().map(|(_, _, e, _)| *e).flatten().unwrap_or(offset);
2022 let next_offset = next.map(|(_, o, _, _)| o);
Kinuko Yasuda54b75d72022-08-18 10:09:54 -07002023 let end = end.or(next_offset).unwrap_or(record.size * 8);
Michael Forster82c02d32022-05-20 21:47:33 -07002024
2025 if let Some((Some(prev_field), _, Some(prev_end), _)) = prev {
2026 assert!(
Lukasz Anforowiczd4742ff2022-07-11 17:05:02 -07002027 record.is_union() || prev_end <= offset,
Michael Forster82c02d32022-05-20 21:47:33 -07002028 "Unexpected offset+size for field {:?} in record {}",
2029 prev_field,
2030 record.cc_name
2031 );
2032 }
2033
2034 (field, prev_end, offset, end, desc)
2035 })
Devin Jeanpierreb69bcae2022-02-03 09:45:50 +00002036 .enumerate()
Michael Forster82c02d32022-05-20 21:47:33 -07002037 .map(|(field_index, (field, prev_end, offset, end, desc))| {
2038 // `is_opaque_blob` and bitfield representations are always
2039 // unaligned, even though the actual C++ field might be aligned.
2040 // To put the current field at the right offset, we might need to
2041 // insert some extra padding.
2042 //
2043 // No padding should be needed if the type of the current field is
2044 // known (i.e. if the current field is correctly aligned based on
2045 // its original type).
2046 //
2047 // We also don't need padding if we're in a union.
Lukasz Anforowiczd4742ff2022-07-11 17:05:02 -07002048 let padding_size_in_bits = if record.is_union()
Michael Forster82c02d32022-05-20 21:47:33 -07002049 || (field.is_some() && get_field_rs_type_for_layout(field.unwrap()).is_ok())
2050 {
2051 0
2052 } else {
2053 let padding_start = (prev_end + 7) / 8 * 8; // round up to byte boundary
2054 offset - padding_start
2055 };
2056
2057 let padding = if padding_size_in_bits == 0 {
2058 quote! {}
2059 } else {
2060 let padding_name = make_rs_ident(&format!("__padding{}", field_index));
2061 let padding_type = bit_padding(padding_size_in_bits);
2062 quote! { #padding_name: #padding_type, }
2063 };
2064
2065 // Bitfields get represented by private padding to ensure overall
2066 // struct layout is compatible.
2067 if field.is_none() {
2068 let name = make_rs_ident(&format!("__bitfields{}", field_index));
2069 let bitfield_padding = bit_padding(end - offset);
2070 return Ok(quote! {
2071 __NEWLINE__ #( __COMMENT__ #desc )*
2072 #padding #name: #bitfield_padding
2073 });
2074 }
2075 let field = field.unwrap();
2076
Lukasz Anforowicz0dbcf0e2022-05-17 06:46:24 -07002077 let ident = make_rs_field_ident(field, field_index);
Lukasz Anforowiczfea0db92022-05-17 17:28:04 -07002078 let doc_comment = match field.type_.as_ref() {
2079 Ok(_) => generate_doc_comment(&field.doc_comment),
2080 Err(msg) => {
2081 let supplemental_text =
2082 format!("Reason for representing this field as a blob of bytes:\n{}", msg);
2083 let new_text = match field.doc_comment.as_ref() {
2084 None => supplemental_text,
2085 Some(old_text) => format!("{}\n\n{}", old_text, supplemental_text),
2086 };
2087 generate_doc_comment(&Some(new_text))
2088 }
2089 };
2090 let access = if field.access == AccessSpecifier::Public
2091 && get_field_rs_type_for_layout(field).is_ok()
2092 {
Lukasz Anforowicz0dbcf0e2022-05-17 06:46:24 -07002093 quote! { pub }
2094 } else {
Rosica Dejanovska6676ad82022-06-07 14:28:59 -07002095 quote! { pub(crate) }
Lukasz Anforowicz0dbcf0e2022-05-17 06:46:24 -07002096 };
2097
Lukasz Anforowiczfea0db92022-05-17 17:28:04 -07002098 let field_type = match get_field_rs_type_for_layout(field) {
Michael Forster82c02d32022-05-20 21:47:33 -07002099 Err(_) => bit_padding(end - field.offset),
Lukasz Anforowiczfea0db92022-05-17 17:28:04 -07002100 Ok(rs_type) => {
Devin Jeanpierre409f6f62022-07-07 02:00:26 -07002101 let type_kind = db.rs_type_kind(rs_type.clone()).with_context(|| {
Lukasz Anforowicz0dbcf0e2022-05-17 06:46:24 -07002102 format!(
2103 "Failed to format type for field {:?} on record {:?}",
2104 field, record
2105 )
2106 })?;
Devin Jeanpierre409f6f62022-07-07 02:00:26 -07002107 let mut formatted = quote! {#type_kind};
Lukasz Anforowiczd4742ff2022-07-11 17:05:02 -07002108 if should_implement_drop(record) || record.is_union() {
Devin Jeanpierre409f6f62022-07-07 02:00:26 -07002109 if needs_manually_drop(db, rs_type.clone())? {
Lukasz Anforowiczfea0db92022-05-17 17:28:04 -07002110 // TODO(b/212690698): Avoid (somewhat unergonomic) ManuallyDrop
2111 // if we can ask Rust to preserve field destruction order if the
Lukasz Anforowiczff7df4a2022-06-02 14:27:45 -07002112 // destructor is the SpecialMemberFunc::NontrivialMembers
Lukasz Anforowiczfea0db92022-05-17 17:28:04 -07002113 // case.
Devin Jeanpierrecd5ebf32022-07-08 05:31:55 -07002114 formatted = quote! { ::std::mem::ManuallyDrop<#formatted> }
Lukasz Anforowiczfea0db92022-05-17 17:28:04 -07002115 } else {
2116 field_copy_trait_assertions.push(quote! {
2117 const _: () = {
2118 static_assertions::assert_impl_all!(#formatted: Copy);
2119 };
2120 });
2121 }
2122 };
2123 formatted
2124 }
Lukasz Anforowicz6d553632022-01-06 21:36:14 +00002125 };
Lukasz Anforowicz0dbcf0e2022-05-17 06:46:24 -07002126
Lukasz Anforowicz5765bb82022-05-17 17:21:06 -07002127 Ok(quote! { #padding #doc_comment #access #ident: #field_type })
Devin Jeanpierre7e9a1de2021-12-03 08:04:22 +00002128 })
Devin Jeanpierre09c6f452021-09-29 07:34:24 +00002129 .collect::<Result<Vec<_>>>()?;
Lukasz Anforowiczfed64e62022-03-22 22:39:04 +00002130
Kinuko Yasuda54b75d72022-08-18 10:09:54 -07002131 let size = Literal::usize_unsuffixed(record.size);
Lukasz Anforowiczdc37d6d2022-05-17 08:20:13 -07002132 let alignment = Literal::usize_unsuffixed(record.alignment);
Lukasz Anforowiczd4742ff2022-07-11 17:05:02 -07002133 let field_offset_assertions = if record.is_union() {
Marcel Hlopkofa9a3952022-05-10 01:34:13 -07002134 // TODO(https://github.com/Gilnaa/memoffset/issues/66): generate assertions for unions once
2135 // offsetof supports them.
2136 vec![]
2137 } else {
Michael Forster82c02d32022-05-20 21:47:33 -07002138 fields_with_bounds
Devin Jeanpierrea2be2a22022-05-18 18:59:05 -07002139 .enumerate()
Michael Forster82c02d32022-05-20 21:47:33 -07002140 .map(|(field_index, (field, _, _, _))| {
2141 if let Some(field) = field {
2142 let field_ident = make_rs_field_ident(field, field_index);
Lukasz Anforowicz30360ba2022-05-23 12:15:12 -07002143
2144 // The assertion below reinforces that the division by 8 on the next line is
2145 // justified (because the bitfields have been coallesced / filtered out
2146 // earlier).
2147 assert_eq!(field.offset % 8, 0);
2148 let expected_offset = Literal::usize_unsuffixed(field.offset / 8);
2149
Michael Forster82c02d32022-05-20 21:47:33 -07002150 let actual_offset_expr = quote! {
Lukasz Anforowiczed94b242022-09-07 11:47:58 -07002151 memoffset::offset_of!(#qualified_ident, #field_ident)
Michael Forster82c02d32022-05-20 21:47:33 -07002152 };
2153 quote! {
2154 const _: () = assert!(#actual_offset_expr == #expected_offset);
2155 }
2156 } else {
2157 quote! {}
Devin Jeanpierrea2be2a22022-05-18 18:59:05 -07002158 }
2159 })
2160 .collect_vec()
Marcel Hlopkofa9a3952022-05-10 01:34:13 -07002161 };
Lukasz Anforowiczb4d17782022-07-07 08:09:13 -07002162 // TODO(b/212696226): Generate `assert_impl_all!` or `assert_not_impl_any!`
Lukasz Anforowiczfed64e62022-03-22 22:39:04 +00002163 // assertions about the `Copy` trait - this trait should be implemented
2164 // iff `should_implement_drop(record)` is false.
Devin Jeanpierre781eeb12022-09-06 19:23:25 -07002165 let mut features = BTreeSet::new();
Devin Jeanpierre2ed14ec2021-10-06 11:32:19 +00002166
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00002167 let derives = generate_derives(record);
Devin Jeanpierre9227d2c2021-10-06 12:26:05 +00002168 let derives = if derives.is_empty() {
2169 quote! {}
2170 } else {
2171 quote! {#[derive( #(#derives),* )]}
2172 };
Lukasz Anforowiczd4742ff2022-07-11 17:05:02 -07002173 let record_kind = if record.is_union() {
Teddy Katzd2cd1422022-04-04 09:41:33 -07002174 quote! { union }
2175 } else {
2176 quote! { struct }
2177 };
Devin Jeanpierre190b90a2022-05-24 06:00:34 -07002178
Devin Jeanpierre215ee8e2022-05-16 16:09:41 -07002179 let recursively_pinned_attribute = if record.is_unpin() {
Lukasz Anforowiczdd9ae0f2022-02-17 15:52:53 +00002180 quote! {}
Devin Jeanpierreea700d32021-10-06 11:33:56 +00002181 } else {
Michael Forsterbee84482021-10-13 08:35:38 +00002182 // negative_impls are necessary for universal initialization due to Rust's
2183 // coherence rules: PhantomPinned isn't enough to prove to Rust that a
2184 // blanket impl that requires Unpin doesn't apply. See http://<internal link>=h.f6jp8ifzgt3n
Devin Jeanpierre781eeb12022-09-06 19:23:25 -07002185 features.insert(make_rs_ident("negative_impls"));
Devin Jeanpierre215ee8e2022-05-16 16:09:41 -07002186 if should_implement_drop(record) {
Devin Jeanpierrecd5ebf32022-07-08 05:31:55 -07002187 quote! {#[::ctor::recursively_pinned(PinnedDrop)]}
Devin Jeanpierre215ee8e2022-05-16 16:09:41 -07002188 } else {
Devin Jeanpierrecd5ebf32022-07-08 05:31:55 -07002189 quote! {#[::ctor::recursively_pinned]}
Lukasz Anforowiczdd9ae0f2022-02-17 15:52:53 +00002190 }
2191 };
Devin Jeanpierre273eeae2021-10-06 13:29:35 +00002192
Devin Jeanpierrec80e6242022-02-03 01:56:40 +00002193 let mut repr_attributes = vec![quote! {C}];
2194 if record.override_alignment && record.alignment > 1 {
2195 let alignment = Literal::usize_unsuffixed(record.alignment);
2196 repr_attributes.push(quote! {align(#alignment)});
2197 }
2198
Devin Jeanpierre1221c2a2022-05-05 22:36:22 -07002199 // Adjust the struct to also include base class subobjects, vtables, etc.
2200 let head_padding = if let Some(first_field) = record.fields.first() {
2201 first_field.offset / 8
2202 } else {
Kinuko Yasuda54b75d72022-08-18 10:09:54 -07002203 record.size
Devin Jeanpierre1221c2a2022-05-05 22:36:22 -07002204 };
Devin Jeanpierrea2be2a22022-05-18 18:59:05 -07002205 // Prevent direct initialization for non-aggregate structs.
2206 //
2207 // Technically, any implicit-lifetime type is going to be fine to initialize
2208 // using direct initialization of the fields, even if it is not an aggregate,
2209 // because this is "just" setting memory to the appropriate values, and
2210 // implicit-lifetime types can automatically begin their lifetime without
2211 // running a constructor at all.
2212 //
2213 // However, not all types used in interop are implicit-lifetime. For example,
2214 // while any `Unpin` C++ value is, some `!Unpin` structs (e.g. `std::list`)
2215 // will not be. So for consistency, we apply the same rule for both
2216 // implicit-lifetime and non-implicit-lifetime types: the C++ rule, that the
2217 // type must be an *aggregate* type.
2218 //
2219 // TODO(b/232969667): Protect unions from direct initialization, too.
Lukasz Anforowiczd4742ff2022-07-11 17:05:02 -07002220 let allow_direct_init = record.is_aggregate || record.is_union();
Devin Jeanpierrea2be2a22022-05-18 18:59:05 -07002221 let head_padding = if head_padding > 0 || !allow_direct_init {
Devin Jeanpierre1221c2a2022-05-05 22:36:22 -07002222 let n = proc_macro2::Literal::usize_unsuffixed(head_padding);
Devin Jeanpierrec80e6242022-02-03 01:56:40 +00002223 quote! {
Devin Jeanpierrecd5ebf32022-07-08 05:31:55 -07002224 __non_field_data: [::std::mem::MaybeUninit<u8>; #n],
Devin Jeanpierrec80e6242022-02-03 01:56:40 +00002225 }
2226 } else {
2227 quote! {}
2228 };
2229
Devin Jeanpierre27450132022-04-11 13:52:01 -07002230 // TODO(b/227442773): After namespace support is added, use the fully-namespaced
2231 // name.
2232 let incomplete_symbol = &record.cc_name;
2233 let incomplete_definition = quote! {
Rosica Dejanovska8283fe62022-05-09 10:04:28 -07002234 forward_declare::unsafe_define!(forward_declare::symbol!(#incomplete_symbol), #qualified_ident);
Devin Jeanpierre27450132022-04-11 13:52:01 -07002235 };
2236
Devin Jeanpierre409f6f62022-07-07 02:00:26 -07002237 let no_unique_address_accessors = cc_struct_no_unique_address_impl(db, record)?;
Devin Jeanpierre8763a8e2022-04-26 15:45:13 -07002238 let mut record_generated_items = record
Rosica Dejanovskab2bd59e2022-04-11 09:02:03 -07002239 .child_item_ids
2240 .iter()
2241 .map(|id| {
Devin Jeanpierre781eeb12022-09-06 19:23:25 -07002242 let item = ir.find_decl(*id).with_context(|| {
2243 format!("Failed to look up `record.child_item_ids` for {:?}", record)
2244 })?;
Michael VanBemmelf5cbdf42022-10-14 17:00:11 -07002245 generate_item(db, item, errors)
Rosica Dejanovskab2bd59e2022-04-11 09:02:03 -07002246 })
2247 .collect::<Result<Vec<_>>>()?;
2248
Devin Jeanpierre52a14c32022-06-29 19:12:11 -07002249 record_generated_items.push(cc_struct_upcast_impl(record, &ir)?);
Devin Jeanpierre8763a8e2022-04-26 15:45:13 -07002250
Rosica Dejanovskab2bd59e2022-04-11 09:02:03 -07002251 let mut items = vec![];
2252 let mut thunks_from_record_items = vec![];
Devin Jeanpierre4f06f832022-04-26 15:51:30 -07002253 let mut thunk_impls_from_record_items = vec![];
Rosica Dejanovskab2bd59e2022-04-11 09:02:03 -07002254 let mut assertions_from_record_items = vec![];
2255
Devin Jeanpierre4f06f832022-04-26 15:51:30 -07002256 for generated in record_generated_items {
2257 items.push(generated.item);
Rosica Dejanovskab2bd59e2022-04-11 09:02:03 -07002258 if !generated.thunks.is_empty() {
Devin Jeanpierre4f06f832022-04-26 15:51:30 -07002259 thunks_from_record_items.push(generated.thunks);
Rosica Dejanovskab2bd59e2022-04-11 09:02:03 -07002260 }
2261 if !generated.assertions.is_empty() {
Devin Jeanpierre4f06f832022-04-26 15:51:30 -07002262 assertions_from_record_items.push(generated.assertions);
2263 }
2264 if !generated.thunk_impls.is_empty() {
2265 thunk_impls_from_record_items.push(generated.thunk_impls);
Rosica Dejanovskab2bd59e2022-04-11 09:02:03 -07002266 }
Devin Jeanpierre781eeb12022-09-06 19:23:25 -07002267 features.extend(generated.features.clone());
Rosica Dejanovskab2bd59e2022-04-11 09:02:03 -07002268 }
2269
Michael Forsterdb8101a2021-10-08 06:56:03 +00002270 let record_tokens = quote! {
Michael Forster028800b2021-10-05 12:39:59 +00002271 #doc_comment
Devin Jeanpierre9227d2c2021-10-06 12:26:05 +00002272 #derives
Devin Jeanpierre215ee8e2022-05-16 16:09:41 -07002273 #recursively_pinned_attribute
Devin Jeanpierrec80e6242022-02-03 01:56:40 +00002274 #[repr(#( #repr_attributes ),*)]
Teddy Katzd2cd1422022-04-04 09:41:33 -07002275 pub #record_kind #ident {
Devin Jeanpierre1221c2a2022-05-05 22:36:22 -07002276 #head_padding
Lukasz Anforowicz0dbcf0e2022-05-17 06:46:24 -07002277 #( #field_definitions, )*
Marcel Hlopkob4b28742021-09-15 12:45:20 +00002278 }
Googlerec648ff2021-09-23 07:19:53 +00002279
Devin Jeanpierre27450132022-04-11 13:52:01 -07002280 #incomplete_definition
2281
Devin Jeanpierre58181ac2022-02-14 21:30:05 +00002282 #no_unique_address_accessors
2283
Rosica Dejanovskab2bd59e2022-04-11 09:02:03 -07002284 __NEWLINE__ __NEWLINE__
2285 #( #items __NEWLINE__ __NEWLINE__)*
Devin Jeanpierre273eeae2021-10-06 13:29:35 +00002286 };
2287
Lukasz Anforowicz95938f82022-03-24 13:51:54 +00002288 let record_trait_assertions = {
Devin Jeanpierre52a14c32022-06-29 19:12:11 -07002289 let record_type_name = RsTypeKind::new_record(record.clone(), &ir)?.to_token_stream();
Lukasz Anforowicz95938f82022-03-24 13:51:54 +00002290 let mut assertions: Vec<TokenStream> = vec![];
2291 let mut add_assertion = |assert_impl_macro: TokenStream, trait_name: TokenStream| {
2292 assertions.push(quote! {
Lukasz Anforowicz768bba32022-04-11 14:06:13 -07002293 const _: () = { static_assertions::#assert_impl_macro (#record_type_name: #trait_name); };
Lukasz Anforowicz95938f82022-03-24 13:51:54 +00002294 });
2295 };
2296 if should_derive_clone(record) {
2297 add_assertion(quote! { assert_impl_all! }, quote! { Clone });
2298 } else {
Lukasz Anforowiczb4d17782022-07-07 08:09:13 -07002299 // Can't `assert_not_impl_any!` here, because `Clone` may be
Lukasz Anforowicz95938f82022-03-24 13:51:54 +00002300 // implemented rather than derived.
2301 }
2302 let mut add_conditional_assertion = |should_impl_trait: bool, trait_name: TokenStream| {
2303 let assert_impl_macro = if should_impl_trait {
2304 quote! { assert_impl_all! }
2305 } else {
Lukasz Anforowiczb4d17782022-07-07 08:09:13 -07002306 quote! { assert_not_impl_any! }
Lukasz Anforowicz95938f82022-03-24 13:51:54 +00002307 };
2308 add_assertion(assert_impl_macro, trait_name);
2309 };
2310 add_conditional_assertion(should_derive_copy(record), quote! { Copy });
2311 add_conditional_assertion(should_implement_drop(record), quote! { Drop });
2312 assertions
2313 };
Michael Forsterdb8101a2021-10-08 06:56:03 +00002314 let assertion_tokens = quote! {
Devin Jeanpierrecd5ebf32022-07-08 05:31:55 -07002315 const _: () = assert!(::std::mem::size_of::<#qualified_ident>() == #size);
2316 const _: () = assert!(::std::mem::align_of::<#qualified_ident>() == #alignment);
Lukasz Anforowicz95938f82022-03-24 13:51:54 +00002317 #( #record_trait_assertions )*
Lukasz Anforowiczfed64e62022-03-22 22:39:04 +00002318 #( #field_offset_assertions )*
2319 #( #field_copy_trait_assertions )*
Rosica Dejanovskab2bd59e2022-04-11 09:02:03 -07002320 #( #assertions_from_record_items )*
Michael Forsterdb8101a2021-10-08 06:56:03 +00002321 };
2322
Rosica Dejanovskab2bd59e2022-04-11 09:02:03 -07002323 let thunk_tokens = quote! {
2324 #( #thunks_from_record_items )*
2325 };
2326
2327 Ok(GeneratedItem {
2328 item: record_tokens,
Devin Jeanpierre781eeb12022-09-06 19:23:25 -07002329 features,
Rosica Dejanovskab2bd59e2022-04-11 09:02:03 -07002330 assertions: assertion_tokens,
2331 thunks: thunk_tokens,
Devin Jeanpierre4f06f832022-04-26 15:51:30 -07002332 thunk_impls: quote! {#(#thunk_impls_from_record_items __NEWLINE__ __NEWLINE__)*},
Rosica Dejanovskab2bd59e2022-04-11 09:02:03 -07002333 has_record: true,
2334 })
Marcel Hlopkob4b28742021-09-15 12:45:20 +00002335}
2336
Devin Jeanpierrea99e0a32022-08-17 01:36:58 -07002337fn check_by_value(record: &Record) -> Result<()> {
2338 if record.destructor == SpecialMemberFunc::Unavailable {
2339 bail!(
2340 "Can't directly construct values of type `{}` as it has a non-public or deleted destructor",
2341 record.cc_name
2342 )
2343 }
Devin Jeanpierreccb67672022-08-17 10:05:47 -07002344 if record.is_abstract {
2345 bail!("Can't directly construct values of type `{}`: it is abstract", record.cc_name);
2346 }
Devin Jeanpierrea99e0a32022-08-17 01:36:58 -07002347 Ok(())
2348}
2349
Lukasz Anforowicz2e41bb62022-01-11 18:23:07 +00002350fn should_derive_clone(record: &Record) -> bool {
Lukasz Anforowiczd4742ff2022-07-11 17:05:02 -07002351 if record.is_union() {
Lukasz Anforowicz2469a5e2022-06-02 09:44:51 -07002352 // `union`s (unlike `struct`s) should only derive `Clone` if they are `Copy`.
2353 should_derive_copy(record)
2354 } else {
Devin Jeanpierrea99e0a32022-08-17 01:36:58 -07002355 record.is_unpin()
2356 && record.copy_constructor == SpecialMemberFunc::Trivial
2357 && check_by_value(record).is_ok()
Lukasz Anforowicz2469a5e2022-06-02 09:44:51 -07002358 }
Lukasz Anforowicz2e41bb62022-01-11 18:23:07 +00002359}
2360
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00002361fn should_derive_copy(record: &Record) -> bool {
2362 // TODO(b/202258760): Make `Copy` inclusion configurable.
Lukasz Anforowicz2469a5e2022-06-02 09:44:51 -07002363 record.is_unpin()
Lukasz Anforowiczff7df4a2022-06-02 14:27:45 -07002364 && record.copy_constructor == SpecialMemberFunc::Trivial
2365 && record.destructor == ir::SpecialMemberFunc::Trivial
Devin Jeanpierrea99e0a32022-08-17 01:36:58 -07002366 && check_by_value(record).is_ok()
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00002367}
2368
2369fn generate_derives(record: &Record) -> Vec<Ident> {
2370 let mut derives = vec![];
Lukasz Anforowicz2e41bb62022-01-11 18:23:07 +00002371 if should_derive_clone(record) {
Marcel Hlopkoeaae9b72022-01-21 15:54:11 +00002372 derives.push(make_rs_ident("Clone"));
Devin Jeanpierre2ed14ec2021-10-06 11:32:19 +00002373 }
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00002374 if should_derive_copy(record) {
Marcel Hlopkoeaae9b72022-01-21 15:54:11 +00002375 derives.push(make_rs_ident("Copy"));
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00002376 }
2377 derives
Devin Jeanpierre2ed14ec2021-10-06 11:32:19 +00002378}
2379
Devin Jeanpierre45b01962022-07-07 06:12:11 -07002380fn generate_enum(db: &Database, enum_: &Enum) -> Result<TokenStream> {
Teddy Katz76fa42b2022-02-23 01:22:56 +00002381 let name = make_rs_ident(&enum_.identifier.identifier);
Devin Jeanpierre409f6f62022-07-07 02:00:26 -07002382 let underlying_type = db.rs_type_kind(enum_.underlying_type.rs_type.clone())?;
Teddy Katz76fa42b2022-02-23 01:22:56 +00002383 let enumerator_names =
2384 enum_.enumerators.iter().map(|enumerator| make_rs_ident(&enumerator.identifier.identifier));
2385 let enumerator_values = enum_.enumerators.iter().map(|enumerator| enumerator.value);
2386 Ok(quote! {
2387 #[repr(transparent)]
2388 #[derive(Debug, PartialEq, Eq, Copy, Clone, Hash, PartialOrd, Ord)]
2389 pub struct #name(#underlying_type);
2390 impl #name {
2391 #(pub const #enumerator_names: #name = #name(#enumerator_values);)*
2392 }
2393 impl From<#underlying_type> for #name {
2394 fn from(value: #underlying_type) -> #name {
Marcel Hlopko872b41a2022-05-10 01:49:33 -07002395 #name(value)
Teddy Katz76fa42b2022-02-23 01:22:56 +00002396 }
2397 }
2398 impl From<#name> for #underlying_type {
2399 fn from(value: #name) -> #underlying_type {
Marcel Hlopko872b41a2022-05-10 01:49:33 -07002400 value.0
Teddy Katz76fa42b2022-02-23 01:22:56 +00002401 }
2402 }
2403 })
2404}
2405
Devin Jeanpierre45b01962022-07-07 06:12:11 -07002406fn generate_type_alias(db: &Database, type_alias: &TypeAlias) -> Result<TokenStream> {
Marcel Hlopkoeaae9b72022-01-21 15:54:11 +00002407 let ident = make_rs_ident(&type_alias.identifier.identifier);
Lukasz Anforowiczc503af42022-03-04 23:18:15 +00002408 let doc_comment = generate_doc_comment(&type_alias.doc_comment);
Devin Jeanpierre409f6f62022-07-07 02:00:26 -07002409 let underlying_type = db
2410 .rs_type_kind(type_alias.underlying_type.rs_type.clone())
Googler6a0a5252022-01-11 14:08:09 +00002411 .with_context(|| format!("Failed to format underlying type for {:?}", type_alias))?;
Lukasz Anforowiczc503af42022-03-04 23:18:15 +00002412 Ok(quote! {
2413 #doc_comment
2414 pub type #ident = #underlying_type;
2415 })
Googler6a0a5252022-01-11 14:08:09 +00002416}
2417
Michael Forster523dbd42021-10-12 11:05:44 +00002418/// Generates Rust source code for a given `UnsupportedItem`.
Michael VanBemmelf5cbdf42022-10-14 17:00:11 -07002419fn generate_unsupported(
2420 item: &UnsupportedItem,
2421 errors: &mut dyn ErrorReporting,
2422) -> Result<TokenStream> {
2423 errors.insert(item.cause());
2424
Googler48a74dd2021-10-25 07:31:53 +00002425 let location = if item.source_loc.filename.is_empty() {
2426 "<unknown location>".to_string()
2427 } else {
2428 // TODO(forster): The "google3" prefix should probably come from a command line
2429 // argument.
2430 // TODO(forster): Consider linking to the symbol instead of to the line number
2431 // to avoid wrong links while generated files have not caught up.
2432 format!("google3/{};l={}", &item.source_loc.filename, &item.source_loc.line)
2433 };
Michael Forster6a184ad2021-10-12 13:04:05 +00002434 let message = format!(
Googler48a74dd2021-10-25 07:31:53 +00002435 "{}\nError while generating bindings for item '{}':\n{}",
Michael VanBemmelf5cbdf42022-10-14 17:00:11 -07002436 &location,
2437 &item.name,
2438 item.message()
Michael Forster6a184ad2021-10-12 13:04:05 +00002439 );
Michael Forster523dbd42021-10-12 11:05:44 +00002440 Ok(quote! { __COMMENT__ #message })
2441}
2442
Michael Forsterf1dce422021-10-13 09:50:16 +00002443/// Generates Rust source code for a given `Comment`.
2444fn generate_comment(comment: &Comment) -> Result<TokenStream> {
2445 let text = &comment.text;
2446 Ok(quote! { __COMMENT__ #text })
2447}
2448
Michael VanBemmelf5cbdf42022-10-14 17:00:11 -07002449fn generate_namespace(
2450 db: &Database,
2451 namespace: &Namespace,
2452 errors: &mut dyn ErrorReporting,
2453) -> Result<GeneratedItem> {
Devin Jeanpierre52a14c32022-06-29 19:12:11 -07002454 let ir = db.ir();
Rosica Dejanovskadd9a9032022-04-12 07:34:41 -07002455 let mut items = vec![];
2456 let mut thunks = vec![];
Lukasz Anforowicz90bb7462022-09-01 08:13:28 -07002457 let mut thunk_impls = vec![];
Rosica Dejanovskadd9a9032022-04-12 07:34:41 -07002458 let mut assertions = vec![];
2459 let mut has_record = false;
2460 let mut features = BTreeSet::new();
2461
2462 for item_id in namespace.child_item_ids.iter() {
Devin Jeanpierre781eeb12022-09-06 19:23:25 -07002463 let item = ir.find_decl(*item_id).with_context(|| {
2464 format!("Failed to look up namespace.child_item_ids for {:?}", namespace)
2465 })?;
Michael VanBemmelf5cbdf42022-10-14 17:00:11 -07002466 let generated = generate_item(db, item, errors)?;
Rosica Dejanovskadd9a9032022-04-12 07:34:41 -07002467 items.push(generated.item);
2468 if !generated.thunks.is_empty() {
2469 thunks.push(generated.thunks);
2470 }
Lukasz Anforowicz90bb7462022-09-01 08:13:28 -07002471 if !generated.thunk_impls.is_empty() {
2472 thunk_impls.push(generated.thunk_impls);
2473 }
Rosica Dejanovskadd9a9032022-04-12 07:34:41 -07002474 if !generated.assertions.is_empty() {
2475 assertions.push(generated.assertions);
2476 }
2477 features.extend(generated.features);
2478 has_record = has_record || generated.has_record;
2479 }
2480
Rosica Dejanovska93aeafb2022-06-01 07:05:31 -07002481 let reopened_namespace_idx = ir.get_reopened_namespace_idx(namespace.id)?;
Devin Jeanpierrefe6aaea2022-09-09 12:33:50 -07002482 // True if this is actually the module with the name `#name`, rather than e.g.
2483 // `#name_0`, `#name_1`, etc.
2484 let is_canonical_namespace_module =
Rosica Dejanovska93aeafb2022-06-01 07:05:31 -07002485 ir.is_last_reopened_namespace(namespace.id, namespace.canonical_namespace_id)?;
2486
Devin Jeanpierrefe6aaea2022-09-09 12:33:50 -07002487 let name = if is_canonical_namespace_module {
Rosica Dejanovska93aeafb2022-06-01 07:05:31 -07002488 make_rs_ident(&namespace.name.identifier)
2489 } else {
2490 make_rs_ident(&format!("{}_{}", &namespace.name.identifier, reopened_namespace_idx))
2491 };
2492
2493 let use_stmt_for_previous_namespace = if reopened_namespace_idx == 0 {
2494 quote! {}
2495 } else {
2496 let previous_namespace_ident = make_rs_ident(&format!(
2497 "{}_{}",
2498 &namespace.name.identifier,
2499 reopened_namespace_idx - 1
2500 ));
2501 quote! { pub use super::#previous_namespace_ident::*; __NEWLINE__ __NEWLINE__ }
2502 };
Rosica Dejanovskadd9a9032022-04-12 07:34:41 -07002503
Devin Jeanpierrefe6aaea2022-09-09 12:33:50 -07002504 let use_stmt_for_inline_namespace = if namespace.is_inline && is_canonical_namespace_module {
2505 quote! {pub use #name::*; __NEWLINE__}
2506 } else {
2507 quote! {}
2508 };
2509
Rosica Dejanovskadd9a9032022-04-12 07:34:41 -07002510 let namespace_tokens = quote! {
2511 pub mod #name {
Rosica Dejanovska93aeafb2022-06-01 07:05:31 -07002512 #use_stmt_for_previous_namespace
2513
Rosica Dejanovskadd9a9032022-04-12 07:34:41 -07002514 #( #items __NEWLINE__ __NEWLINE__ )*
Rosica Dejanovskadd9a9032022-04-12 07:34:41 -07002515 }
Devin Jeanpierrefe6aaea2022-09-09 12:33:50 -07002516 __NEWLINE__
2517 #use_stmt_for_inline_namespace
Rosica Dejanovskadd9a9032022-04-12 07:34:41 -07002518 };
2519
2520 Ok(GeneratedItem {
2521 item: namespace_tokens,
2522 features: features,
2523 has_record: has_record,
Lukasz Anforowicz90bb7462022-09-01 08:13:28 -07002524 thunks: quote! { #( #thunks )* },
2525 thunk_impls: quote! { #( #thunk_impls )* },
2526 assertions: quote! { #( #assertions )* },
Rosica Dejanovskadd9a9032022-04-12 07:34:41 -07002527 ..Default::default()
2528 })
2529}
2530
Rosica Dejanovska8da8f792022-03-29 02:02:22 -07002531#[derive(Clone, Debug, Default)]
2532struct GeneratedItem {
2533 item: TokenStream,
2534 thunks: TokenStream,
Devin Jeanpierre4f06f832022-04-26 15:51:30 -07002535 // C++ source code for helper functions.
2536 thunk_impls: TokenStream,
Rosica Dejanovska8da8f792022-03-29 02:02:22 -07002537 assertions: TokenStream,
2538 features: BTreeSet<Ident>,
2539 has_record: bool,
2540}
2541
Michael VanBemmelf5cbdf42022-10-14 17:00:11 -07002542fn generate_item(
2543 db: &Database,
2544 item: &Item,
2545 errors: &mut dyn ErrorReporting,
2546) -> Result<GeneratedItem> {
Devin Jeanpierre52a14c32022-06-29 19:12:11 -07002547 let ir = db.ir();
Devin Jeanpierreab85d442022-06-29 19:16:41 -07002548 let overloaded_funcs = db.overloaded_funcs();
Rosica Dejanovska8da8f792022-03-29 02:02:22 -07002549 let generated_item = match item {
Devin Jeanpierre52a14c32022-06-29 19:12:11 -07002550 Item::Func(func) => match db.generate_func(func.clone()) {
Devin Jeanpierred7b48102022-03-31 04:15:03 -07002551 Err(e) => GeneratedItem {
Michael VanBemmelf5cbdf42022-10-14 17:00:11 -07002552 item: generate_unsupported(
2553 &make_unsupported_fn(func, &ir, format!("{e}"))?,
2554 errors,
2555 )?,
Devin Jeanpierred7b48102022-03-31 04:15:03 -07002556 ..Default::default()
2557 },
2558 Ok(None) => GeneratedItem::default(),
Devin Jeanpierre52a14c32022-06-29 19:12:11 -07002559 Ok(Some(f)) => {
Devin Jeanpierrece549202022-07-25 08:54:34 -07002560 let (api_func, thunk, function_id) = &*f;
Devin Jeanpierre52a14c32022-06-29 19:12:11 -07002561 if overloaded_funcs.contains(function_id) {
Rosica Dejanovska8da8f792022-03-29 02:02:22 -07002562 GeneratedItem {
Michael VanBemmelf5cbdf42022-10-14 17:00:11 -07002563 item: generate_unsupported(
2564 &make_unsupported_fn(
2565 func,
2566 &ir,
2567 "Cannot generate bindings for overloaded function",
2568 )?,
2569 errors,
2570 )?,
Rosica Dejanovska8da8f792022-03-29 02:02:22 -07002571 ..Default::default()
2572 }
2573 } else {
Devin Jeanpierre52a14c32022-06-29 19:12:11 -07002574 // TODO(b/236687702): Use Rc for these, or else split this into a non-query
2575 // and only use the query for Function IDs.
Rosica Dejanovska8da8f792022-03-29 02:02:22 -07002576 GeneratedItem {
Devin Jeanpierre52a14c32022-06-29 19:12:11 -07002577 item: api_func.tokens.clone(),
2578 thunks: thunk.tokens.clone(),
Rosica Dejanovska8da8f792022-03-29 02:02:22 -07002579 features: api_func.features.union(&thunk.features).cloned().collect(),
2580 ..Default::default()
2581 }
2582 }
2583 }
2584 },
Devin Jeanpierrec0543eb2022-04-20 16:00:34 -07002585 Item::IncompleteRecord(incomplete_record) => {
2586 if !ir.is_current_target(&incomplete_record.owning_target)
2587 && !ir.is_stdlib_target(&incomplete_record.owning_target)
2588 {
Devin Jeanpierre090a9872022-04-26 15:19:46 -07002589 GeneratedItem::default()
Devin Jeanpierrec0543eb2022-04-20 16:00:34 -07002590 } else {
2591 GeneratedItem {
2592 item: generate_incomplete_record(incomplete_record)?,
2593 ..Default::default()
2594 }
2595 }
2596 }
Rosica Dejanovska8da8f792022-03-29 02:02:22 -07002597 Item::Record(record) => {
2598 if !ir.is_current_target(&record.owning_target)
2599 && !ir.is_stdlib_target(&record.owning_target)
2600 {
Devin Jeanpierre090a9872022-04-26 15:19:46 -07002601 GeneratedItem::default()
Rosica Dejanovska8da8f792022-03-29 02:02:22 -07002602 } else {
Michael VanBemmelf5cbdf42022-10-14 17:00:11 -07002603 generate_record(db, record, errors)?
Rosica Dejanovska8da8f792022-03-29 02:02:22 -07002604 }
2605 }
2606 Item::Enum(enum_) => {
2607 if !ir.is_current_target(&enum_.owning_target)
2608 && !ir.is_stdlib_target(&enum_.owning_target)
2609 {
Devin Jeanpierre090a9872022-04-26 15:19:46 -07002610 GeneratedItem::default()
Rosica Dejanovska8da8f792022-03-29 02:02:22 -07002611 } else {
Devin Jeanpierre409f6f62022-07-07 02:00:26 -07002612 GeneratedItem { item: generate_enum(db, enum_)?, ..Default::default() }
Rosica Dejanovska8da8f792022-03-29 02:02:22 -07002613 }
2614 }
2615 Item::TypeAlias(type_alias) => {
2616 if !ir.is_current_target(&type_alias.owning_target)
2617 && !ir.is_stdlib_target(&type_alias.owning_target)
2618 {
Devin Jeanpierre090a9872022-04-26 15:19:46 -07002619 GeneratedItem::default()
Devin Jeanpierre5fecde52022-09-14 06:53:39 -07002620 } else if type_alias.enclosing_record_id.is_some() {
2621 // TODO(b/200067824): support nested type aliases.
2622 GeneratedItem {
Michael VanBemmelf5cbdf42022-10-14 17:00:11 -07002623 item: generate_unsupported(
2624 &make_unsupported_nested_type_alias(type_alias)?,
2625 errors,
2626 )?,
Devin Jeanpierre5fecde52022-09-14 06:53:39 -07002627 ..Default::default()
2628 }
Rosica Dejanovska8da8f792022-03-29 02:02:22 -07002629 } else {
Devin Jeanpierre409f6f62022-07-07 02:00:26 -07002630 GeneratedItem { item: generate_type_alias(db, type_alias)?, ..Default::default() }
Rosica Dejanovska8da8f792022-03-29 02:02:22 -07002631 }
2632 }
2633 Item::UnsupportedItem(unsupported) => {
Michael VanBemmelf5cbdf42022-10-14 17:00:11 -07002634 GeneratedItem { item: generate_unsupported(unsupported, errors)?, ..Default::default() }
Rosica Dejanovska8da8f792022-03-29 02:02:22 -07002635 }
2636 Item::Comment(comment) => {
2637 GeneratedItem { item: generate_comment(comment)?, ..Default::default() }
2638 }
Michael VanBemmelf5cbdf42022-10-14 17:00:11 -07002639 Item::Namespace(namespace) => generate_namespace(db, namespace, errors)?,
Devin Jeanpierre96bf0bd2022-10-04 20:32:15 -07002640 Item::UseMod(use_mod) => {
Devin Jeanpierred5a53622022-10-04 22:00:02 -07002641 let UseMod { path, mod_name, .. } = &**use_mod;
Devin Jeanpierre96bf0bd2022-10-04 20:32:15 -07002642 let mod_name = make_rs_ident(&mod_name.identifier);
Devin Jeanpierred5a53622022-10-04 22:00:02 -07002643 GeneratedItem {
Devin Jeanpierre96bf0bd2022-10-04 20:32:15 -07002644 item: quote! {
2645 #[path = #path]
2646 mod #mod_name;
2647 pub use #mod_name::*;
2648 },
2649 ..Default::default()
2650 }
2651 }
Rosica Dejanovska8da8f792022-03-29 02:02:22 -07002652 };
2653
2654 Ok(generated_item)
2655}
2656
Devin Jeanpierreab85d442022-06-29 19:16:41 -07002657/// Identifies all functions having overloads that we can't import (yet).
2658///
2659/// TODO(b/213280424): Implement support for overloaded functions.
2660fn overloaded_funcs(db: &dyn BindingsGenerator) -> Rc<HashSet<Rc<FunctionId>>> {
2661 let mut seen_funcs = HashSet::new();
2662 let mut overloaded_funcs = HashSet::new();
2663 for func in db.ir().functions() {
2664 if let Ok(Some(f)) = db.generate_func(func.clone()) {
Devin Jeanpierrece549202022-07-25 08:54:34 -07002665 let (.., function_id) = &*f;
Devin Jeanpierreab85d442022-06-29 19:16:41 -07002666 if !seen_funcs.insert(function_id.clone()) {
2667 overloaded_funcs.insert(function_id.clone());
2668 }
2669 }
2670 }
2671 Rc::new(overloaded_funcs)
2672}
2673
Devin Jeanpierre4f06f832022-04-26 15:51:30 -07002674// Returns the Rust code implementing bindings, plus any auxiliary C++ code
2675// needed to support it.
Michael VanBemmelf5cbdf42022-10-14 17:00:11 -07002676fn generate_bindings_tokens(
2677 ir: Rc<IR>,
2678 crubit_support_path: &str,
2679 errors: &mut dyn ErrorReporting,
2680) -> Result<BindingsTokens> {
Devin Jeanpierre52a14c32022-06-29 19:12:11 -07002681 let mut db = Database::default();
2682 db.set_ir(ir.clone());
2683
Michael Forstered642022021-10-04 09:48:25 +00002684 let mut items = vec![];
Marcel Hlopko42abfc82021-08-09 07:03:17 +00002685 let mut thunks = vec![];
Devin Jeanpierreb8ce2c12022-07-13 10:34:01 -07002686 let mut thunk_impls = vec![generate_rs_api_impl(&mut db, crubit_support_path)?];
Michael Forsterdb8101a2021-10-08 06:56:03 +00002687 let mut assertions = vec![];
Marcel Hlopko42abfc82021-08-09 07:03:17 +00002688
Googler454f2652021-12-06 12:53:12 +00002689 // We import nullable pointers as an Option<&T> and assume that at the ABI
2690 // level, None is represented as a zero pointer value whereas Some is
2691 // represented as as non-zero pointer value. This seems like a pretty safe
2692 // assumption to make, but to provide some safeguard, assert that
2693 // `Option<&i32>` and `&i32` have the same size.
2694 assertions.push(quote! {
Devin Jeanpierrecd5ebf32022-07-08 05:31:55 -07002695 const _: () = assert!(::std::mem::size_of::<Option<&i32>>() == ::std::mem::size_of::<&i32>());
Googler454f2652021-12-06 12:53:12 +00002696 });
2697
Michael Forsterbee84482021-10-13 08:35:38 +00002698 // TODO(jeanpierreda): Delete has_record, either in favor of using RsSnippet, or not
2699 // having uses. See https://chat.google.com/room/AAAAnQmj8Qs/6QbkSvWcfhA
Devin Jeanpierreea700d32021-10-06 11:33:56 +00002700 let mut has_record = false;
Devin Jeanpierre273eeae2021-10-06 13:29:35 +00002701 let mut features = BTreeSet::new();
Marcel Hlopko42abfc82021-08-09 07:03:17 +00002702
Rosica Dejanovskab2bd59e2022-04-11 09:02:03 -07002703 for top_level_item_id in ir.top_level_item_ids() {
Devin Jeanpierre781eeb12022-09-06 19:23:25 -07002704 let item =
2705 ir.find_decl(*top_level_item_id).context("Failed to look up ir.top_level_item_ids")?;
Michael VanBemmelf5cbdf42022-10-14 17:00:11 -07002706 let generated = generate_item(&db, item, errors)?;
Rosica Dejanovska8da8f792022-03-29 02:02:22 -07002707 items.push(generated.item);
Devin Jeanpierreb5ba1402022-03-29 07:29:24 -07002708 if !generated.thunks.is_empty() {
Rosica Dejanovska8da8f792022-03-29 02:02:22 -07002709 thunks.push(generated.thunks);
Michael Forstered642022021-10-04 09:48:25 +00002710 }
Devin Jeanpierreb5ba1402022-03-29 07:29:24 -07002711 if !generated.assertions.is_empty() {
Rosica Dejanovska8da8f792022-03-29 02:02:22 -07002712 assertions.push(generated.assertions);
2713 }
Devin Jeanpierre4f06f832022-04-26 15:51:30 -07002714 if !generated.thunk_impls.is_empty() {
2715 thunk_impls.push(generated.thunk_impls);
2716 }
Rosica Dejanovska8da8f792022-03-29 02:02:22 -07002717 features.extend(generated.features);
2718 has_record = has_record || generated.has_record;
Marcel Hlopko42abfc82021-08-09 07:03:17 +00002719 }
2720
Marcel Hlopkob4b28742021-09-15 12:45:20 +00002721 let mod_detail = if thunks.is_empty() {
2722 quote! {}
2723 } else {
2724 quote! {
2725 mod detail {
Googler55647142022-01-11 12:37:39 +00002726 #[allow(unused_imports)]
Devin Jeanpierred4dde0e2021-10-13 20:48:25 +00002727 use super::*;
Marcel Hlopkob4b28742021-09-15 12:45:20 +00002728 extern "C" {
2729 #( #thunks )*
2730 }
Marcel Hlopko42abfc82021-08-09 07:03:17 +00002731 }
2732 }
2733 };
2734
Devin Jeanpierre273eeae2021-10-06 13:29:35 +00002735 let features = if features.is_empty() {
2736 quote! {}
2737 } else {
2738 quote! {
Lukasz Anforowicz16889da2022-10-06 13:37:48 -07002739 #![feature( #(#features),* )] __NEWLINE__
2740 #![allow(stable_features)]
Devin Jeanpierre273eeae2021-10-06 13:29:35 +00002741 }
2742 };
2743
Devin Jeanpierre4f06f832022-04-26 15:51:30 -07002744 Ok(BindingsTokens {
2745 rs_api: quote! {
2746 #features __NEWLINE__
2747 #![allow(non_camel_case_types)] __NEWLINE__
Lukasz Anforowicz945919c2022-05-12 13:08:47 -07002748 #![allow(non_snake_case)] __NEWLINE__
Lukasz Anforowiczcc262632022-05-12 15:07:43 -07002749 #![allow(non_upper_case_globals)] __NEWLINE__
2750 #![deny(warnings)] __NEWLINE__ __NEWLINE__
Googler55647142022-01-11 12:37:39 +00002751
Devin Jeanpierre4f06f832022-04-26 15:51:30 -07002752 #( #items __NEWLINE__ __NEWLINE__ )*
Marcel Hlopkob4b28742021-09-15 12:45:20 +00002753
Devin Jeanpierre4f06f832022-04-26 15:51:30 -07002754 #mod_detail __NEWLINE__ __NEWLINE__
Michael Forsterdb8101a2021-10-08 06:56:03 +00002755
Devin Jeanpierre4f06f832022-04-26 15:51:30 -07002756 #( #assertions __NEWLINE__ __NEWLINE__ )*
2757 },
2758 rs_api_impl: quote! {#(#thunk_impls __NEWLINE__ __NEWLINE__ )*},
Marcel Hlopko89547752021-12-10 09:39:41 +00002759 })
Marcel Hlopko42abfc82021-08-09 07:03:17 +00002760}
2761
Marcel Hlopkoeaae9b72022-01-21 15:54:11 +00002762/// Makes an 'Ident' to be used in the Rust source code. Escapes Rust keywords.
2763fn make_rs_ident(ident: &str) -> Ident {
2764 // TODO(https://github.com/dtolnay/syn/pull/1098): Remove the hardcoded list once syn recognizes
2765 // 2018 and 2021 keywords.
2766 if ["async", "await", "try", "dyn"].contains(&ident) {
2767 return format_ident!("r#{}", ident);
2768 }
2769 match syn::parse_str::<syn::Ident>(ident) {
2770 Ok(_) => format_ident!("{}", ident),
2771 Err(_) => format_ident!("r#{}", ident),
2772 }
2773}
2774
Lukasz Anforowicz55616892022-10-06 09:16:57 -07002775/// Formats a C++ identifier. Panics if `ident` is a C++ reserved keyword.
2776fn format_cc_ident(ident: &str) -> TokenStream {
2777 code_gen_utils::format_cc_ident(ident).expect("IR should only contain valid C++ identifiers")
2778}
2779
Devin Jeanpierrefec5bfe2022-04-05 10:59:10 -07002780/// Returns Some(crate_ident) if this is an imported crate.
2781fn rs_imported_crate_name(owning_target: &BazelLabel, ir: &IR) -> Option<Ident> {
Googler6a0a5252022-01-11 14:08:09 +00002782 if ir.is_current_target(owning_target) || ir.is_stdlib_target(owning_target) {
Devin Jeanpierrefec5bfe2022-04-05 10:59:10 -07002783 None
Googler6a0a5252022-01-11 14:08:09 +00002784 } else {
Devin Jeanpierre084ebbd2022-04-01 04:32:46 -07002785 let owning_crate_name = owning_target.target_name();
Marcel Hlopkod906b892022-01-27 08:52:36 +00002786 // TODO(b/216587072): Remove this hacky escaping and use the import! macro once
2787 // available
Lukasz Anforowiczdd9ae0f2022-02-17 15:52:53 +00002788 let escaped_owning_crate_name = owning_crate_name.replace('-', "_");
Marcel Hlopkod906b892022-01-27 08:52:36 +00002789 let owning_crate = make_rs_ident(&escaped_owning_crate_name);
Devin Jeanpierrefec5bfe2022-04-05 10:59:10 -07002790 Some(owning_crate)
Googler6a0a5252022-01-11 14:08:09 +00002791 }
2792}
2793
Devin Jeanpierre103cbb52022-04-06 12:55:16 -07002794#[derive(Copy, Clone, Debug, Eq, PartialEq)]
Lukasz Anforowicz40c2eb82022-01-11 18:22:31 +00002795enum Mutability {
2796 Const,
2797 Mut,
2798}
2799
2800impl Mutability {
2801 fn format_for_pointer(&self) -> TokenStream {
2802 match self {
2803 Mutability::Mut => quote! {mut},
2804 Mutability::Const => quote! {const},
2805 }
2806 }
2807
2808 fn format_for_reference(&self) -> TokenStream {
2809 match self {
2810 Mutability::Mut => quote! {mut},
2811 Mutability::Const => quote! {},
2812 }
2813 }
2814}
2815
Devin Jeanpierre4e94a082022-08-10 01:58:35 -07002816/// Either a named lifetime, or the magic `'_` elided lifetime.
Devin Jeanpierre7ed8c6f2022-08-01 13:41:02 -07002817///
2818/// Warning: elided lifetimes are not always valid, and sometimes named
2819/// lifetimes are required. In particular, this should never be used for
2820/// output lifetimes.
2821///
2822/// However, because output lifetimes are never elided, a lifetime that only
2823/// occurs in a single input position can always be elided.
2824#[derive(Debug, PartialEq, Eq, Hash, Clone)]
Devin Jeanpierre4e94a082022-08-10 01:58:35 -07002825pub struct Lifetime(pub Rc<str>);
2826
2827impl From<&ir::LifetimeName> for Lifetime {
2828 fn from(lifetime_name: &ir::LifetimeName) -> Self {
2829 Lifetime(lifetime_name.name.clone())
2830 }
Devin Jeanpierre7ed8c6f2022-08-01 13:41:02 -07002831}
2832
2833impl Lifetime {
Devin Jeanpierre4e94a082022-08-10 01:58:35 -07002834 pub fn new(name: &str) -> Self {
2835 Lifetime(Rc::from(name))
2836 }
Devin Jeanpierre7ed8c6f2022-08-01 13:41:02 -07002837 /// Formats a lifetime for use as a reference lifetime parameter.
2838 ///
2839 /// In this case, elided lifetimes are empty.
2840 pub fn format_for_reference(&self) -> TokenStream {
Devin Jeanpierre4e94a082022-08-10 01:58:35 -07002841 match &*self.0 {
2842 "_" => quote! {},
2843 _ => quote! {#self},
Devin Jeanpierre7ed8c6f2022-08-01 13:41:02 -07002844 }
2845 }
2846}
2847
2848/// Formats a lifetime for use anywhere.
2849///
2850/// For the specific context of references, prefer `format_for_reference`, as it
2851/// gives a more idiomatic formatting for elided lifetimes.
2852impl ToTokens for Lifetime {
2853 fn to_tokens(&self, tokens: &mut TokenStream) {
Devin Jeanpierre4e94a082022-08-10 01:58:35 -07002854 let Self(name) = self;
2855 let lifetime = syn::Lifetime::new(&format!("'{name}"), proc_macro2::Span::call_site());
2856 lifetime.to_tokens(tokens);
Devin Jeanpierre7ed8c6f2022-08-01 13:41:02 -07002857 }
2858}
2859
Devin Jeanpierre409f6f62022-07-07 02:00:26 -07002860#[derive(Clone, Debug, PartialEq, Eq)]
Devin Jeanpierre8cd8ae72022-06-29 18:59:40 -07002861enum RsTypeKind {
Devin Jeanpierrefec5bfe2022-04-05 10:59:10 -07002862 Pointer {
Devin Jeanpierreb2b6cf82022-07-07 01:49:27 -07002863 pointee: Rc<RsTypeKind>,
Devin Jeanpierrefec5bfe2022-04-05 10:59:10 -07002864 mutability: Mutability,
2865 },
2866 Reference {
Devin Jeanpierreb2b6cf82022-07-07 01:49:27 -07002867 referent: Rc<RsTypeKind>,
Devin Jeanpierrefec5bfe2022-04-05 10:59:10 -07002868 mutability: Mutability,
Devin Jeanpierre7ed8c6f2022-08-01 13:41:02 -07002869 lifetime: Lifetime,
Devin Jeanpierrefec5bfe2022-04-05 10:59:10 -07002870 },
2871 RvalueReference {
Devin Jeanpierreb2b6cf82022-07-07 01:49:27 -07002872 referent: Rc<RsTypeKind>,
Devin Jeanpierrefec5bfe2022-04-05 10:59:10 -07002873 mutability: Mutability,
Devin Jeanpierre7ed8c6f2022-08-01 13:41:02 -07002874 lifetime: Lifetime,
Devin Jeanpierrefec5bfe2022-04-05 10:59:10 -07002875 },
2876 FuncPtr {
Devin Jeanpierre8cd8ae72022-06-29 18:59:40 -07002877 abi: Rc<str>,
Devin Jeanpierreb2b6cf82022-07-07 01:49:27 -07002878 return_type: Rc<RsTypeKind>,
2879 param_types: Rc<[RsTypeKind]>,
Devin Jeanpierrefec5bfe2022-04-05 10:59:10 -07002880 },
Devin Jeanpierrec0543eb2022-04-20 16:00:34 -07002881 /// An incomplete record type.
2882 IncompleteRecord {
Devin Jeanpierre8cd8ae72022-06-29 18:59:40 -07002883 incomplete_record: Rc<IncompleteRecord>,
Devin Jeanpierrec0543eb2022-04-20 16:00:34 -07002884
2885 /// The imported crate this comes from, or None if the current crate.
2886 crate_ident: Option<Ident>,
Rosica Dejanovska8283fe62022-05-09 10:04:28 -07002887 /// The namespace qualifier for this record.
Lukasz Anforowicz92be29f2022-09-02 17:12:29 -07002888 namespace_qualifier: Rc<NamespaceQualifier>,
Devin Jeanpierrec0543eb2022-04-20 16:00:34 -07002889 },
2890 /// A complete record type.
Devin Jeanpierrefec5bfe2022-04-05 10:59:10 -07002891 Record {
Devin Jeanpierre8cd8ae72022-06-29 18:59:40 -07002892 record: Rc<Record>,
Rosica Dejanovska8283fe62022-05-09 10:04:28 -07002893 /// The namespace qualifier for this record.
Lukasz Anforowicz92be29f2022-09-02 17:12:29 -07002894 namespace_qualifier: Rc<NamespaceQualifier>,
Devin Jeanpierrefec5bfe2022-04-05 10:59:10 -07002895 /// The imported crate this comes from, or None if the current crate.
2896 crate_ident: Option<Ident>,
2897 },
2898 TypeAlias {
Devin Jeanpierre8cd8ae72022-06-29 18:59:40 -07002899 type_alias: Rc<TypeAlias>,
Devin Jeanpierreb2b6cf82022-07-07 01:49:27 -07002900 underlying_type: Rc<RsTypeKind>,
Rosica Dejanovska8283fe62022-05-09 10:04:28 -07002901 /// The namespace qualifier for this alias.
Lukasz Anforowicz92be29f2022-09-02 17:12:29 -07002902 namespace_qualifier: Rc<NamespaceQualifier>,
Devin Jeanpierrefec5bfe2022-04-05 10:59:10 -07002903 /// The imported crate this comes from, or None if the current crate.
2904 crate_ident: Option<Ident>,
2905 },
Lukasz Anforowicz40c2eb82022-01-11 18:22:31 +00002906 Unit,
Devin Jeanpierrefec5bfe2022-04-05 10:59:10 -07002907 Other {
Devin Jeanpierre8cd8ae72022-06-29 18:59:40 -07002908 name: Rc<str>,
Devin Jeanpierreb2b6cf82022-07-07 01:49:27 -07002909 type_args: Rc<[RsTypeKind]>,
Devin Jeanpierrefec5bfe2022-04-05 10:59:10 -07002910 },
Lukasz Anforowicz40c2eb82022-01-11 18:22:31 +00002911}
2912
Devin Jeanpierre8cd8ae72022-06-29 18:59:40 -07002913impl RsTypeKind {
Devin Jeanpierre8cd8ae72022-06-29 18:59:40 -07002914 pub fn new_record(record: Rc<Record>, ir: &IR) -> Result<Self> {
Lukasz Anforowicz92be29f2022-09-02 17:12:29 -07002915 let namespace_qualifier = Rc::new(NamespaceQualifier::new(record.id, ir)?);
Devin Jeanpierre8cd8ae72022-06-29 18:59:40 -07002916 let crate_ident = rs_imported_crate_name(&record.owning_target, ir);
2917 Ok(RsTypeKind::Record { record, namespace_qualifier, crate_ident })
Devin Jeanpierrefec5bfe2022-04-05 10:59:10 -07002918 }
2919
Devin Jeanpierre149950d2022-02-22 21:02:02 +00002920 /// Returns true if the type is known to be `Unpin`, false otherwise.
Devin Jeanpierref85ae592022-04-01 04:33:57 -07002921 pub fn is_unpin(&self) -> bool {
Devin Jeanpierre149950d2022-02-22 21:02:02 +00002922 match self {
Devin Jeanpierrec0543eb2022-04-20 16:00:34 -07002923 RsTypeKind::IncompleteRecord { .. } => false,
Devin Jeanpierrefec5bfe2022-04-05 10:59:10 -07002924 RsTypeKind::Record { record, .. } => record.is_unpin(),
Devin Jeanpierref85ae592022-04-01 04:33:57 -07002925 RsTypeKind::TypeAlias { underlying_type, .. } => underlying_type.is_unpin(),
Devin Jeanpierre149950d2022-02-22 21:02:02 +00002926 _ => true,
2927 }
2928 }
2929
Devin Jeanpierre11ce2b92022-07-27 07:54:21 -07002930 /// Returns true if the type is known to be move-constructible, false
2931 /// otherwise.
2932 ///
2933 /// For the purposes of this method, references are considered
2934 /// move-constructible (as if they were pointers).
2935 pub fn is_move_constructible(&self) -> bool {
2936 match self {
2937 RsTypeKind::IncompleteRecord { .. } => false,
2938 RsTypeKind::Record { record, .. } => {
2939 record.move_constructor != ir::SpecialMemberFunc::Unavailable
2940 }
2941 RsTypeKind::TypeAlias { underlying_type, .. } => {
2942 underlying_type.is_move_constructible()
2943 }
2944 _ => true,
2945 }
2946 }
2947
Devin Jeanpierrea99e0a32022-08-17 01:36:58 -07002948 /// Returns Ok if the type can be used by value, or an error describing why
2949 /// it can't.
2950 pub fn check_by_value(&self) -> Result<()> {
2951 match self {
2952 RsTypeKind::Record { record, .. } => check_by_value(record),
2953 RsTypeKind::TypeAlias { underlying_type, .. } => underlying_type.check_by_value(),
2954 _ => Ok(()),
2955 }
2956 }
2957
Devin Jeanpierre82e8e362022-04-05 11:04:55 -07002958 pub fn format_as_return_type_fragment(&self) -> TokenStream {
Lukasz Anforowiczcf230fd2022-02-18 19:20:39 +00002959 match self {
Devin Jeanpierred2b7a8f2022-04-01 04:37:16 -07002960 RsTypeKind::Unit => quote! {},
Lukasz Anforowiczcf230fd2022-02-18 19:20:39 +00002961 other_type => {
Devin Jeanpierre92ca2612022-04-06 11:35:13 -07002962 quote! { -> #other_type }
Lukasz Anforowiczcf230fd2022-02-18 19:20:39 +00002963 }
2964 }
2965 }
2966
Lukasz Anforowiczcde4b1b2022-02-03 21:20:55 +00002967 /// Formats this RsTypeKind as `&'a mut MaybeUninit<SomeStruct>`. This is
2968 /// used to format `__this` parameter in a constructor thunk.
Devin Jeanpierre82e8e362022-04-05 11:04:55 -07002969 pub fn format_mut_ref_as_uninitialized(&self) -> Result<TokenStream> {
Lukasz Anforowiczcde4b1b2022-02-03 21:20:55 +00002970 match self {
Devin Jeanpierred2b7a8f2022-04-01 04:37:16 -07002971 RsTypeKind::Reference { referent, lifetime, mutability: Mutability::Mut } => {
Devin Jeanpierre7ed8c6f2022-08-01 13:41:02 -07002972 let lifetime = lifetime.format_for_reference();
Devin Jeanpierrecd5ebf32022-07-08 05:31:55 -07002973 Ok(quote! { & #lifetime mut ::std::mem::MaybeUninit< #referent > })
Lukasz Anforowicz40c2eb82022-01-11 18:22:31 +00002974 }
Devin Jeanpierre149950d2022-02-22 21:02:02 +00002975 _ => bail!("Expected reference to format as MaybeUninit, got: {:?}", self),
Lukasz Anforowiczcde4b1b2022-02-03 21:20:55 +00002976 }
Lukasz Anforowicz40c2eb82022-01-11 18:22:31 +00002977 }
2978
Devin Jeanpierre149950d2022-02-22 21:02:02 +00002979 /// Formats this RsTypeKind as the `self` parameter: usually, `&'a self` or
2980 /// `&'a mut self`.
2981 ///
2982 /// If this is !Unpin, however, it uses `self: Pin<&mut Self>` instead.
Devin Jeanpierre108e9c02022-06-02 07:10:09 -07002983 pub fn format_as_self_param(&self) -> Result<TokenStream> {
Devin Jeanpierre215ee8e2022-05-16 16:09:41 -07002984 let referent;
2985 let mutability;
2986 let lifetime;
2987 match self {
Devin Jeanpierre108e9c02022-06-02 07:10:09 -07002988 RsTypeKind::Pointer { .. } => {
Devin Jeanpierre8a4fa202022-06-08 12:33:11 -07002989 // TODO(jeanpierreda): provide end-user-facing docs, and insert a link to e.g.
2990 // something like <internal link>
2991 bail!(
2992 "`self` has no lifetime. Use lifetime annotations or `#pragma clang lifetime_elision` to create bindings for this function."
2993 )
Lukasz Anforowicz732ca642022-02-03 20:58:38 +00002994 }
Devin Jeanpierre215ee8e2022-05-16 16:09:41 -07002995 RsTypeKind::Reference {
2996 referent: reference_pointee,
2997 lifetime: reference_lifetime,
2998 mutability: reference_mutability,
2999 } => {
3000 referent = reference_pointee;
3001 mutability = reference_mutability;
Devin Jeanpierre7ed8c6f2022-08-01 13:41:02 -07003002 lifetime = reference_lifetime;
Lukasz Anforowicz231a3bb2022-01-12 14:05:59 +00003003 }
Michael VanBemmel83eca6b2022-07-20 10:16:38 -07003004 RsTypeKind::Record { .. } => {
Devin Jeanpierree9be70a2022-07-25 08:58:54 -07003005 // This case doesn't happen for methods, but is needed for free functions mapped
3006 // to a trait impl that take the first argument by value.
Michael VanBemmel83eca6b2022-07-20 10:16:38 -07003007 return Ok(quote! { self });
3008 }
Lukasz Anforowicz732ca642022-02-03 20:58:38 +00003009 _ => bail!("Unexpected type of `self` parameter: {:?}", self),
Lukasz Anforowicz231a3bb2022-01-12 14:05:59 +00003010 }
Devin Jeanpierre215ee8e2022-05-16 16:09:41 -07003011 let mut_ = mutability.format_for_reference();
Devin Jeanpierre7ed8c6f2022-08-01 13:41:02 -07003012 let lifetime = lifetime.format_for_reference();
Devin Jeanpierre215ee8e2022-05-16 16:09:41 -07003013 if mutability == &Mutability::Mut && !referent.is_unpin() {
Devin Jeanpierre5d5ca7a2022-07-20 02:27:00 -07003014 // TODO(b/239661934): Add a `use ::std::pin::Pin` to the crate, and use
Devin Jeanpierre215ee8e2022-05-16 16:09:41 -07003015 // `Pin`.
Devin Jeanpierrecd5ebf32022-07-08 05:31:55 -07003016 Ok(quote! {self: ::std::pin::Pin< & #lifetime #mut_ Self>})
Devin Jeanpierre215ee8e2022-05-16 16:09:41 -07003017 } else {
3018 Ok(quote! { & #lifetime #mut_ self })
3019 }
Lukasz Anforowicz231a3bb2022-01-12 14:05:59 +00003020 }
3021
Lukasz Anforowicz90bdb962022-02-14 21:07:45 +00003022 /// Returns whether the type represented by `self` implements the `Copy`
3023 /// trait.
Lukasz Anforowicza94ab702022-01-14 22:40:25 +00003024 pub fn implements_copy(&self) -> bool {
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00003025 // TODO(b/212696226): Verify results of `implements_copy` via static
3026 // assertions in the generated Rust code (because incorrect results
3027 // can silently lead to unsafe behavior).
3028 match self {
3029 RsTypeKind::Unit => true,
3030 RsTypeKind::Pointer { .. } => true,
Lukasz Anforowiczcf230fd2022-02-18 19:20:39 +00003031 RsTypeKind::FuncPtr { .. } => true,
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00003032 RsTypeKind::Reference { mutability: Mutability::Const, .. } => true,
3033 RsTypeKind::Reference { mutability: Mutability::Mut, .. } => false,
Devin Jeanpierredeea7892022-03-29 02:13:31 -07003034 RsTypeKind::RvalueReference { .. } => false,
Devin Jeanpierrec0543eb2022-04-20 16:00:34 -07003035 RsTypeKind::IncompleteRecord { .. } => false,
Devin Jeanpierrefec5bfe2022-04-05 10:59:10 -07003036 RsTypeKind::Record { record, .. } => should_derive_copy(record),
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00003037 RsTypeKind::TypeAlias { underlying_type, .. } => underlying_type.implements_copy(),
Lukasz Anforowiczd81bea92022-02-11 08:57:58 +00003038 RsTypeKind::Other { type_args, .. } => {
Lukasz Anforowicz20651e32022-02-10 14:52:15 +00003039 // All types that may appear here without `type_args` (e.g.
3040 // primitive types like `i32`) implement `Copy`. Generic types
3041 // that may be present here (e.g. Option<...>) are `Copy` if all
3042 // of their `type_args` are `Copy`.
3043 type_args.iter().all(|t| t.implements_copy())
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00003044 }
3045 }
3046 }
Lukasz Anforowicza94ab702022-01-14 22:40:25 +00003047
Lukasz Anforowiczf9564622022-01-28 14:31:04 +00003048 pub fn is_ref_to(&self, expected_record: &Record) -> bool {
3049 match self {
3050 RsTypeKind::Reference { referent, .. } => referent.is_record(expected_record),
3051 _ => false,
3052 }
3053 }
3054
Lukasz Anforowicza94ab702022-01-14 22:40:25 +00003055 pub fn is_shared_ref_to(&self, expected_record: &Record) -> bool {
3056 match self {
3057 RsTypeKind::Reference { referent, mutability: Mutability::Const, .. } => {
Lukasz Anforowiczf9564622022-01-28 14:31:04 +00003058 referent.is_record(expected_record)
Lukasz Anforowicza94ab702022-01-14 22:40:25 +00003059 }
3060 _ => false,
3061 }
3062 }
Lukasz Anforowiczf9564622022-01-28 14:31:04 +00003063
3064 pub fn is_record(&self, expected_record: &Record) -> bool {
3065 match self {
Devin Jeanpierrefec5bfe2022-04-05 10:59:10 -07003066 RsTypeKind::Record { record: actual_record, .. } => {
3067 actual_record.id == expected_record.id
3068 }
Lukasz Anforowiczf9564622022-01-28 14:31:04 +00003069 _ => false,
3070 }
3071 }
Lukasz Anforowicz90bdb962022-02-14 21:07:45 +00003072
3073 /// Iterates over `self` and all the nested types (e.g. pointees, generic
3074 /// type args, etc.) in DFS order.
Devin Jeanpierre8cd8ae72022-06-29 18:59:40 -07003075 pub fn dfs_iter<'ty>(&'ty self) -> impl Iterator<Item = &'ty RsTypeKind> + '_ {
Lukasz Anforowicz90bdb962022-02-14 21:07:45 +00003076 RsTypeKindIter::new(self)
3077 }
3078
3079 /// Iterates over all `LifetimeId`s in `self` and in all the nested types.
3080 /// Note that the results might contain duplicate LifetimeId values (e.g.
3081 /// if the same LifetimeId is used in two `type_args`).
Devin Jeanpierre4e94a082022-08-10 01:58:35 -07003082 pub fn lifetimes(&self) -> impl Iterator<Item = Lifetime> + '_ {
3083 self.dfs_iter().filter_map(Self::lifetime)
Lukasz Anforowicz90bdb962022-02-14 21:07:45 +00003084 }
Devin Jeanpierrefd4ff822022-07-15 01:43:55 -07003085
3086 /// Returns the pointer or reference target.
3087 pub fn referent(&self) -> Option<&RsTypeKind> {
3088 match self {
3089 Self::Pointer { pointee: p, .. }
3090 | Self::Reference { referent: p, .. }
3091 | Self::RvalueReference { referent: p, .. } => Some(&**p),
3092 _ => None,
3093 }
3094 }
Devin Jeanpierre4e94a082022-08-10 01:58:35 -07003095
3096 /// Returns the reference lifetime, or None if this is not a reference.
3097 pub fn lifetime(&self) -> Option<Lifetime> {
3098 match self {
3099 Self::Reference { lifetime, .. } | Self::RvalueReference { lifetime, .. } => {
3100 Some(lifetime.clone())
3101 }
3102 _ => None,
3103 }
3104 }
Lukasz Anforowicz90bdb962022-02-14 21:07:45 +00003105}
3106
Devin Jeanpierre8cd8ae72022-06-29 18:59:40 -07003107impl ToTokens for RsTypeKind {
Devin Jeanpierre92ca2612022-04-06 11:35:13 -07003108 fn to_tokens(&self, tokens: &mut TokenStream) {
3109 self.to_token_stream().to_tokens(tokens)
3110 }
3111
3112 fn to_token_stream(&self) -> TokenStream {
3113 match self {
3114 RsTypeKind::Pointer { pointee, mutability } => {
3115 let mutability = mutability.format_for_pointer();
3116 quote! {* #mutability #pointee}
3117 }
3118 RsTypeKind::Reference { referent, mutability, lifetime } => {
3119 let mut_ = mutability.format_for_reference();
Devin Jeanpierre7ed8c6f2022-08-01 13:41:02 -07003120 let lifetime = lifetime.format_for_reference();
Devin Jeanpierre92ca2612022-04-06 11:35:13 -07003121 let reference = quote! {& #lifetime #mut_ #referent};
3122 if mutability == &Mutability::Mut && !referent.is_unpin() {
Devin Jeanpierre5d5ca7a2022-07-20 02:27:00 -07003123 // TODO(b/239661934): Add a `use ::std::pin::Pin` to the crate, and use
Devin Jeanpierre92ca2612022-04-06 11:35:13 -07003124 // `Pin`. This either requires deciding how to qualify pin at
3125 // RsTypeKind-creation time, or returning an RsSnippet from here (and not
3126 // implementing ToTokens, but instead some other interface.)
Devin Jeanpierrecd5ebf32022-07-08 05:31:55 -07003127 quote! {::std::pin::Pin< #reference >}
Devin Jeanpierre92ca2612022-04-06 11:35:13 -07003128 } else {
3129 reference
3130 }
3131 }
3132 RsTypeKind::RvalueReference { referent, mutability, lifetime } => {
Devin Jeanpierre5d5ca7a2022-07-20 02:27:00 -07003133 // TODO(b/239661934): Add a `use ::ctor::RvalueReference` (etc.) to the crate.
Devin Jeanpierre92ca2612022-04-06 11:35:13 -07003134 if mutability == &Mutability::Mut {
Devin Jeanpierrecd5ebf32022-07-08 05:31:55 -07003135 quote! {::ctor::RvalueReference<#lifetime, #referent>}
Devin Jeanpierre92ca2612022-04-06 11:35:13 -07003136 } else {
Devin Jeanpierrecd5ebf32022-07-08 05:31:55 -07003137 quote! {::ctor::ConstRvalueReference<#lifetime, #referent>}
Devin Jeanpierre92ca2612022-04-06 11:35:13 -07003138 }
3139 }
3140 RsTypeKind::FuncPtr { abi, return_type, param_types } => {
3141 let return_frag = return_type.format_as_return_type_fragment();
3142 quote! { extern #abi fn( #( #param_types ),* ) #return_frag }
3143 }
Marcel Hlopko36234892022-05-10 00:39:54 -07003144 RsTypeKind::IncompleteRecord {
3145 incomplete_record,
3146 namespace_qualifier,
3147 crate_ident,
3148 } => {
Rosica Dejanovskae12d7172022-06-22 12:20:17 -07003149 let record_ident = make_rs_ident(&incomplete_record.rs_name);
Lukasz Anforowicz92be29f2022-09-02 17:12:29 -07003150 let namespace_qualifier = namespace_qualifier.format_for_rs();
Rosica Dejanovska8283fe62022-05-09 10:04:28 -07003151 match crate_ident {
3152 Some(ci) => {
Lukasz Anforowicz4e2e0162022-09-01 07:07:32 -07003153 quote! { #ci #namespace_qualifier #record_ident }
Marcel Hlopko36234892022-05-10 00:39:54 -07003154 }
Rosica Dejanovska8283fe62022-05-09 10:04:28 -07003155 None => {
Lukasz Anforowicz4e2e0162022-09-01 07:07:32 -07003156 quote! { crate:: #namespace_qualifier #record_ident }
Rosica Dejanovska8283fe62022-05-09 10:04:28 -07003157 }
3158 }
Devin Jeanpierrec0543eb2022-04-20 16:00:34 -07003159 }
Rosica Dejanovska8283fe62022-05-09 10:04:28 -07003160 RsTypeKind::Record { record, namespace_qualifier, crate_ident } => {
3161 let ident = make_rs_ident(&record.rs_name);
Lukasz Anforowicz92be29f2022-09-02 17:12:29 -07003162 let namespace_qualifier = namespace_qualifier.format_for_rs();
Rosica Dejanovska8283fe62022-05-09 10:04:28 -07003163 match crate_ident {
3164 Some(ci) => {
Lukasz Anforowicz4e2e0162022-09-01 07:07:32 -07003165 quote! { #ci:: #namespace_qualifier #ident }
Marcel Hlopko36234892022-05-10 00:39:54 -07003166 }
Rosica Dejanovska8283fe62022-05-09 10:04:28 -07003167 None => {
Lukasz Anforowicz4e2e0162022-09-01 07:07:32 -07003168 quote! { crate:: #namespace_qualifier #ident }
Rosica Dejanovska8283fe62022-05-09 10:04:28 -07003169 }
3170 }
Devin Jeanpierre92ca2612022-04-06 11:35:13 -07003171 }
Rosica Dejanovska8283fe62022-05-09 10:04:28 -07003172 RsTypeKind::TypeAlias { type_alias, namespace_qualifier, crate_ident, .. } => {
3173 let ident = make_rs_ident(&type_alias.identifier.identifier);
Lukasz Anforowicz92be29f2022-09-02 17:12:29 -07003174 let namespace_qualifier = namespace_qualifier.format_for_rs();
Rosica Dejanovska8283fe62022-05-09 10:04:28 -07003175 match crate_ident {
3176 Some(ci) => {
Lukasz Anforowicz4e2e0162022-09-01 07:07:32 -07003177 quote! { #ci:: #namespace_qualifier #ident }
Marcel Hlopko36234892022-05-10 00:39:54 -07003178 }
Rosica Dejanovska8283fe62022-05-09 10:04:28 -07003179 None => {
Lukasz Anforowicz4e2e0162022-09-01 07:07:32 -07003180 quote! { crate:: #namespace_qualifier #ident }
Rosica Dejanovska8283fe62022-05-09 10:04:28 -07003181 }
3182 }
Devin Jeanpierre92ca2612022-04-06 11:35:13 -07003183 }
Googler73021682022-10-28 02:39:25 -07003184 // This doesn't affect void in function return values, as those are special-cased to be
3185 // omitted.
3186 RsTypeKind::Unit => quote!{::std::os::raw::c_void},
Devin Jeanpierre92ca2612022-04-06 11:35:13 -07003187 RsTypeKind::Other { name, type_args } => {
3188 let ident = make_rs_ident(name);
Felipe de A. Mello Pereiraec5f22c2022-11-02 04:04:01 -07003189 let generic_params =
3190 format_generic_params(/* lifetimes= */ &[], type_args.iter());
Devin Jeanpierre92ca2612022-04-06 11:35:13 -07003191 quote! {#ident #generic_params}
3192 }
3193 }
3194 }
3195}
3196
Devin Jeanpierre8cd8ae72022-06-29 18:59:40 -07003197struct RsTypeKindIter<'ty> {
3198 todo: Vec<&'ty RsTypeKind>,
Lukasz Anforowicz90bdb962022-02-14 21:07:45 +00003199}
3200
Devin Jeanpierre8cd8ae72022-06-29 18:59:40 -07003201impl<'ty> RsTypeKindIter<'ty> {
3202 pub fn new(ty: &'ty RsTypeKind) -> Self {
Lukasz Anforowicz90bdb962022-02-14 21:07:45 +00003203 Self { todo: vec![ty] }
3204 }
3205}
3206
Devin Jeanpierre8cd8ae72022-06-29 18:59:40 -07003207impl<'ty> Iterator for RsTypeKindIter<'ty> {
3208 type Item = &'ty RsTypeKind;
Lukasz Anforowicz90bdb962022-02-14 21:07:45 +00003209
3210 fn next(&mut self) -> Option<Self::Item> {
3211 match self.todo.pop() {
3212 None => None,
3213 Some(curr) => {
3214 match curr {
Devin Jeanpierrec0543eb2022-04-20 16:00:34 -07003215 RsTypeKind::Unit
3216 | RsTypeKind::IncompleteRecord { .. }
3217 | RsTypeKind::Record { .. } => {}
Lukasz Anforowicz90bdb962022-02-14 21:07:45 +00003218 RsTypeKind::Pointer { pointee, .. } => self.todo.push(pointee),
3219 RsTypeKind::Reference { referent, .. } => self.todo.push(referent),
Devin Jeanpierredeea7892022-03-29 02:13:31 -07003220 RsTypeKind::RvalueReference { referent, .. } => self.todo.push(referent),
Lukasz Anforowicz90bdb962022-02-14 21:07:45 +00003221 RsTypeKind::TypeAlias { underlying_type: t, .. } => self.todo.push(t),
Lukasz Anforowiczcf230fd2022-02-18 19:20:39 +00003222 RsTypeKind::FuncPtr { return_type, param_types, .. } => {
3223 self.todo.push(return_type);
3224 self.todo.extend(param_types.iter().rev());
Devin Jeanpierre1b8f4a12022-03-22 21:29:42 +00003225 }
Lukasz Anforowicz90bdb962022-02-14 21:07:45 +00003226 RsTypeKind::Other { type_args, .. } => self.todo.extend(type_args.iter().rev()),
3227 };
3228 Some(curr)
3229 }
3230 }
3231 }
Lukasz Anforowicz40c2eb82022-01-11 18:22:31 +00003232}
3233
Devin Jeanpierre6bb81802022-08-10 02:08:47 -07003234fn unique_lifetimes<'a>(
3235 types: impl IntoIterator<Item = &'a RsTypeKind> + 'a,
3236) -> impl Iterator<Item = Lifetime> + 'a {
3237 let mut unordered_lifetimes = HashSet::new();
3238 types
3239 .into_iter()
3240 .flat_map(|ty| ty.lifetimes())
3241 .filter(move |lifetime| unordered_lifetimes.insert(lifetime.clone()))
3242}
3243
Devin Jeanpierre3a0cc5a2022-07-12 09:36:34 -07003244fn rs_type_kind(db: &dyn BindingsGenerator, ty: ir::RsType) -> Result<RsTypeKind> {
3245 let ir = db.ir();
3246 // The lambdas deduplicate code needed by multiple `match` branches.
3247 let get_type_args = || -> Result<Vec<RsTypeKind>> {
3248 ty.type_args.iter().map(|type_arg| db.rs_type_kind(type_arg.clone())).collect()
3249 };
3250 let get_pointee = || -> Result<Rc<RsTypeKind>> {
3251 if ty.type_args.len() != 1 {
3252 bail!("Missing pointee/referent type (need exactly 1 type argument): {:?}", ty);
3253 }
3254 Ok(Rc::new(get_type_args()?.remove(0)))
3255 };
Devin Jeanpierre7ed8c6f2022-08-01 13:41:02 -07003256 let get_lifetime = || -> Result<Lifetime> {
Devin Jeanpierre3a0cc5a2022-07-12 09:36:34 -07003257 if ty.lifetime_args.len() != 1 {
3258 bail!("Missing reference lifetime (need exactly 1 lifetime argument): {:?}", ty);
3259 }
3260 let lifetime_id = ty.lifetime_args[0];
3261 ir.get_lifetime(lifetime_id)
3262 .ok_or_else(|| anyhow!("no known lifetime with id {lifetime_id:?}"))
Devin Jeanpierre4e94a082022-08-10 01:58:35 -07003263 .map(Lifetime::from)
Devin Jeanpierre3a0cc5a2022-07-12 09:36:34 -07003264 };
3265
3266 let result = match ty.name.as_deref() {
3267 None => {
3268 ensure!(
3269 ty.type_args.is_empty(),
3270 "Type arguments on records nor type aliases are not yet supported: {:?}",
3271 ty
3272 );
3273 match ir.item_for_type(&ty)? {
3274 Item::IncompleteRecord(incomplete_record) => RsTypeKind::IncompleteRecord {
3275 incomplete_record: incomplete_record.clone(),
Devin Jeanpierre781eeb12022-09-06 19:23:25 -07003276 namespace_qualifier: Rc::new(NamespaceQualifier::new(
3277 incomplete_record.id,
3278 &ir,
3279 )?),
Devin Jeanpierre3a0cc5a2022-07-12 09:36:34 -07003280 crate_ident: rs_imported_crate_name(&incomplete_record.owning_target, &ir),
3281 },
3282 Item::Record(record) => RsTypeKind::new_record(record.clone(), &ir)?,
Devin Jeanpierre5fecde52022-09-14 06:53:39 -07003283 Item::TypeAlias(type_alias) => {
3284 // TODO(b/200067824): support nested type aliases.
3285 if type_alias.enclosing_record_id.is_some() {
3286 // Until this is supported, we import this as the underlying type.
3287 db.rs_type_kind(type_alias.underlying_type.rs_type.clone())?
3288 } else {
3289 RsTypeKind::TypeAlias {
3290 type_alias: type_alias.clone(),
3291 namespace_qualifier: Rc::new(NamespaceQualifier::new(
3292 type_alias.id,
3293 &ir,
3294 )?),
3295 crate_ident: rs_imported_crate_name(&type_alias.owning_target, &ir),
3296 underlying_type: Rc::new(
3297 db.rs_type_kind(type_alias.underlying_type.rs_type.clone())?,
3298 ),
3299 }
3300 }
3301 }
Devin Jeanpierre3a0cc5a2022-07-12 09:36:34 -07003302 other_item => bail!("Item does not define a type: {:?}", other_item),
3303 }
3304 }
3305 Some(name) => match name {
3306 "()" => {
3307 if !ty.type_args.is_empty() {
3308 bail!("Unit type must not have type arguments: {:?}", ty);
3309 }
3310 RsTypeKind::Unit
3311 }
3312 "*mut" => RsTypeKind::Pointer { pointee: get_pointee()?, mutability: Mutability::Mut },
3313 "*const" => {
3314 RsTypeKind::Pointer { pointee: get_pointee()?, mutability: Mutability::Const }
3315 }
3316 "&mut" => RsTypeKind::Reference {
3317 referent: get_pointee()?,
3318 mutability: Mutability::Mut,
3319 lifetime: get_lifetime()?,
3320 },
3321 "&" => RsTypeKind::Reference {
3322 referent: get_pointee()?,
3323 mutability: Mutability::Const,
3324 lifetime: get_lifetime()?,
3325 },
3326 "#RvalueReference mut" => RsTypeKind::RvalueReference {
3327 referent: get_pointee()?,
3328 mutability: Mutability::Mut,
3329 lifetime: get_lifetime()?,
3330 },
3331 "#RvalueReference const" => RsTypeKind::RvalueReference {
3332 referent: get_pointee()?,
3333 mutability: Mutability::Const,
3334 lifetime: get_lifetime()?,
3335 },
3336 name => {
3337 let mut type_args = get_type_args()?;
3338 match name.strip_prefix("#funcPtr ") {
3339 None => RsTypeKind::Other { name: name.into(), type_args: Rc::from(type_args) },
3340 Some(abi) => {
3341 // TODO(b/217419782): Consider enforcing `'static` lifetime.
3342 ensure!(!type_args.is_empty(), "No return type in fn type: {:?}", ty);
3343 RsTypeKind::FuncPtr {
3344 abi: abi.into(),
3345 return_type: Rc::new(type_args.remove(type_args.len() - 1)),
3346 param_types: Rc::from(type_args),
3347 }
3348 }
3349 }
3350 }
3351 },
3352 };
3353 Ok(result)
Devin Jeanpierre7a7328e2021-09-17 07:10:08 +00003354}
3355
Lukasz Anforowicz90bb7462022-09-01 08:13:28 -07003356fn cc_type_name_for_record(record: &Record, ir: &IR) -> Result<TokenStream> {
Devin Jeanpierre5fecde52022-09-14 06:53:39 -07003357 let tagless = cc_tagless_type_name_for_record(record, ir)?;
3358 let tag_kind = cc_tag_kind(record);
3359 Ok(quote! { #tag_kind #tagless })
3360}
3361
3362fn cc_tagless_type_name_for_record(record: &Record, ir: &IR) -> Result<TokenStream> {
Lukasz Anforowicz90bb7462022-09-01 08:13:28 -07003363 let ident = format_cc_ident(&record.cc_name);
Lukasz Anforowicz92be29f2022-09-02 17:12:29 -07003364 let namespace_qualifier = NamespaceQualifier::new(record.id, ir)?.format_for_cc();
Devin Jeanpierre5fecde52022-09-14 06:53:39 -07003365 Ok(quote! { #namespace_qualifier #ident })
Lukasz Anforowicz90bb7462022-09-01 08:13:28 -07003366}
3367
Rosica Dejanovska8283fe62022-05-09 10:04:28 -07003368fn cc_type_name_for_item(item: &ir::Item, ir: &IR) -> Result<TokenStream> {
Lukasz Anforowicz90bb7462022-09-01 08:13:28 -07003369 match item {
Lukasz Anforowicz8dd51792022-08-31 10:11:17 -07003370 Item::IncompleteRecord(incomplete_record) => {
3371 let ident = format_cc_ident(&incomplete_record.cc_name);
Devin Jeanpierre781eeb12022-09-06 19:23:25 -07003372 let namespace_qualifier =
3373 NamespaceQualifier::new(incomplete_record.id, ir)?.format_for_cc();
Lukasz Anforowicz8dd51792022-08-31 10:11:17 -07003374 let tag_kind = incomplete_record.record_type;
Lukasz Anforowicz90bb7462022-09-01 08:13:28 -07003375 Ok(quote! { #tag_kind #namespace_qualifier #ident })
Lukasz Anforowicz8dd51792022-08-31 10:11:17 -07003376 }
Lukasz Anforowicz90bb7462022-09-01 08:13:28 -07003377 Item::Record(record) => cc_type_name_for_record(record, ir),
Lukasz Anforowicz4c3a2cc2022-03-11 00:24:49 +00003378 Item::TypeAlias(type_alias) => {
3379 let ident = format_cc_ident(&type_alias.identifier.identifier);
Devin Jeanpierre5fecde52022-09-14 06:53:39 -07003380 if let Some(record_id) = type_alias.enclosing_record_id {
3381 let parent =
3382 cc_tagless_type_name_for_record(ir.find_decl::<Rc<Record>>(record_id)?, ir)?;
3383 Ok(quote! { #parent :: #ident })
3384 } else {
3385 let namespace_qualifier =
3386 NamespaceQualifier::new(type_alias.id, ir)?.format_for_cc();
3387 Ok(quote! { #namespace_qualifier #ident })
3388 }
Devin Jeanpierre1b8f4a12022-03-22 21:29:42 +00003389 }
Googler6a0a5252022-01-11 14:08:09 +00003390 _ => bail!("Item does not define a type: {:?}", item),
Lukasz Anforowicz90bb7462022-09-01 08:13:28 -07003391 }
Googler6a0a5252022-01-11 14:08:09 +00003392}
3393
Lukasz Anforowiczd4742ff2022-07-11 17:05:02 -07003394fn cc_tag_kind(record: &ir::Record) -> TokenStream {
Kinuko Yasuda8dd84642022-08-17 09:19:47 -07003395 if record.is_anon_record_with_typedef {
3396 quote! {}
3397 } else {
Lukasz Anforowicz8dd51792022-08-31 10:11:17 -07003398 record.record_type.into_token_stream()
Marcel Hlopko4c29c6f2022-05-04 00:49:14 -07003399 }
3400}
3401
Lukasz Anforowicz71e9b592022-03-04 19:03:02 +00003402// Maps a Rust ABI [1] into a Clang attribute. See also
3403// `ConvertCcCallConvIntoRsApi` in importer.cc.
3404// [1]
3405// https://doc.rust-lang.org/reference/items/functions.html#extern-function-qualifier
3406fn format_cc_call_conv_as_clang_attribute(rs_abi: &str) -> Result<TokenStream> {
3407 match rs_abi {
3408 "cdecl" => Ok(quote! {}),
3409 "fastcall" => Ok(quote! { __attribute__((fastcall)) }),
3410 "stdcall" => Ok(quote! { __attribute__((stdcall)) }),
3411 "thiscall" => Ok(quote! { __attribute__((thiscall)) }),
3412 "vectorcall" => Ok(quote! { __attribute__((vectorcall)) }),
3413 _ => bail!("Unsupported ABI: {}", rs_abi),
3414 }
3415}
3416
Marcel Hlopkoc0956cf2021-11-29 08:31:28 +00003417fn format_cc_type(ty: &ir::CcType, ir: &IR) -> Result<TokenStream> {
Devin Jeanpierre69fe2392022-10-04 22:01:24 -07003418 // Formatting *both* pointers *and* references as pointers, because:
3419 // - Pointers and references have the same representation in the ABI.
3420 // - Clang's `-Wreturn-type-c-linkage` warns when using references in C++
3421 // function thunks declared as `extern "C"` (see b/238681766).
3422 format_cc_type_inner(ty, ir, /* references_ok= */ false)
3423}
3424fn format_cc_type_inner(ty: &ir::CcType, ir: &IR, references_ok: bool) -> Result<TokenStream> {
Devin Jeanpierre09c6f452021-09-29 07:34:24 +00003425 let const_fragment = if ty.is_const {
Devin Jeanpierre184f9ac2021-09-17 13:47:03 +00003426 quote! {const}
3427 } else {
3428 quote! {}
3429 };
Marcel Hlopkoc0956cf2021-11-29 08:31:28 +00003430 if let Some(ref name) = ty.name {
3431 match name.as_str() {
Devin Jeanpierre69fe2392022-10-04 22:01:24 -07003432 mut name @ ("*" | "&" | "&&") => {
Googlerff7fc232021-12-02 09:43:00 +00003433 if ty.type_args.len() != 1 {
3434 bail!("Invalid pointer type (need exactly 1 type argument): {:?}", ty);
Marcel Hlopkoc0956cf2021-11-29 08:31:28 +00003435 }
Devin Jeanpierre69fe2392022-10-04 22:01:24 -07003436 let nested_type = format_cc_type_inner(&ty.type_args[0], ir, references_ok)?;
3437 if !references_ok {
3438 name = "*";
3439 }
3440 let ptr = match name {
3441 "*" => quote! {*},
3442 "&" => quote! {&},
3443 "&&" => quote! {&&},
3444 _ => unreachable!(),
3445 };
3446 Ok(quote! {#nested_type #ptr #const_fragment})
Devin Jeanpierre7a7328e2021-09-17 07:10:08 +00003447 }
Lukasz Anforowicz71e9b592022-03-04 19:03:02 +00003448 cc_type_name => match cc_type_name.strip_prefix("#funcValue ") {
3449 None => {
3450 if !ty.type_args.is_empty() {
3451 bail!("Type not yet supported: {:?}", ty);
3452 }
Lukasz Anforowicz20a3c692022-10-06 08:48:15 -07003453 // Not using `code_gen_utils::format_cc_ident`, because
3454 // `cc_type_name` may be a C++ reserved keyword (e.g.
3455 // `int`).
3456 let cc_ident: TokenStream = cc_type_name.parse().unwrap();
Googler84cd5602022-10-07 08:25:46 -07003457 Ok(quote! { #cc_ident #const_fragment })
Devin Jeanpierre1b8f4a12022-03-22 21:29:42 +00003458 }
Lukasz Anforowicz71e9b592022-03-04 19:03:02 +00003459 Some(abi) => match ty.type_args.split_last() {
3460 None => bail!("funcValue type without a return type: {:?}", ty),
3461 Some((ret_type, param_types)) => {
Devin Jeanpierre69fe2392022-10-04 22:01:24 -07003462 // Function pointer types don't ignore references, but luckily,
3463 // `-Wreturn-type-c-linkage` does. So we can just re-enable references now
3464 // so that the function type is exactly correct.
3465 let ret_type =
3466 format_cc_type_inner(ret_type, ir, /* references_ok= */ true)?;
Lukasz Anforowicz71e9b592022-03-04 19:03:02 +00003467 let param_types = param_types
3468 .iter()
Devin Jeanpierre69fe2392022-10-04 22:01:24 -07003469 .map(|t| format_cc_type_inner(t, ir, /* references_ok= */ true))
Lukasz Anforowicz71e9b592022-03-04 19:03:02 +00003470 .collect::<Result<Vec<_>>>()?;
3471 let attr = format_cc_call_conv_as_clang_attribute(abi)?;
3472 // `type_identity_t` is used below to avoid having to
3473 // emit spiral-like syntax where some syntax elements of
3474 // an inner type (e.g. function type as below) can
3475 // surround syntax elements of an outer type (e.g. a
3476 // pointer type). Compare: `int (*foo)(int, int)` VS
3477 // `type_identity_t<int(int, int)>* foo`.
Lukasz Anforowicz23171542022-04-11 15:26:17 -07003478 Ok(quote! { crubit::type_identity_t<
Devin Jeanpierre1b8f4a12022-03-22 21:29:42 +00003479 #ret_type ( #( #param_types ),* ) #attr
3480 > })
3481 }
3482 },
3483 },
Devin Jeanpierre7a7328e2021-09-17 07:10:08 +00003484 }
Marcel Hlopkoc0956cf2021-11-29 08:31:28 +00003485 } else {
Googler6a0a5252022-01-11 14:08:09 +00003486 let item = ir.item_for_type(ty)?;
Rosica Dejanovska8283fe62022-05-09 10:04:28 -07003487 let type_name = cc_type_name_for_item(item, ir)?;
Googler6a0a5252022-01-11 14:08:09 +00003488 Ok(quote! {#const_fragment #type_name})
Devin Jeanpierre7a7328e2021-09-17 07:10:08 +00003489 }
3490}
3491
Rosica Dejanovska8283fe62022-05-09 10:04:28 -07003492fn cc_struct_layout_assertion(record: &Record, ir: &IR) -> Result<TokenStream> {
Googler6a0a5252022-01-11 14:08:09 +00003493 if !ir.is_current_target(&record.owning_target) && !ir.is_stdlib_target(&record.owning_target) {
Rosica Dejanovska8283fe62022-05-09 10:04:28 -07003494 return Ok(quote! {});
Marcel Hlopkoa0f38662021-12-03 08:45:26 +00003495 }
Lukasz Anforowicz4c3a2cc2022-03-11 00:24:49 +00003496 let record_ident = format_cc_ident(&record.cc_name);
Lukasz Anforowicz92be29f2022-09-02 17:12:29 -07003497 let namespace_qualifier = NamespaceQualifier::new(record.id, ir)?.format_for_cc();
Kinuko Yasuda54b75d72022-08-18 10:09:54 -07003498 let cc_size = Literal::usize_unsuffixed(record.original_cc_size);
Googler5ea88642021-09-29 08:05:59 +00003499 let alignment = Literal::usize_unsuffixed(record.alignment);
Lukasz Anforowiczd4742ff2022-07-11 17:05:02 -07003500 let tag_kind = cc_tag_kind(record);
Devin Jeanpierre190b90a2022-05-24 06:00:34 -07003501 let field_assertions = record
3502 .fields
3503 .iter()
3504 .filter(|f| f.access == AccessSpecifier::Public && f.identifier.is_some())
3505 // https://en.cppreference.com/w/cpp/types/offsetof points out that "if member is [...]
3506 // a bit-field [...] the behavior [of `offsetof` macro] is undefined.". In such
3507 // scenario clang reports an error: cannot compute offset of bit-field 'field_name'.
3508 .filter(|f| !f.is_bitfield)
3509 .map(|field| {
3510 // The IR contains the offset in bits, while `CRUBIT_OFFSET_OF` returns the
3511 // offset in bytes, so we need to convert. We can assert that
3512 // `field.offset` is always at field boundaries, because the
3513 // bitfields have been filtered out earlier.
3514 assert_eq!(field.offset % 8, 0);
3515 let expected_offset = Literal::usize_unsuffixed(field.offset / 8);
Lukasz Anforowicz30360ba2022-05-23 12:15:12 -07003516
Devin Jeanpierre190b90a2022-05-24 06:00:34 -07003517 let field_ident = format_cc_ident(&field.identifier.as_ref().unwrap().identifier);
3518 let actual_offset = quote! {
3519 CRUBIT_OFFSET_OF(#field_ident, #tag_kind #namespace_qualifier #record_ident)
3520 };
Lukasz Anforowicz30360ba2022-05-23 12:15:12 -07003521
Devin Jeanpierre190b90a2022-05-24 06:00:34 -07003522 quote! { static_assert( #actual_offset == #expected_offset); }
3523 });
Rosica Dejanovska8283fe62022-05-09 10:04:28 -07003524 Ok(quote! {
Kinuko Yasuda54b75d72022-08-18 10:09:54 -07003525 static_assert(sizeof(#tag_kind #namespace_qualifier #record_ident) == #cc_size);
Rosica Dejanovska8283fe62022-05-09 10:04:28 -07003526 static_assert(alignof(#tag_kind #namespace_qualifier #record_ident) == #alignment);
Googler5ea88642021-09-29 08:05:59 +00003527 #( #field_assertions )*
Rosica Dejanovska8283fe62022-05-09 10:04:28 -07003528 })
Googler5ea88642021-09-29 08:05:59 +00003529}
3530
Devin Jeanpierre58181ac2022-02-14 21:30:05 +00003531// Returns the accessor functions for no_unique_address member variables.
Devin Jeanpierre45b01962022-07-07 06:12:11 -07003532fn cc_struct_no_unique_address_impl(db: &Database, record: &Record) -> Result<TokenStream> {
Devin Jeanpierre58181ac2022-02-14 21:30:05 +00003533 let mut fields = vec![];
3534 let mut types = vec![];
3535 for field in &record.fields {
3536 if field.access != AccessSpecifier::Public || !field.is_no_unique_address {
3537 continue;
3538 }
Lukasz Anforowiczfea0db92022-05-17 17:28:04 -07003539 // Can't use `get_field_rs_type_for_layout` here, because we want to dig into
3540 // no_unique_address fields, despite laying them out as opaque blobs of bytes.
Devin Jeanpierre409f6f62022-07-07 02:00:26 -07003541 if let Ok(rs_type) = field.type_.as_ref().map(|t| t.rs_type.clone()) {
Lukasz Anforowiczfea0db92022-05-17 17:28:04 -07003542 fields.push(make_rs_ident(
3543 &field
3544 .identifier
3545 .as_ref()
3546 .expect("Unnamed fields can't be annotated with [[no_unique_address]]")
3547 .identifier,
3548 ));
Devin Jeanpierre409f6f62022-07-07 02:00:26 -07003549 types.push(db.rs_type_kind(rs_type).with_context(|| {
Lukasz Anforowiczfea0db92022-05-17 17:28:04 -07003550 format!("Failed to format type for field {:?} on record {:?}", field, record)
Devin Jeanpierre409f6f62022-07-07 02:00:26 -07003551 })?);
Lukasz Anforowiczfea0db92022-05-17 17:28:04 -07003552 }
Devin Jeanpierre58181ac2022-02-14 21:30:05 +00003553 }
3554
3555 if fields.is_empty() {
3556 return Ok(quote! {});
3557 }
3558
Lukasz Anforowicz4c3a2cc2022-03-11 00:24:49 +00003559 let ident = make_rs_ident(&record.rs_name);
Devin Jeanpierre58181ac2022-02-14 21:30:05 +00003560 Ok(quote! {
3561 impl #ident {
3562 #(
3563 pub fn #fields(&self) -> &#types {
3564 unsafe {&* (&self.#fields as *const _ as *const #types)}
3565 }
3566 )*
3567 }
3568 })
3569}
3570
Devin Jeanpierre56777022022-02-03 01:57:15 +00003571/// Returns the implementation of base class conversions, for converting a type
3572/// to its unambiguous public base classes.
Lukasz Anforowicz90bb7462022-09-01 08:13:28 -07003573fn cc_struct_upcast_impl(record: &Rc<Record>, ir: &IR) -> Result<GeneratedItem> {
Devin Jeanpierre56777022022-02-03 01:57:15 +00003574 let mut impls = Vec::with_capacity(record.unambiguous_public_bases.len());
Devin Jeanpierref99db3e2022-04-27 23:10:33 -07003575 let mut thunks = vec![];
3576 let mut cc_impls = vec![];
Devin Jeanpierre56777022022-02-03 01:57:15 +00003577 for base in &record.unambiguous_public_bases {
Devin Jeanpierre8cd8ae72022-06-29 18:59:40 -07003578 let base_record: &Rc<Record> = ir
Lukasz Anforowicz44047252022-03-23 13:04:48 +00003579 .find_decl(base.base_record_id)
3580 .with_context(|| format!("Can't find a base record of {:?}", record))?;
Devin Jeanpierre8cd8ae72022-06-29 18:59:40 -07003581 let base_name = RsTypeKind::new_record(base_record.clone(), ir)?.into_token_stream();
Lukasz Anforowicz90bb7462022-09-01 08:13:28 -07003582 let derived_name = RsTypeKind::new_record(record.clone(), ir)?.into_token_stream();
Devin Jeanpierref99db3e2022-04-27 23:10:33 -07003583 let body;
Devin Jeanpierre56777022022-02-03 01:57:15 +00003584 if let Some(offset) = base.offset {
3585 let offset = Literal::i64_unsuffixed(offset);
Devin Jeanpierreb368e682022-05-03 02:23:44 -07003586 body = quote! {(derived as *const _ as *const u8).offset(#offset) as *const #base_name};
Devin Jeanpierref99db3e2022-04-27 23:10:33 -07003587 } else {
Devin Jeanpierref99db3e2022-04-27 23:10:33 -07003588 let cast_fn_name = make_rs_ident(&format!(
3589 "__crubit_dynamic_upcast__{}__to__{}",
Lukasz Anforowicz3f133972022-09-01 09:01:02 -07003590 record.mangled_cc_name, base_record.mangled_cc_name
Devin Jeanpierref99db3e2022-04-27 23:10:33 -07003591 ));
Lukasz Anforowicz90bb7462022-09-01 08:13:28 -07003592 let base_cc_name = cc_type_name_for_record(base_record.as_ref(), ir)?;
3593 let derived_cc_name = cc_type_name_for_record(record.as_ref(), ir)?;
Devin Jeanpierref99db3e2022-04-27 23:10:33 -07003594 cc_impls.push(quote! {
3595 extern "C" const #base_cc_name& #cast_fn_name(const #derived_cc_name& from) {
3596 return from;
Devin Jeanpierre56777022022-02-03 01:57:15 +00003597 }
3598 });
Devin Jeanpierref99db3e2022-04-27 23:10:33 -07003599 thunks.push(quote! {
3600 pub fn #cast_fn_name (from: *const #derived_name) -> *const #base_name;
3601 });
3602 body = quote! {
Rosica Dejanovska68c11ac2022-05-12 03:48:59 -07003603 crate::detail::#cast_fn_name(derived)
Devin Jeanpierref99db3e2022-04-27 23:10:33 -07003604 };
Devin Jeanpierre56777022022-02-03 01:57:15 +00003605 }
Devin Jeanpierref99db3e2022-04-27 23:10:33 -07003606 impls.push(quote! {
Devin Jeanpierreb368e682022-05-03 02:23:44 -07003607 unsafe impl oops::Inherits<#base_name> for #derived_name {
3608 unsafe fn upcast_ptr(derived: *const Self) -> *const #base_name {
3609 #body
Devin Jeanpierref99db3e2022-04-27 23:10:33 -07003610 }
3611 }
3612 });
Devin Jeanpierre56777022022-02-03 01:57:15 +00003613 }
3614
Devin Jeanpierre8763a8e2022-04-26 15:45:13 -07003615 Ok(GeneratedItem {
Devin Jeanpierref99db3e2022-04-27 23:10:33 -07003616 item: quote! {#(#impls)*},
3617 thunks: quote! {#(#thunks)*},
3618 thunk_impls: quote! {#(#cc_impls)*},
Devin Jeanpierre8763a8e2022-04-26 15:45:13 -07003619 ..Default::default()
Devin Jeanpierre56777022022-02-03 01:57:15 +00003620 })
3621}
3622
Googlera675ae02021-12-07 08:04:59 +00003623fn thunk_ident(func: &Func) -> Ident {
3624 format_ident!("__rust_thunk__{}", func.mangled_name)
Devin Jeanpierref2ec8712021-10-13 20:47:16 +00003625}
3626
Devin Jeanpierreb8ce2c12022-07-13 10:34:01 -07003627fn generate_rs_api_impl(db: &mut Database, crubit_support_path: &str) -> Result<TokenStream> {
Michael Forsterbee84482021-10-13 08:35:38 +00003628 // This function uses quote! to generate C++ source code out of convenience.
3629 // This is a bold idea so we have to continously evaluate if it still makes
3630 // sense or the cost of working around differences in Rust and C++ tokens is
3631 // greather than the value added.
Marcel Hlopko3164eee2021-08-24 20:09:22 +00003632 //
Michael Forsterbee84482021-10-13 08:35:38 +00003633 // See rs_bindings_from_cc/
3634 // token_stream_printer.rs for a list of supported placeholders.
Marcel Hlopko3164eee2021-08-24 20:09:22 +00003635 let mut thunks = vec![];
Devin Jeanpierreb8ce2c12022-07-13 10:34:01 -07003636 let ir = db.ir();
Michael Forster7ef80732021-10-01 18:12:19 +00003637 for func in ir.functions() {
Devin Jeanpierreb8ce2c12022-07-13 10:34:01 -07003638 if can_skip_cc_thunk(db, func) {
Marcel Hlopko3164eee2021-08-24 20:09:22 +00003639 continue;
3640 }
Devin Jeanpierred5a53622022-10-04 22:00:02 -07003641 match db.generate_func(func.clone()).unwrap_or_default() {
3642 None => {
3643 // No function was generated that will call this thunk.
3644 continue;
3645 }
3646 Some(generated) => {
3647 let (_, _, function_id) = &*generated;
3648 // TODO(jeanpierreda): this should be moved into can_skip_cc_thunk, but that'd be
3649 // cyclic right now, because overloaded_funcs calls generate_func calls
3650 // can_skip_cc_thunk. We probably need to break generate_func apart.
3651 if db.overloaded_funcs().contains(function_id) {
3652 continue;
3653 }
3654 }
Devin Jeanpierree9be70a2022-07-25 08:58:54 -07003655 }
Marcel Hlopko3164eee2021-08-24 20:09:22 +00003656
Googlera675ae02021-12-07 08:04:59 +00003657 let thunk_ident = thunk_ident(func);
Devin Jeanpierref2ec8712021-10-13 20:47:16 +00003658 let implementation_function = match &func.name {
Lukasz Anforowicz9c663ca2022-02-09 01:33:31 +00003659 UnqualifiedIdentifier::Operator(op) => {
3660 let name = syn::parse_str::<TokenStream>(&op.name)?;
3661 quote! { operator #name }
3662 }
Devin Jeanpierref2ec8712021-10-13 20:47:16 +00003663 UnqualifiedIdentifier::Identifier(id) => {
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +00003664 let fn_ident = format_cc_ident(&id.identifier);
Rosica Dejanovskaa08b3862022-06-02 08:22:49 -07003665 match func.member_func_metadata.as_ref() {
Lukasz Anforowiczaab8ad22021-12-19 20:29:26 +00003666 Some(meta) => {
Rosica Dejanovskaa08b3862022-06-02 08:22:49 -07003667 if let Some(_) = meta.instance_method_metadata {
3668 quote! { #fn_ident }
3669 } else {
Devin Jeanpierreab85d442022-06-29 19:16:41 -07003670 let record: &Rc<Record> = ir.find_decl(meta.record_id)?;
Rosica Dejanovskaa08b3862022-06-02 08:22:49 -07003671 let record_ident = format_cc_ident(&record.cc_name);
Devin Jeanpierre781eeb12022-09-06 19:23:25 -07003672 let namespace_qualifier =
3673 NamespaceQualifier::new(record.id, &ir)?.format_for_cc();
Lukasz Anforowicz4e2e0162022-09-01 07:07:32 -07003674 quote! { #namespace_qualifier #record_ident :: #fn_ident }
Rosica Dejanovskaa08b3862022-06-02 08:22:49 -07003675 }
3676 }
3677 None => {
Devin Jeanpierre781eeb12022-09-06 19:23:25 -07003678 let namespace_qualifier =
3679 NamespaceQualifier::new(func.id, &ir)?.format_for_cc();
Lukasz Anforowicz4e2e0162022-09-01 07:07:32 -07003680 quote! { #namespace_qualifier #fn_ident }
Lukasz Anforowiczaab8ad22021-12-19 20:29:26 +00003681 }
3682 }
Devin Jeanpierref2ec8712021-10-13 20:47:16 +00003683 }
Lukasz Anforowicz7b0042d2022-01-06 23:00:19 +00003684 // Use `destroy_at` to avoid needing to spell out the class name. Destructor identiifers
Devin Jeanpierrecc6cf092021-12-16 04:31:14 +00003685 // use the name of the type itself, without namespace qualification, template
3686 // parameters, or aliases. We do not need to use that naming scheme anywhere else in
3687 // the bindings, and it can be difficult (impossible?) to spell in the general case. By
3688 // using destroy_at, we avoid needing to determine or remember what the correct spelling
Lukasz Anforowicz7b0042d2022-01-06 23:00:19 +00003689 // is. Similar arguments apply to `construct_at`.
Lukasz Anforowicze643ec92021-12-22 15:45:15 +00003690 UnqualifiedIdentifier::Constructor => {
Lukasz Anforowicz23171542022-04-11 15:26:17 -07003691 quote! { crubit::construct_at }
Lukasz Anforowicze643ec92021-12-22 15:45:15 +00003692 }
Devin Jeanpierref2ec8712021-10-13 20:47:16 +00003693 UnqualifiedIdentifier::Destructor => quote! {std::destroy_at},
Devin Jeanpierref2ec8712021-10-13 20:47:16 +00003694 };
Marcel Hlopko3164eee2021-08-24 20:09:22 +00003695
Devin Jeanpierreb8ce2c12022-07-13 10:34:01 -07003696 let mut param_idents =
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +00003697 func.params.iter().map(|p| format_cc_ident(&p.identifier.identifier)).collect_vec();
Marcel Hlopko3164eee2021-08-24 20:09:22 +00003698
Devin Jeanpierreb8ce2c12022-07-13 10:34:01 -07003699 let mut param_types = func
Devin Jeanpierre09c6f452021-09-29 07:34:24 +00003700 .params
3701 .iter()
Devin Jeanpierre11ce2b92022-07-27 07:54:21 -07003702 .map(|p| {
3703 let formatted = format_cc_type(&p.type_.cc_type, &ir)?;
3704 if !db.rs_type_kind(p.type_.rs_type.clone())?.is_unpin() {
3705 // non-Unpin types are wrapped by a pointer in the thunk.
3706 Ok(quote! {#formatted *})
3707 } else {
3708 Ok(formatted)
3709 }
3710 })
Devin Jeanpierre09c6f452021-09-29 07:34:24 +00003711 .collect::<Result<Vec<_>>>()?;
Marcel Hlopko3164eee2021-08-24 20:09:22 +00003712
Devin Jeanpierre11ce2b92022-07-27 07:54:21 -07003713 let arg_expressions = func
Devin Jeanpierrefd4ff822022-07-15 01:43:55 -07003714 .params
Devin Jeanpierreb8ce2c12022-07-13 10:34:01 -07003715 .iter()
Lukasz Anforowicz07b33902022-07-13 15:27:03 -07003716 .map(|p| {
3717 let ident = format_cc_ident(&p.identifier.identifier);
3718 match p.type_.cc_type.name.as_deref() {
Devin Jeanpierre11ce2b92022-07-27 07:54:21 -07003719 Some("&") => Ok(quote! { * #ident }),
3720 Some("&&") => Ok(quote! { std::move(* #ident) }),
3721 _ => {
3722 // non-Unpin types are wrapped by a pointer in the thunk.
3723 if !db.rs_type_kind(p.type_.rs_type.clone())?.is_unpin() {
3724 Ok(quote! { std::move(* #ident) })
3725 } else {
3726 Ok(quote! { #ident })
3727 }
3728 }
Lukasz Anforowicz07b33902022-07-13 15:27:03 -07003729 }
3730 })
Devin Jeanpierre11ce2b92022-07-27 07:54:21 -07003731 .collect::<Result<Vec<_>>>()?;
Devin Jeanpierreb8ce2c12022-07-13 10:34:01 -07003732
3733 // Here, we add a __return parameter if the return type is not trivially
3734 // relocatable. (We do this after the arg_expressions computation, so
3735 // that it's only in the parameter list, not the argument list.)
3736 //
3737 // RsTypeKind is where, as much as anywhere, where the information about trivial
3738 // relocatability is stored.
3739 let is_trivial_return = db.rs_type_kind(func.return_type.rs_type.clone())?.is_unpin();
3740 let mut return_type_name = format_cc_type(&func.return_type.cc_type, &ir)?;
3741 if !is_trivial_return {
3742 param_idents.insert(0, format_cc_ident("__return"));
3743 param_types.insert(0, quote! {#return_type_name *});
3744 return_type_name = quote! {void};
3745 }
3746
Felipe de A. Mello Pereira7baec6e2022-10-05 02:45:13 -07003747 let this_ref_qualification =
3748 func.member_func_metadata.as_ref().and_then(|meta| match &func.name {
Devin Jeanpierre435d71f2022-10-04 22:18:50 -07003749 UnqualifiedIdentifier::Constructor | UnqualifiedIdentifier::Destructor => None,
Felipe de A. Mello Pereira7baec6e2022-10-05 02:45:13 -07003750 UnqualifiedIdentifier::Identifier(_) | UnqualifiedIdentifier::Operator(_) => meta
3751 .instance_method_metadata
3752 .as_ref()
3753 .map(|instance_method| instance_method.reference),
3754 });
3755 let (implementation_function, arg_expressions) =
3756 if let Some(this_ref_qualification) = this_ref_qualification {
3757 let this_param = func
3758 .params
3759 .first()
3760 .ok_or_else(|| anyhow!("Instance methods must have `__this` param."))?;
Devin Jeanpierre435d71f2022-10-04 22:18:50 -07003761
Felipe de A. Mello Pereira7baec6e2022-10-05 02:45:13 -07003762 let this_arg = format_cc_ident(&this_param.identifier.identifier);
3763 let this_dot = if this_ref_qualification == ir::ReferenceQualification::RValue {
3764 quote! {std::move(*#this_arg).}
3765 } else {
3766 quote! {#this_arg->}
3767 };
3768 (
3769 quote! { #this_dot #implementation_function},
3770 arg_expressions.iter().skip(1).cloned().collect_vec(),
3771 )
Devin Jeanpierre435d71f2022-10-04 22:18:50 -07003772 } else {
Felipe de A. Mello Pereira7baec6e2022-10-05 02:45:13 -07003773 (implementation_function, arg_expressions.clone())
Devin Jeanpierre435d71f2022-10-04 22:18:50 -07003774 };
Lukasz Anforowiczb3d89aa2022-01-12 14:35:52 +00003775
Devin Jeanpierreb8ce2c12022-07-13 10:34:01 -07003776 let return_expr = quote! {#implementation_function( #( #arg_expressions ),* )};
3777 let return_stmt = if !is_trivial_return {
Devin Jeanpierre521b4ee2022-07-27 07:19:50 -07003778 // Explicitly use placement new so that we get guaranteed copy elision in C++17.
Devin Jeanpierreb8ce2c12022-07-13 10:34:01 -07003779 let out_param = &param_idents[0];
Devin Jeanpierre521b4ee2022-07-27 07:19:50 -07003780 quote! {new(#out_param) auto(#return_expr)}
Devin Jeanpierreb8ce2c12022-07-13 10:34:01 -07003781 } else {
Lukasz Anforowicz07b33902022-07-13 15:27:03 -07003782 match func.return_type.cc_type.name.as_deref() {
3783 Some("void") => return_expr,
3784 Some("&") => quote! { return & #return_expr },
3785 Some("&&") => {
3786 // The code below replicates bits of `format_cc_type`, but formats an rvalue
3787 // reference (which `format_cc_type` would format as a pointer).
3788 // `const_fragment` from `format_cc_type` is ignored - it is not applicable for
3789 // references.
3790 let ty = &func.return_type.cc_type;
3791 if ty.type_args.len() != 1 {
3792 bail!("Invalid reference type (need exactly 1 type argument): {:?}", ty);
3793 }
3794 let nested_type = format_cc_type(&ty.type_args[0], &ir)?;
3795 quote! {
3796 #nested_type && lvalue = #return_expr;
3797 return &lvalue
3798 }
Devin Jeanpierrefd4ff822022-07-15 01:43:55 -07003799 }
Lukasz Anforowicz07b33902022-07-13 15:27:03 -07003800 _ => quote! { return #return_expr },
3801 }
Devin Jeanpierreb8ce2c12022-07-13 10:34:01 -07003802 };
3803
Marcel Hlopko3164eee2021-08-24 20:09:22 +00003804 thunks.push(quote! {
3805 extern "C" #return_type_name #thunk_ident( #( #param_types #param_idents ),* ) {
Devin Jeanpierreb8ce2c12022-07-13 10:34:01 -07003806 #return_stmt;
Marcel Hlopko3164eee2021-08-24 20:09:22 +00003807 }
3808 });
3809 }
3810
Rosica Dejanovska8283fe62022-05-09 10:04:28 -07003811 let layout_assertions = ir
3812 .records()
Devin Jeanpierreb8ce2c12022-07-13 10:34:01 -07003813 .map(|record| cc_struct_layout_assertion(record, &ir))
Rosica Dejanovska8283fe62022-05-09 10:04:28 -07003814 .collect::<Result<Vec<_>>>()?;
Googler5ea88642021-09-29 08:05:59 +00003815
Lukasz Anforowicz434c4692022-11-01 14:05:24 -07003816 let mut internal_includes = BTreeSet::new();
3817 internal_includes.insert(CcInclude::memory()); // ubiquitous.
Devin Jeanpierre231ef8d2021-10-27 10:50:44 +00003818 if ir.records().next().is_some() {
Lukasz Anforowicz434c4692022-11-01 14:05:24 -07003819 internal_includes.insert(CcInclude::cstddef());
Devin Jeanpierre231ef8d2021-10-27 10:50:44 +00003820 };
Lukasz Anforowicz434c4692022-11-01 14:05:24 -07003821 for crubit_header in ["cxx20_backports.h", "offsetof.h"] {
3822 internal_includes.insert(CcInclude::user_header(
3823 format!("{crubit_support_path}/{crubit_header}").into(),
3824 ));
3825 }
3826 let internal_includes = format_cc_includes(&internal_includes);
Lukasz Anforowicz4457baf2021-12-23 17:24:04 +00003827
Michael Forsterbee84482021-10-13 08:35:38 +00003828 // In order to generate C++ thunk in all the cases Clang needs to be able to
Lukasz Anforowicz434c4692022-11-01 14:05:24 -07003829 // access declarations from public headers of the C++ library. We don't
3830 // process these includes via `format_cc_includes` to preserve their
3831 // original order (some libraries require certain headers to be included
3832 // first - e.g. `config.h`).
3833 let ir_includes =
Lukasz Anforowicz121338a2022-11-01 14:28:32 -07003834 ir.public_headers().map(|hdr| CcInclude::user_header(hdr.name.clone())).collect_vec();
Marcel Hlopko3164eee2021-08-24 20:09:22 +00003835
Marcel Hlopko89547752021-12-10 09:39:41 +00003836 Ok(quote! {
Lukasz Anforowicz434c4692022-11-01 14:05:24 -07003837 #internal_includes
Lukasz Anforowicz121338a2022-11-01 14:28:32 -07003838 __NEWLINE__
3839 __COMMENT__ "Public headers of the C++ library being wrapped."
Lukasz Anforowicz434c4692022-11-01 14:05:24 -07003840 #( #ir_includes )* __NEWLINE__
Marcel Hlopkob8069ae2022-02-19 09:31:00 +00003841 __HASH_TOKEN__ pragma clang diagnostic push __NEWLINE__
3842 // Disable Clang thread-safety-analysis warnings that would otherwise
3843 // complain about thunks that call mutex locking functions in an unpaired way.
3844 __HASH_TOKEN__ pragma clang diagnostic ignored "-Wthread-safety-analysis" __NEWLINE__
Marcel Hlopko3164eee2021-08-24 20:09:22 +00003845
Michael Forsterdb8101a2021-10-08 06:56:03 +00003846 #( #thunks )* __NEWLINE__ __NEWLINE__
Googler5ea88642021-09-29 08:05:59 +00003847
Michael Forsterdb8101a2021-10-08 06:56:03 +00003848 #( #layout_assertions __NEWLINE__ __NEWLINE__ )*
Marcel Hlopkoc6b726c2021-10-07 06:53:09 +00003849
Marcel Hlopkob8069ae2022-02-19 09:31:00 +00003850 __NEWLINE__
3851 __HASH_TOKEN__ pragma clang diagnostic pop __NEWLINE__
Marcel Hlopkoc6b726c2021-10-07 06:53:09 +00003852 // To satisfy http://cs/symbol:devtools.metadata.Presubmit.CheckTerminatingNewline check.
3853 __NEWLINE__
Marcel Hlopko89547752021-12-10 09:39:41 +00003854 })
Marcel Hlopko3164eee2021-08-24 20:09:22 +00003855}
3856
Marcel Hlopko42abfc82021-08-09 07:03:17 +00003857#[cfg(test)]
3858mod tests {
Devin Jeanpierre45cb1162021-10-27 10:54:28 +00003859 use super::*;
Lukasz Anforowiczefd635d2022-09-22 10:12:49 -07003860 use ir_matchers::assert_ir_matches;
Lukasz Anforowiczedabf002022-04-28 06:45:09 -07003861 use ir_testing::{
3862 ir_from_cc, ir_from_cc_dependency, ir_record, make_ir_from_items, retrieve_func,
Martin Brænnee5ba6b62022-06-23 07:38:40 -07003863 with_lifetime_macros,
Lukasz Anforowiczedabf002022-04-28 06:45:09 -07003864 };
Lukasz Anforowiczb4d17782022-07-07 08:09:13 -07003865 use static_assertions::{assert_impl_all, assert_not_impl_any};
Marcel Hlopko89547752021-12-10 09:39:41 +00003866 use token_stream_matchers::{
Lukasz Anforowiczefd635d2022-09-22 10:12:49 -07003867 assert_cc_matches, assert_cc_not_matches, assert_rs_matches, assert_rs_not_matches,
Devin Jeanpierre7e9a1de2021-12-03 08:04:22 +00003868 };
Lukasz Anforowiczae9be082022-10-05 07:33:57 -07003869 use token_stream_printer::rs_tokens_to_formatted_string_for_tests;
Marcel Hlopko42abfc82021-08-09 07:03:17 +00003870
Devin Jeanpierree9850a72022-06-29 19:04:48 -07003871 fn generate_bindings_tokens(ir: Rc<IR>) -> Result<BindingsTokens> {
Michael VanBemmelf5cbdf42022-10-14 17:00:11 -07003872 super::generate_bindings_tokens(ir, "crubit/rs_bindings_support", &mut IgnoreErrors)
Lukasz Anforowiczdd907702022-05-06 09:24:07 -07003873 }
3874
Devin Jeanpierre409f6f62022-07-07 02:00:26 -07003875 fn db_from_cc(cc_src: &str) -> Result<Database> {
3876 let mut db = Database::default();
3877 db.set_ir(ir_from_cc(cc_src)?);
3878 Ok(db)
3879 }
3880
Marcel Hlopko42abfc82021-08-09 07:03:17 +00003881 #[test]
Marcel Hlopkob8069ae2022-02-19 09:31:00 +00003882 fn test_disable_thread_safety_warnings() -> Result<()> {
3883 let ir = ir_from_cc("inline void foo() {}")?;
Devin Jeanpierree9850a72022-06-29 19:04:48 -07003884 let rs_api_impl = generate_bindings_tokens(ir)?.rs_api_impl;
Marcel Hlopkob8069ae2022-02-19 09:31:00 +00003885 assert_cc_matches!(
3886 rs_api_impl,
3887 quote! {
3888 ...
3889 __HASH_TOKEN__ pragma clang diagnostic push
3890 __HASH_TOKEN__ pragma clang diagnostic ignored "-Wthread-safety-analysis"
3891 ...
3892
3893 __HASH_TOKEN__ pragma clang diagnostic pop
3894 ...
3895 }
3896 );
3897 Ok(())
3898 }
3899
3900 #[test]
Marcel Hlopko3b9bf9e2021-11-29 08:25:14 +00003901 // TODO(hlopko): Move this test to a more principled place where it can access
3902 // `ir_testing`.
3903 fn test_duplicate_decl_ids_err() {
3904 let mut r1 = ir_record("R1");
Lukasz Anforowicz14732b22022-06-02 09:11:08 -07003905 r1.id = ItemId::new_for_testing(42);
Marcel Hlopko3b9bf9e2021-11-29 08:25:14 +00003906 let mut r2 = ir_record("R2");
Lukasz Anforowicz14732b22022-06-02 09:11:08 -07003907 r2.id = ItemId::new_for_testing(42);
Marcel Hlopko3b9bf9e2021-11-29 08:25:14 +00003908 let result = make_ir_from_items([r1.into(), r2.into()]);
3909 assert!(result.is_err());
3910 assert!(result.unwrap_err().to_string().contains("Duplicate decl_id found in"));
3911 }
3912
3913 #[test]
Marcel Hlopko45fba972021-08-23 19:52:20 +00003914 fn test_simple_function() -> Result<()> {
Marcel Hlopko89547752021-12-10 09:39:41 +00003915 let ir = ir_from_cc("int Add(int a, int b);")?;
Devin Jeanpierree9850a72022-06-29 19:04:48 -07003916 let BindingsTokens { rs_api, rs_api_impl } = generate_bindings_tokens(ir)?;
Marcel Hlopko89547752021-12-10 09:39:41 +00003917 assert_rs_matches!(
3918 rs_api,
3919 quote! {
Michael Forsterdb8101a2021-10-08 06:56:03 +00003920 #[inline(always)]
Marcel Hlopko89547752021-12-10 09:39:41 +00003921 pub fn Add(a: i32, b: i32) -> i32 {
Rosica Dejanovska68c11ac2022-05-12 03:48:59 -07003922 unsafe { crate::detail::__rust_thunk___Z3Addii(a, b) }
Marcel Hlopko89547752021-12-10 09:39:41 +00003923 }
3924 }
3925 );
3926 assert_rs_matches!(
3927 rs_api,
3928 quote! {
Michael Forsterdb8101a2021-10-08 06:56:03 +00003929 mod detail {
Googler55647142022-01-11 12:37:39 +00003930 #[allow(unused_imports)]
Devin Jeanpierred4dde0e2021-10-13 20:48:25 +00003931 use super::*;
Michael Forsterdb8101a2021-10-08 06:56:03 +00003932 extern "C" {
3933 #[link_name = "_Z3Addii"]
Googlera675ae02021-12-07 08:04:59 +00003934 pub(crate) fn __rust_thunk___Z3Addii(a: i32, b: i32) -> i32;
Marcel Hlopko89547752021-12-10 09:39:41 +00003935 }
3936 }
3937 }
Marcel Hlopko42abfc82021-08-09 07:03:17 +00003938 );
Michael Forsterdb8101a2021-10-08 06:56:03 +00003939
Devin Jeanpierre4f06f832022-04-26 15:51:30 -07003940 assert_cc_not_matches!(rs_api_impl, quote! {__rust_thunk___Z3Addii});
Michael Forsterdb8101a2021-10-08 06:56:03 +00003941
Marcel Hlopko3164eee2021-08-24 20:09:22 +00003942 Ok(())
3943 }
3944
3945 #[test]
3946 fn test_inline_function() -> Result<()> {
Marcel Hlopko89547752021-12-10 09:39:41 +00003947 let ir = ir_from_cc("inline int Add(int a, int b);")?;
Devin Jeanpierree9850a72022-06-29 19:04:48 -07003948 let BindingsTokens { rs_api, rs_api_impl } = generate_bindings_tokens(ir)?;
Marcel Hlopko89547752021-12-10 09:39:41 +00003949 assert_rs_matches!(
3950 rs_api,
3951 quote! {
Michael Forsterdb8101a2021-10-08 06:56:03 +00003952 #[inline(always)]
Marcel Hlopko89547752021-12-10 09:39:41 +00003953 pub fn Add(a: i32, b: i32) -> i32 {
Rosica Dejanovska68c11ac2022-05-12 03:48:59 -07003954 unsafe { crate::detail::__rust_thunk___Z3Addii(a, b) }
Marcel Hlopko89547752021-12-10 09:39:41 +00003955 }
3956 }
3957 );
3958 assert_rs_matches!(
3959 rs_api,
3960 quote! {
Michael Forsterdb8101a2021-10-08 06:56:03 +00003961 mod detail {
Googler55647142022-01-11 12:37:39 +00003962 #[allow(unused_imports)]
Devin Jeanpierred4dde0e2021-10-13 20:48:25 +00003963 use super::*;
Michael Forsterdb8101a2021-10-08 06:56:03 +00003964 extern "C" {
Googlera675ae02021-12-07 08:04:59 +00003965 pub(crate) fn __rust_thunk___Z3Addii(a: i32, b: i32) -> i32;
Marcel Hlopko89547752021-12-10 09:39:41 +00003966 }
3967 }
3968 }
Marcel Hlopko3164eee2021-08-24 20:09:22 +00003969 );
3970
Marcel Hlopko89547752021-12-10 09:39:41 +00003971 assert_cc_matches!(
Devin Jeanpierre4f06f832022-04-26 15:51:30 -07003972 rs_api_impl,
Marcel Hlopko89547752021-12-10 09:39:41 +00003973 quote! {
Googlera675ae02021-12-07 08:04:59 +00003974 extern "C" int __rust_thunk___Z3Addii(int a, int b) {
Lukasz Anforowicz07b33902022-07-13 15:27:03 -07003975 return Add(a, b);
Marcel Hlopko3164eee2021-08-24 20:09:22 +00003976 }
Marcel Hlopko89547752021-12-10 09:39:41 +00003977 }
Marcel Hlopko3164eee2021-08-24 20:09:22 +00003978 );
Marcel Hlopko42abfc82021-08-09 07:03:17 +00003979 Ok(())
3980 }
Marcel Hlopkob4b28742021-09-15 12:45:20 +00003981
3982 #[test]
Marcel Hlopkoa0f38662021-12-03 08:45:26 +00003983 fn test_simple_function_with_types_from_other_target() -> Result<()> {
3984 let ir = ir_from_cc_dependency(
3985 "inline ReturnStruct DoSomething(ParamStruct param);",
Devin Jeanpierreb8ce2c12022-07-13 10:34:01 -07003986 "struct ReturnStruct final {}; struct ParamStruct final {};",
Marcel Hlopkoa0f38662021-12-03 08:45:26 +00003987 )?;
3988
Devin Jeanpierree9850a72022-06-29 19:04:48 -07003989 let BindingsTokens { rs_api, rs_api_impl } = generate_bindings_tokens(ir)?;
Marcel Hlopko89547752021-12-10 09:39:41 +00003990 assert_rs_matches!(
3991 rs_api,
3992 quote! {
Marcel Hlopkoa0f38662021-12-03 08:45:26 +00003993 #[inline(always)]
3994 pub fn DoSomething(param: dependency::ParamStruct)
Marcel Hlopko89547752021-12-10 09:39:41 +00003995 -> dependency::ReturnStruct {
Rosica Dejanovska68c11ac2022-05-12 03:48:59 -07003996 unsafe { crate::detail::__rust_thunk___Z11DoSomething11ParamStruct(param) }
Marcel Hlopko89547752021-12-10 09:39:41 +00003997 }
3998 }
3999 );
4000 assert_rs_matches!(
4001 rs_api,
4002 quote! {
4003 mod detail {
Googler55647142022-01-11 12:37:39 +00004004 #[allow(unused_imports)]
Marcel Hlopko89547752021-12-10 09:39:41 +00004005 use super::*;
4006 extern "C" {
Lukasz Anforowicz07b33902022-07-13 15:27:03 -07004007 pub(crate) fn __rust_thunk___Z11DoSomething11ParamStruct(
4008 param: dependency::ParamStruct) -> dependency::ReturnStruct;
Marcel Hlopko89547752021-12-10 09:39:41 +00004009 }
4010 }}
Marcel Hlopkoa0f38662021-12-03 08:45:26 +00004011 );
4012
Marcel Hlopko89547752021-12-10 09:39:41 +00004013 assert_cc_matches!(
Devin Jeanpierre4f06f832022-04-26 15:51:30 -07004014 rs_api_impl,
Marcel Hlopko89547752021-12-10 09:39:41 +00004015 quote! {
Lukasz Anforowicz07b33902022-07-13 15:27:03 -07004016 extern "C" struct ReturnStruct __rust_thunk___Z11DoSomething11ParamStruct(
4017 struct ParamStruct param) {
4018 return DoSomething(param);
Marcel Hlopkoa0f38662021-12-03 08:45:26 +00004019 }
Marcel Hlopko89547752021-12-10 09:39:41 +00004020 }
Marcel Hlopkoa0f38662021-12-03 08:45:26 +00004021 );
4022 Ok(())
4023 }
4024
4025 #[test]
Lukasz Anforowiczb1ff2e52022-05-16 10:54:23 -07004026 fn test_template_in_dependency_and_alias_in_current_target() -> Result<()> {
4027 // See also the test with the same name in `ir_from_cc_test.rs`.
4028 let ir = {
4029 let dependency_src = r#" #pragma clang lifetime_elision
4030 template <typename T>
4031 struct MyTemplate {
4032 T GetValue() { return field; }
4033 T field;
4034 }; "#;
4035 let current_target_src = r#" #pragma clang lifetime_elision
4036 using MyAliasOfTemplate = MyTemplate<int>; "#;
4037 ir_from_cc_dependency(current_target_src, dependency_src)?
4038 };
4039
Devin Jeanpierree9850a72022-06-29 19:04:48 -07004040 let BindingsTokens { rs_api, rs_api_impl } = generate_bindings_tokens(ir)?;
Lukasz Anforowiczb1ff2e52022-05-16 10:54:23 -07004041 assert_rs_matches!(
4042 rs_api,
4043 quote! {
4044 #[repr(C)]
4045 pub struct __CcTemplateInst10MyTemplateIiE {
4046 pub field: i32,
4047 }
4048 }
4049 );
4050 assert_rs_matches!(
4051 rs_api,
4052 quote! {
4053 impl __CcTemplateInst10MyTemplateIiE {
4054 #[inline(always)]
4055 pub fn GetValue<'a>(self: ... Pin<&'a mut Self>) -> i32 { unsafe {
Lukasz Anforowicz8d1e4322022-06-08 07:56:06 -07004056 crate::detail::__rust_thunk___ZN10MyTemplateIiE8GetValueEv__2f_2ftest_3atesting_5ftarget(
Lukasz Anforowiczb1ff2e52022-05-16 10:54:23 -07004057 self)
4058 }}
4059 }
4060 }
4061 );
4062 assert_rs_matches!(
4063 rs_api,
4064 quote! {
4065 pub type MyAliasOfTemplate = crate::__CcTemplateInst10MyTemplateIiE;
4066 }
4067 );
4068 assert_rs_matches!(
4069 rs_api,
4070 quote! {
Lukasz Anforowicz8d1e4322022-06-08 07:56:06 -07004071 mod detail { ... extern "C" {
Lukasz Anforowiczb1ff2e52022-05-16 10:54:23 -07004072 ...
Lukasz Anforowicz8d1e4322022-06-08 07:56:06 -07004073 pub(crate) fn
4074 __rust_thunk___ZN10MyTemplateIiE8GetValueEv__2f_2ftest_3atesting_5ftarget<'a>(
4075 __this: ... Pin<&'a mut crate::__CcTemplateInst10MyTemplateIiE>
4076 ) -> i32;
4077 ...
4078 } }
Lukasz Anforowiczb1ff2e52022-05-16 10:54:23 -07004079 }
4080 );
4081 assert_cc_matches!(
4082 rs_api_impl,
4083 quote! {
Lukasz Anforowicz8d1e4322022-06-08 07:56:06 -07004084 extern "C"
4085 int __rust_thunk___ZN10MyTemplateIiE8GetValueEv__2f_2ftest_3atesting_5ftarget(
Lukasz Anforowiczd4742ff2022-07-11 17:05:02 -07004086 struct MyTemplate<int>* __this) {
Lukasz Anforowiczb1ff2e52022-05-16 10:54:23 -07004087 return __this->GetValue();
4088 }
4089 }
4090 );
4091
4092 Ok(())
4093 }
4094
4095 #[test]
4096 fn test_template_with_out_of_line_definition() -> Result<()> {
4097 // See also an end-to-end test in the `test/templates/out_of_line_definition`
4098 // directory.
4099 let ir = ir_from_cc(
4100 r#" #pragma clang lifetime_elision
4101 template <typename T>
4102 class MyTemplate final {
4103 public:
4104 static MyTemplate Create(T value);
4105 const T& value() const;
4106
4107 private:
4108 T value_;
4109 };
4110
4111 using MyTypeAlias = MyTemplate<int>; "#,
4112 )?;
4113
Devin Jeanpierree9850a72022-06-29 19:04:48 -07004114 let BindingsTokens { rs_api_impl, .. } = generate_bindings_tokens(ir)?;
Lukasz Anforowiczb1ff2e52022-05-16 10:54:23 -07004115
4116 // Even though the member functions above are *not* defined inline (e.g.
4117 // IR::Func::is_inline is false), they still need to have thunks generated for
4118 // them (to force/guarantee that the class template and its members get
4119 // instantiated). This is also covered in the following end-to-end
4120 // tests:
4121 // - test/templates/out_of_line_definition/ - without a thunk, the template
4122 // won't be instantiated and Rust bindings won't be able to call the member
4123 // function (there will be no instantiation of the member function in the C++
4124 // object files)
4125 // - test/templates/definition_in_cc/ - the instantiation happens in the .cc
4126 // file and therefore the thunk is not *required* (but it doesn't hurt to have
4127 // the thunk)
4128 assert_cc_matches!(
4129 rs_api_impl,
4130 quote! {
4131 extern "C" class MyTemplate<int>
Lukasz Anforowicz8d1e4322022-06-08 07:56:06 -07004132 __rust_thunk___ZN10MyTemplateIiE6CreateEi__2f_2ftest_3atesting_5ftarget(
4133 int value) {
Lukasz Anforowicz07b33902022-07-13 15:27:03 -07004134 return MyTemplate<int>::Create(value);
Lukasz Anforowiczb1ff2e52022-05-16 10:54:23 -07004135 }
4136 }
4137 );
4138 assert_cc_matches!(
4139 rs_api_impl,
4140 quote! {
Lukasz Anforowicz07b33902022-07-13 15:27:03 -07004141 extern "C" int const*
Lukasz Anforowicz8d1e4322022-06-08 07:56:06 -07004142 __rust_thunk___ZNK10MyTemplateIiE5valueEv__2f_2ftest_3atesting_5ftarget(
Lukasz Anforowiczb1ff2e52022-05-16 10:54:23 -07004143 const class MyTemplate<int>*__this) {
Lukasz Anforowicz07b33902022-07-13 15:27:03 -07004144 return &__this->value();
Lukasz Anforowiczb1ff2e52022-05-16 10:54:23 -07004145 }
4146 }
4147 );
4148 Ok(())
4149 }
4150
4151 #[test]
Marcel Hlopkob4b28742021-09-15 12:45:20 +00004152 fn test_simple_struct() -> Result<()> {
Devin Jeanpierree9be70a2022-07-25 08:58:54 -07004153 let ir = ir_from_cc(
4154 r#"
4155 #pragma clang lifetime_elision
Devin Jeanpierre88343c72022-01-15 01:10:23 +00004156 struct SomeStruct final {
Devin Jeanpierree9be70a2022-07-25 08:58:54 -07004157 ~SomeStruct() {}
Marcel Hlopko89547752021-12-10 09:39:41 +00004158 int public_int;
4159 protected:
4160 int protected_int;
4161 private:
4162 int private_int;
4163 };
Devin Jeanpierree9be70a2022-07-25 08:58:54 -07004164 "#,
4165 )?;
Michael Forster028800b2021-10-05 12:39:59 +00004166
Devin Jeanpierree9850a72022-06-29 19:04:48 -07004167 let BindingsTokens { rs_api, rs_api_impl } = generate_bindings_tokens(ir)?;
Marcel Hlopko89547752021-12-10 09:39:41 +00004168 assert_rs_matches!(
4169 rs_api,
4170 quote! {
Devin Jeanpierree9be70a2022-07-25 08:58:54 -07004171 #[::ctor::recursively_pinned(PinnedDrop)]
Lukasz Anforowiczdf8fcae2022-06-02 14:54:43 -07004172 #[repr(C, align(4))]
Michael Forsterdb8101a2021-10-08 06:56:03 +00004173 pub struct SomeStruct {
Devin Jeanpierrecd5ebf32022-07-08 05:31:55 -07004174 __non_field_data: [::std::mem::MaybeUninit<u8>; 0],
Michael Forsterdb8101a2021-10-08 06:56:03 +00004175 pub public_int: i32,
Lukasz Anforowiczdf8fcae2022-06-02 14:54:43 -07004176 #[doc = " Reason for representing this field as a blob of bytes:\n Types of non-public C++ fields can be elided away"]
Devin Jeanpierrecd5ebf32022-07-08 05:31:55 -07004177 pub(crate) protected_int: [::std::mem::MaybeUninit<u8>; 4],
Lukasz Anforowiczdf8fcae2022-06-02 14:54:43 -07004178 #[doc = " Reason for representing this field as a blob of bytes:\n Types of non-public C++ fields can be elided away"]
Devin Jeanpierrecd5ebf32022-07-08 05:31:55 -07004179 pub(crate) private_int: [::std::mem::MaybeUninit<u8>; 4],
Marcel Hlopko89547752021-12-10 09:39:41 +00004180 }
4181 }
4182 );
4183 assert_rs_matches!(
4184 rs_api,
4185 quote! {
Devin Jeanpierrecd5ebf32022-07-08 05:31:55 -07004186 const _: () = assert!(::std::mem::size_of::<Option<&i32>>() == ::std::mem::size_of::<&i32>());
4187 const _: () = assert!(::std::mem::size_of::<crate::SomeStruct>() == 12);
4188 const _: () = assert!(::std::mem::align_of::<crate::SomeStruct>() == 4);
Devin Jeanpierree9be70a2022-07-25 08:58:54 -07004189 const _: () = { static_assertions::assert_not_impl_any!(crate::SomeStruct: Copy); };
4190 const _: () = { static_assertions::assert_impl_all!(crate::SomeStruct: Drop); };
Lukasz Anforowiczed94b242022-09-07 11:47:58 -07004191 const _: () = assert!(memoffset::offset_of!(crate::SomeStruct, public_int) == 0);
4192 const _: () = assert!(memoffset::offset_of!(crate::SomeStruct, protected_int) == 4);
4193 const _: () = assert!(memoffset::offset_of!(crate::SomeStruct, private_int) == 8);
Marcel Hlopko89547752021-12-10 09:39:41 +00004194 }
Marcel Hlopkob4b28742021-09-15 12:45:20 +00004195 );
Marcel Hlopko89547752021-12-10 09:39:41 +00004196 assert_cc_matches!(
4197 rs_api_impl,
4198 quote! {
Lukasz Anforowiczd4742ff2022-07-11 17:05:02 -07004199 extern "C" void __rust_thunk___ZN10SomeStructD1Ev(struct SomeStruct * __this) {
Lukasz Anforowicz07b33902022-07-13 15:27:03 -07004200 std::destroy_at(__this);
Marcel Hlopko89547752021-12-10 09:39:41 +00004201 }
4202 }
4203 );
4204 assert_cc_matches!(
4205 rs_api_impl,
4206 quote! {
Lukasz Anforowiczd4742ff2022-07-11 17:05:02 -07004207 static_assert(sizeof(struct SomeStruct) == 12);
4208 static_assert(alignof(struct SomeStruct) == 4);
4209 static_assert(CRUBIT_OFFSET_OF(public_int, struct SomeStruct) == 0);
Marcel Hlopko89547752021-12-10 09:39:41 +00004210 }
Googler5ea88642021-09-29 08:05:59 +00004211 );
Marcel Hlopkob4b28742021-09-15 12:45:20 +00004212 Ok(())
4213 }
Devin Jeanpierre7a7328e2021-09-17 07:10:08 +00004214
Devin Jeanpierre2ed14ec2021-10-06 11:32:19 +00004215 #[test]
Lukasz Anforowiczd4742ff2022-07-11 17:05:02 -07004216 fn test_struct_vs_class() -> Result<()> {
Devin Jeanpierree9be70a2022-07-25 08:58:54 -07004217 let ir = ir_from_cc(
4218 r#"
4219 #pragma clang lifetime_elision
4220 struct SomeStruct final {
4221 SomeStruct() {}
4222 int field;
4223 };
4224 class SomeClass final {
4225 public:
4226 SomeClass() {}
4227 int field;
4228 };
4229 "#,
4230 )?;
Lukasz Anforowiczd4742ff2022-07-11 17:05:02 -07004231 let BindingsTokens { rs_api, rs_api_impl } = generate_bindings_tokens(ir)?;
4232
4233 // A Rust `struct` is generated for both `SomeStruct` and `SomeClass`.
4234 assert_rs_matches!(rs_api, quote! { pub struct SomeStruct },);
4235 assert_rs_matches!(rs_api, quote! { pub struct SomeClass },);
4236
4237 // But in C++ we still should refer to `struct SomeStruct` and `class
4238 // SomeClass`. See also b/238212337.
4239 assert_cc_matches!(rs_api_impl, quote! { struct SomeStruct * __this });
4240 assert_cc_matches!(rs_api_impl, quote! { class SomeClass * __this });
4241 assert_cc_matches!(rs_api_impl, quote! { static_assert(sizeof(struct SomeStruct) == 4); });
4242 assert_cc_matches!(rs_api_impl, quote! { static_assert(sizeof(class SomeClass) == 4); });
4243 Ok(())
4244 }
4245
4246 #[test]
Kinuko Yasuda8dd84642022-08-17 09:19:47 -07004247 fn test_struct_vs_typedefed_struct() -> Result<()> {
4248 let ir = ir_from_cc(
4249 r#"
4250 #pragma clang lifetime_elision
4251 struct SomeStruct final {
4252 int x;
4253 } __attribute__((aligned(16)));
4254 typedef struct {
4255 int x;
4256 } SomeAnonStruct __attribute__((aligned(16)));
4257 "#,
4258 )?;
4259 let BindingsTokens { rs_api, rs_api_impl } = generate_bindings_tokens(ir)?;
4260
4261 // A `struct` is generated for both `SomeStruct` and `SomeAnonStruct`, both
4262 // in Rust and in C++.
4263 assert_rs_matches!(rs_api, quote! { pub struct SomeStruct },);
4264 assert_rs_matches!(rs_api, quote! { pub struct SomeAnonStruct },);
4265 assert_rs_matches!(rs_api_impl, quote! { struct SomeStruct * __this },);
4266 assert_rs_matches!(rs_api_impl, quote! { SomeAnonStruct * __this },);
4267
4268 // In C++, both have align == 16, but size for `SomeAnonStruct` is not aligned.
4269 // `SomeAnonStruct` won't have `struct` in the assert.
4270 assert_cc_matches!(
4271 rs_api_impl,
4272 quote! { static_assert(alignof(struct SomeStruct) == 16); }
4273 );
4274 assert_cc_matches!(rs_api_impl, quote! { static_assert(alignof(SomeAnonStruct) == 16); });
4275 assert_cc_matches!(rs_api_impl, quote! { static_assert(sizeof(struct SomeStruct) == 16); });
4276 assert_cc_matches!(rs_api_impl, quote! { static_assert(sizeof(SomeAnonStruct) == 4); });
4277
4278 // In Rust, both have align == 16 and size == 16.
4279 assert_rs_matches!(
4280 rs_api,
4281 quote! { assert!(::std::mem::size_of::<crate::SomeStruct>() == 16); }
4282 );
4283 assert_rs_matches!(
4284 rs_api,
4285 quote! { assert!(::std::mem::align_of::<crate::SomeStruct>() == 16); }
4286 );
4287 assert_rs_matches!(
4288 rs_api,
4289 quote! { assert!(::std::mem::size_of::<crate::SomeAnonStruct>() == 16); }
4290 );
4291 assert_rs_matches!(
4292 rs_api,
4293 quote! { assert!(::std::mem::align_of::<crate::SomeAnonStruct>() == 16); }
4294 );
4295
4296 Ok(())
4297 }
4298
4299 #[test]
Devin Jeanpierre5fecde52022-09-14 06:53:39 -07004300 fn test_typedef_member() -> Result<()> {
4301 let ir = ir_from_cc(
4302 r#"
4303 struct SomeStruct final {
4304 typedef int Type;
4305 };
4306 inline SomeStruct::Type Function() {return 0;}
4307 "#,
4308 )?;
4309 let BindingsTokens { rs_api, rs_api_impl } = generate_bindings_tokens(ir)?;
4310 // TODO(b/200067824): This should use the alias's real name in Rust, as well.
4311 assert_rs_matches!(rs_api, quote! { pub fn Function() -> i32 { ... } },);
4312
4313 assert_cc_matches!(
4314 rs_api_impl,
4315 quote! {
4316 extern "C" SomeStruct::Type __rust_thunk___Z8Functionv(){ return Function(); }
4317 },
4318 );
4319 Ok(())
4320 }
4321
4322 #[test]
Lukasz Anforowicz275fa922022-01-05 16:13:20 +00004323 fn test_ref_to_struct_in_thunk_impls() -> Result<()> {
Lukasz Anforowiczd4742ff2022-07-11 17:05:02 -07004324 let ir = ir_from_cc("struct S{}; inline void foo(S& s) {} ")?;
Devin Jeanpierree9850a72022-06-29 19:04:48 -07004325 let rs_api_impl = generate_bindings_tokens(ir)?.rs_api_impl;
Lukasz Anforowicz275fa922022-01-05 16:13:20 +00004326 assert_cc_matches!(
4327 rs_api_impl,
4328 quote! {
Lukasz Anforowicz07b33902022-07-13 15:27:03 -07004329 extern "C" void __rust_thunk___Z3fooR1S(struct S* s) {
4330 foo(*s);
Lukasz Anforowicz275fa922022-01-05 16:13:20 +00004331 }
4332 }
4333 );
4334 Ok(())
4335 }
4336
4337 #[test]
4338 fn test_const_ref_to_struct_in_thunk_impls() -> Result<()> {
Lukasz Anforowiczd4742ff2022-07-11 17:05:02 -07004339 let ir = ir_from_cc("struct S{}; inline void foo(const S& s) {} ")?;
Devin Jeanpierree9850a72022-06-29 19:04:48 -07004340 let rs_api_impl = generate_bindings_tokens(ir)?.rs_api_impl;
Lukasz Anforowicz275fa922022-01-05 16:13:20 +00004341 assert_cc_matches!(
4342 rs_api_impl,
4343 quote! {
Lukasz Anforowicz07b33902022-07-13 15:27:03 -07004344 extern "C" void __rust_thunk___Z3fooRK1S(const struct S* s) {
4345 foo(*s);
Lukasz Anforowicz275fa922022-01-05 16:13:20 +00004346 }
4347 }
4348 );
4349 Ok(())
4350 }
4351
4352 #[test]
Lukasz Anforowicz957cbf22022-01-05 16:14:05 +00004353 fn test_unsigned_int_in_thunk_impls() -> Result<()> {
4354 let ir = ir_from_cc("inline void foo(unsigned int i) {} ")?;
Devin Jeanpierree9850a72022-06-29 19:04:48 -07004355 let rs_api_impl = generate_bindings_tokens(ir)?.rs_api_impl;
Lukasz Anforowicz957cbf22022-01-05 16:14:05 +00004356 assert_cc_matches!(
4357 rs_api_impl,
4358 quote! {
4359 extern "C" void __rust_thunk___Z3fooj(unsigned int i) {
Lukasz Anforowicz07b33902022-07-13 15:27:03 -07004360 foo(i);
Lukasz Anforowicz957cbf22022-01-05 16:14:05 +00004361 }
4362 }
4363 );
4364 Ok(())
4365 }
4366
4367 #[test]
Marcel Hlopkodd1fcb12021-12-22 14:13:59 +00004368 fn test_record_static_methods_qualify_call_in_thunk() -> Result<()> {
Lukasz Anforowiczae9be082022-10-05 07:33:57 -07004369 let ir = ir_from_cc(
4370 r#"
Marcel Hlopkodd1fcb12021-12-22 14:13:59 +00004371 struct SomeStruct {
4372 static inline int some_func() { return 42; }
Lukasz Anforowiczae9be082022-10-05 07:33:57 -07004373 }; "#,
4374 )?;
Marcel Hlopkodd1fcb12021-12-22 14:13:59 +00004375
4376 assert_cc_matches!(
Devin Jeanpierree9850a72022-06-29 19:04:48 -07004377 generate_bindings_tokens(ir)?.rs_api_impl,
Marcel Hlopkodd1fcb12021-12-22 14:13:59 +00004378 quote! {
4379 extern "C" int __rust_thunk___ZN10SomeStruct9some_funcEv() {
4380 return SomeStruct::some_func();
4381 }
4382 }
4383 );
4384 Ok(())
4385 }
4386
4387 #[test]
Lukasz Anforowiczb3d89aa2022-01-12 14:35:52 +00004388 fn test_record_instance_methods_deref_this_in_thunk() -> Result<()> {
Lukasz Anforowiczae9be082022-10-05 07:33:57 -07004389 let ir = ir_from_cc(
4390 r#"
Lukasz Anforowiczb3d89aa2022-01-12 14:35:52 +00004391 struct SomeStruct {
4392 inline int some_func(int arg) const { return 42 + arg; }
Lukasz Anforowiczae9be082022-10-05 07:33:57 -07004393 }; "#,
4394 )?;
Lukasz Anforowiczb3d89aa2022-01-12 14:35:52 +00004395
4396 assert_cc_matches!(
Devin Jeanpierree9850a72022-06-29 19:04:48 -07004397 generate_bindings_tokens(ir)?.rs_api_impl,
Lukasz Anforowiczb3d89aa2022-01-12 14:35:52 +00004398 quote! {
4399 extern "C" int __rust_thunk___ZNK10SomeStruct9some_funcEi(
Lukasz Anforowiczd4742ff2022-07-11 17:05:02 -07004400 const struct SomeStruct* __this, int arg) {
Lukasz Anforowicz07b33902022-07-13 15:27:03 -07004401 return __this->some_func(arg);
Lukasz Anforowiczb3d89aa2022-01-12 14:35:52 +00004402 }
4403 }
4404 );
4405 Ok(())
4406 }
4407
4408 #[test]
Lukasz Anforowiczfea0db92022-05-17 17:28:04 -07004409 fn test_record_with_unsupported_field_type() -> Result<()> {
4410 // Using a nested struct because it's currently not supported.
4411 // But... any other unsupported type would also work for this test.
4412 let ir = ir_from_cc(
4413 r#"
4414 struct StructWithUnsupportedField {
4415 struct NestedStruct {
4416 int nested_field;
4417 };
4418
4419 // Doc comment for `my_field`.
4420 NestedStruct my_field;
4421 };
4422 "#,
4423 )?;
Devin Jeanpierree9850a72022-06-29 19:04:48 -07004424 let rs_api = generate_bindings_tokens(ir)?.rs_api;
Lukasz Anforowiczfea0db92022-05-17 17:28:04 -07004425 assert_rs_matches!(
4426 rs_api,
4427 quote! {
4428 #[repr(C, align(4))]
4429 pub struct StructWithUnsupportedField {
4430 #[doc = " Doc comment for `my_field`.\n \n Reason for representing this field as a blob of bytes:\n Unsupported type 'struct StructWithUnsupportedField::NestedStruct': No generated bindings found for 'NestedStruct'"]
Devin Jeanpierrecd5ebf32022-07-08 05:31:55 -07004431 pub(crate) my_field: [::std::mem::MaybeUninit<u8>; 4],
Lukasz Anforowiczfea0db92022-05-17 17:28:04 -07004432 }
4433 ...
4434 const _: () = assert!(
Lukasz Anforowiczed94b242022-09-07 11:47:58 -07004435 memoffset::offset_of!(crate::StructWithUnsupportedField, my_field) == 0);
Lukasz Anforowiczfea0db92022-05-17 17:28:04 -07004436 }
4437 );
4438 Ok(())
4439 }
4440
4441 #[test]
Lukasz Anforowicze200e8a2022-05-18 12:36:33 -07004442 fn test_struct_with_unnamed_bitfield_member() -> Result<()> {
4443 // This test input causes `field_decl->getName()` to return an empty string.
4444 // This example is based on `struct timex` from
4445 // /usr/grte/v5/include/bits/timex.h
4446 let ir = ir_from_cc(
4447 r#"
4448 struct SomeStruct {
4449 int first_field;
4450 int :32;
4451 int last_field;
4452 }; "#,
4453 )?;
Devin Jeanpierree9850a72022-06-29 19:04:48 -07004454 let BindingsTokens { rs_api, .. } = generate_bindings_tokens(ir)?;
Lukasz Anforowicze200e8a2022-05-18 12:36:33 -07004455 assert_rs_matches!(
4456 rs_api,
4457 quote! {
4458 #[repr(C)]
4459 pub struct SomeStruct {
Michael Forster82c02d32022-05-20 21:47:33 -07004460 pub first_field: i32, ...
Devin Jeanpierrecd5ebf32022-07-08 05:31:55 -07004461 __bitfields1: [::std::mem::MaybeUninit<u8>; 4],
Lukasz Anforowicze200e8a2022-05-18 12:36:33 -07004462 pub last_field: i32,
4463 }
4464 ...
Lukasz Anforowiczed94b242022-09-07 11:47:58 -07004465 const _: () = assert!(memoffset::offset_of!(crate::SomeStruct, first_field) == 0);
4466 const _: () = assert!(memoffset::offset_of!(crate::SomeStruct, last_field) == 8);
Lukasz Anforowicze200e8a2022-05-18 12:36:33 -07004467 }
4468 );
4469 Ok(())
4470 }
4471
4472 #[test]
Kinuko Yasuda6ff59f12022-08-11 08:41:45 -07004473 fn test_struct_with_inheritable_field() -> Result<()> {
4474 let ir = ir_from_cc(
4475 r#"
4476 struct TrivialButInheritable {
4477 int x;
4478 };
4479 struct StructWithInheritable final {
4480 TrivialButInheritable t;
4481 };
4482 "#,
4483 )?;
4484 let rs_api = generate_bindings_tokens(ir)?.rs_api;
4485 assert_rs_not_matches!(rs_api, quote! {derive ( ... Copy ... )});
4486 assert_rs_not_matches!(rs_api, quote! {derive ( ... Clone ... )});
4487 Ok(())
4488 }
4489
4490 #[test]
4491 fn test_union_with_inheritable_field() -> Result<()> {
4492 let ir = ir_from_cc(
4493 r#"
4494 struct TrivialButInheritable {
4495 int x;
4496 };
4497 union UnionWithInheritable {
4498 TrivialButInheritable t;
4499 };
4500 "#,
4501 )?;
4502 let rs_api = generate_bindings_tokens(ir)?.rs_api;
4503 assert_rs_not_matches!(rs_api, quote! {derive ( ... Copy ... )});
4504 assert_rs_not_matches!(rs_api, quote! {derive ( ... Clone ... )});
4505 Ok(())
4506 }
4507
Devin Jeanpierrea99e0a32022-08-17 01:36:58 -07004508 /// Classes with a non-public destructor shouldn't be constructible, not
4509 /// even via Copy/Clone.
4510 #[test]
4511 fn test_trivial_nonpublic_destructor() -> Result<()> {
4512 let ir = ir_from_cc(
Devin Jeanpierreccb67672022-08-17 10:05:47 -07004513 r#"#pragma clang lifetime_elision
Devin Jeanpierrea99e0a32022-08-17 01:36:58 -07004514 struct Indestructible final {
4515 Indestructible() = default;
4516 Indestructible(int);
4517 Indestructible(const Indestructible&) = default;
Devin Jeanpierreccb67672022-08-17 10:05:47 -07004518 void Foo() const;
Devin Jeanpierrea99e0a32022-08-17 01:36:58 -07004519 private:
4520 ~Indestructible() = default;
4521 };
4522
4523 Indestructible ReturnsValue();
4524 void TakesValue(Indestructible);
Devin Jeanpierreccb67672022-08-17 10:05:47 -07004525 void TakesReference(const Indestructible& x);
Devin Jeanpierrea99e0a32022-08-17 01:36:58 -07004526 "#,
4527 )?;
4528 let rs_api = generate_bindings_tokens(ir)?.rs_api;
Devin Jeanpierreccb67672022-08-17 10:05:47 -07004529 // It isn't available by value:
Devin Jeanpierrea99e0a32022-08-17 01:36:58 -07004530 assert_rs_not_matches!(rs_api, quote! {Default});
4531 assert_rs_not_matches!(rs_api, quote! {From});
4532 assert_rs_not_matches!(rs_api, quote! {derive ( ... Copy ... )});
4533 assert_rs_not_matches!(rs_api, quote! {derive ( ... Clone ... )});
4534 assert_rs_not_matches!(rs_api, quote! {ReturnsValue});
4535 assert_rs_not_matches!(rs_api, quote! {TakesValue});
Devin Jeanpierreccb67672022-08-17 10:05:47 -07004536 // ... but it is otherwise available:
4537 assert_rs_matches!(rs_api, quote! {struct Indestructible});
4538 assert_rs_matches!(rs_api, quote! {fn Foo<'a>(&'a self)});
4539 assert_rs_matches!(rs_api, quote! {fn TakesReference<'a>(x: &'a crate::Indestructible)});
Devin Jeanpierrea99e0a32022-08-17 01:36:58 -07004540 Ok(())
4541 }
4542
4543 #[test]
4544 fn test_nontrivial_nonpublic_destructor() -> Result<()> {
4545 let ir = ir_from_cc(
Devin Jeanpierreccb67672022-08-17 10:05:47 -07004546 r#"#pragma clang lifetime_elision
Devin Jeanpierrea99e0a32022-08-17 01:36:58 -07004547 struct Indestructible final {
4548 Indestructible() = default;
4549 Indestructible(int);
4550 Indestructible(const Indestructible&) = default;
Devin Jeanpierreccb67672022-08-17 10:05:47 -07004551 void Foo() const;
Devin Jeanpierrea99e0a32022-08-17 01:36:58 -07004552 private:
4553 ~Indestructible() {}
4554 };
4555
4556 Indestructible ReturnsValue();
4557 void TakesValue(Indestructible);
Devin Jeanpierreccb67672022-08-17 10:05:47 -07004558 void TakesReference(const Indestructible& x);
4559 "#,
4560 )?;
4561 let rs_api = generate_bindings_tokens(ir)?.rs_api;
4562 // It isn't available by value:
4563 assert_rs_not_matches!(rs_api, quote! {CtorNew});
4564 assert_rs_not_matches!(rs_api, quote! {ReturnsValue});
4565 assert_rs_not_matches!(rs_api, quote! {TakesValue});
4566 // ... but it is otherwise available:
4567 assert_rs_matches!(rs_api, quote! {struct Indestructible});
4568 assert_rs_matches!(rs_api, quote! {fn Foo<'a>(&'a self)});
4569 assert_rs_matches!(rs_api, quote! {fn TakesReference<'a>(x: &'a crate::Indestructible)});
4570 Ok(())
4571 }
4572
4573 /// trivial abstract structs shouldn't be constructible, not even via
4574 /// Copy/Clone.
4575 ///
4576 /// Right now, a struct can only be Copy/Clone if it's final, but that
4577 /// restriction will likely be lifted later.
4578 #[test]
4579 fn test_trivial_abstract_by_value() -> Result<()> {
4580 let ir = ir_from_cc(
4581 r#"#pragma clang lifetime_elision
4582 struct Abstract final {
4583 Abstract() = default;
4584 Abstract(int);
4585 Abstract(const Abstract&) = default;
4586 virtual void Foo() const = 0;
4587 void Nonvirtual() const;
4588 };
4589 void TakesAbstract(const Abstract& a);
4590 "#,
4591 )?;
4592 let rs_api = generate_bindings_tokens(ir)?.rs_api;
4593 // It isn't available by value:
4594 assert_rs_not_matches!(rs_api, quote! {Default});
4595 assert_rs_not_matches!(rs_api, quote! {From});
4596 assert_rs_not_matches!(rs_api, quote! {derive ( ... Copy ... )});
4597 assert_rs_not_matches!(rs_api, quote! {derive ( ... Clone ... )});
4598 // ... but it is otherwise available:
4599 assert_rs_matches!(rs_api, quote! {struct Abstract});
4600 assert_rs_matches!(rs_api, quote! {fn Foo<'a>(&'a self)});
4601 assert_rs_matches!(rs_api, quote! {fn Nonvirtual<'a>(&'a self)});
4602 assert_rs_matches!(rs_api, quote! {fn TakesAbstract<'a>(a: &'a crate::Abstract)});
4603 Ok(())
4604 }
4605
4606 #[test]
4607 fn test_nontrivial_abstract_by_value() -> Result<()> {
4608 let ir = ir_from_cc(
4609 r#"#pragma clang lifetime_elision
4610 struct Abstract final {
4611 Abstract() {};
4612 Abstract(int);
4613 Abstract(const Abstract&) {}
4614 virtual void Foo() const = 0;
4615 void Nonvirtual() const;
4616 };
4617 void TakesAbstract(const Abstract& a);
Devin Jeanpierrea99e0a32022-08-17 01:36:58 -07004618 "#,
4619 )?;
4620 let rs_api = generate_bindings_tokens(ir)?.rs_api;
4621 assert_rs_not_matches!(rs_api, quote! {CtorNew});
Devin Jeanpierreccb67672022-08-17 10:05:47 -07004622 // ... but it is otherwise available:
4623 assert_rs_matches!(rs_api, quote! {struct Abstract});
4624 assert_rs_matches!(rs_api, quote! {fn Foo<'a>(&'a self)});
4625 assert_rs_matches!(rs_api, quote! {fn Nonvirtual<'a>(&'a self)});
4626 assert_rs_matches!(rs_api, quote! {fn TakesAbstract<'a>(a: &'a crate::Abstract)});
Devin Jeanpierrea99e0a32022-08-17 01:36:58 -07004627 Ok(())
4628 }
4629
Kinuko Yasuda6ff59f12022-08-11 08:41:45 -07004630 #[test]
Lukasz Anforowicze200e8a2022-05-18 12:36:33 -07004631 fn test_struct_with_unnamed_struct_and_union_members() -> Result<()> {
4632 // This test input causes `field_decl->getName()` to return an empty string.
4633 // See also:
4634 // - https://en.cppreference.com/w/c/language/struct: "[...] an unnamed member
4635 // of a struct whose type is a struct without name is known as anonymous
4636 // struct."
4637 // - https://rust-lang.github.io/rfcs/2102-unnamed-fields.html
4638 let ir = ir_from_cc(
4639 r#"
4640 struct StructWithUnnamedMembers {
4641 int first_field;
4642
4643 struct {
4644 int anonymous_struct_field_1;
4645 int anonymous_struct_field_2;
4646 };
4647 union {
4648 int anonymous_union_field_1;
4649 int anonymous_union_field_2;
4650 };
4651
4652 int last_field;
4653 }; "#,
4654 )?;
Devin Jeanpierree9850a72022-06-29 19:04:48 -07004655 let BindingsTokens { rs_api, .. } = generate_bindings_tokens(ir)?;
Lukasz Anforowicze200e8a2022-05-18 12:36:33 -07004656 // TODO(b/200067824): Once nested structs anhd unions are supported,
4657 // `__unnamed_field1` and `__unnamed_field2` should have a real, usable
4658 // type.
4659 assert_rs_matches!(
4660 rs_api,
4661 quote! {
4662 #[repr(C, align(4))]
4663 pub struct StructWithUnnamedMembers {
4664 pub first_field: i32,
Googler84cd5602022-10-07 08:25:46 -07004665 #[doc=" Reason for representing this field as a blob of bytes:\n Unsupported type 'struct StructWithUnnamedMembers::(anonymous at ./ir_from_cc_virtual_header.h:7:15)': No generated bindings found for ''"]
Devin Jeanpierrecd5ebf32022-07-08 05:31:55 -07004666 pub(crate) __unnamed_field1: [::std::mem::MaybeUninit<u8>; 8],
Googler84cd5602022-10-07 08:25:46 -07004667 #[doc=" Reason for representing this field as a blob of bytes:\n Unsupported type 'union StructWithUnnamedMembers::(anonymous at ./ir_from_cc_virtual_header.h:11:15)': No generated bindings found for ''"]
Devin Jeanpierrecd5ebf32022-07-08 05:31:55 -07004668 pub(crate) __unnamed_field2: [::std::mem::MaybeUninit<u8>; 4],
Lukasz Anforowicze200e8a2022-05-18 12:36:33 -07004669 pub last_field: i32,
4670 }
4671 ...
Lukasz Anforowiczed94b242022-09-07 11:47:58 -07004672 const _: () = assert!(memoffset::offset_of!(
Lukasz Anforowicz30360ba2022-05-23 12:15:12 -07004673 crate::StructWithUnnamedMembers, first_field) == 0);
Lukasz Anforowiczed94b242022-09-07 11:47:58 -07004674 const _: () = assert!(memoffset::offset_of!(
Lukasz Anforowicz30360ba2022-05-23 12:15:12 -07004675 crate::StructWithUnnamedMembers, __unnamed_field1) == 4);
Lukasz Anforowiczed94b242022-09-07 11:47:58 -07004676 const _: () = assert!(memoffset::offset_of!(
Lukasz Anforowicz30360ba2022-05-23 12:15:12 -07004677 crate::StructWithUnnamedMembers, __unnamed_field2) == 12);
Lukasz Anforowiczed94b242022-09-07 11:47:58 -07004678 const _: () = assert!(memoffset::offset_of!(
Lukasz Anforowicz30360ba2022-05-23 12:15:12 -07004679 crate::StructWithUnnamedMembers, last_field) == 16);
Lukasz Anforowicze200e8a2022-05-18 12:36:33 -07004680 }
4681 );
4682 Ok(())
4683 }
4684
4685 #[test]
Marcel Hlopkoa0f38662021-12-03 08:45:26 +00004686 fn test_struct_from_other_target() -> Result<()> {
4687 let ir = ir_from_cc_dependency("// intentionally empty", "struct SomeStruct {};")?;
Devin Jeanpierree9850a72022-06-29 19:04:48 -07004688 let BindingsTokens { rs_api, rs_api_impl } = generate_bindings_tokens(ir)?;
Devin Jeanpierre4f06f832022-04-26 15:51:30 -07004689 assert_rs_not_matches!(rs_api, quote! { SomeStruct });
4690 assert_cc_not_matches!(rs_api_impl, quote! { SomeStruct });
Marcel Hlopkoa0f38662021-12-03 08:45:26 +00004691 Ok(())
4692 }
4693
4694 #[test]
Devin Jeanpierre2ed14ec2021-10-06 11:32:19 +00004695 fn test_copy_derives() {
Devin Jeanpierreccfefc82021-10-27 10:54:00 +00004696 let record = ir_record("S");
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00004697 assert_eq!(generate_derives(&record), &["Clone", "Copy"]);
Devin Jeanpierre2ed14ec2021-10-06 11:32:19 +00004698 }
4699
4700 #[test]
4701 fn test_copy_derives_not_is_trivial_abi() {
Devin Jeanpierreccfefc82021-10-27 10:54:00 +00004702 let mut record = ir_record("S");
Devin Jeanpierre2ed14ec2021-10-06 11:32:19 +00004703 record.is_trivial_abi = false;
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00004704 assert_eq!(generate_derives(&record), &[""; 0]);
Devin Jeanpierre2ed14ec2021-10-06 11:32:19 +00004705 }
4706
Devin Jeanpierre88343c72022-01-15 01:10:23 +00004707 /// Even if it's trivially relocatable, !Unpin C++ type cannot be
4708 /// cloned/copied or otherwise used by value, because values would allow
4709 /// assignment into the Pin.
4710 ///
4711 /// All !Unpin C++ types, not just non trivially relocatable ones, are
4712 /// unsafe to assign in the Rust sense.
Devin Jeanpierree6e16652021-12-22 15:54:46 +00004713 #[test]
Devin Jeanpierre88343c72022-01-15 01:10:23 +00004714 fn test_copy_derives_not_final() {
Devin Jeanpierree6e16652021-12-22 15:54:46 +00004715 let mut record = ir_record("S");
Teddy Katzd2cd1422022-04-04 09:41:33 -07004716 record.is_inheritable = true;
Devin Jeanpierre88343c72022-01-15 01:10:23 +00004717 assert_eq!(generate_derives(&record), &[""; 0]);
Devin Jeanpierree6e16652021-12-22 15:54:46 +00004718 }
4719
Devin Jeanpierre2ed14ec2021-10-06 11:32:19 +00004720 #[test]
Devin Jeanpierre2ed14ec2021-10-06 11:32:19 +00004721 fn test_copy_derives_ctor_deleted() {
Devin Jeanpierreccfefc82021-10-27 10:54:00 +00004722 let mut record = ir_record("S");
Lukasz Anforowiczff7df4a2022-06-02 14:27:45 -07004723 record.copy_constructor = ir::SpecialMemberFunc::Unavailable;
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00004724 assert_eq!(generate_derives(&record), &[""; 0]);
Devin Jeanpierre2ed14ec2021-10-06 11:32:19 +00004725 }
4726
4727 #[test]
Devin Jeanpierrebe2f33b2021-10-21 12:54:19 +00004728 fn test_copy_derives_ctor_nontrivial_members() {
Devin Jeanpierreccfefc82021-10-27 10:54:00 +00004729 let mut record = ir_record("S");
Lukasz Anforowiczff7df4a2022-06-02 14:27:45 -07004730 record.copy_constructor = ir::SpecialMemberFunc::NontrivialMembers;
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00004731 assert_eq!(generate_derives(&record), &[""; 0]);
Devin Jeanpierrebe2f33b2021-10-21 12:54:19 +00004732 }
4733
4734 #[test]
4735 fn test_copy_derives_ctor_nontrivial_self() {
Devin Jeanpierreccfefc82021-10-27 10:54:00 +00004736 let mut record = ir_record("S");
Lukasz Anforowiczff7df4a2022-06-02 14:27:45 -07004737 record.copy_constructor = ir::SpecialMemberFunc::NontrivialUserDefined;
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00004738 assert_eq!(generate_derives(&record), &[""; 0]);
Devin Jeanpierre2ed14ec2021-10-06 11:32:19 +00004739 }
4740
Devin Jeanpierreb1e816a2022-04-29 20:14:22 -07004741 /// In Rust, a Drop type cannot be Copy.
4742 #[test]
4743 fn test_copy_derives_dtor_nontrivial_self() {
4744 let mut record = ir_record("S");
Lukasz Anforowiczff7df4a2022-06-02 14:27:45 -07004745 for definition in
4746 [ir::SpecialMemberFunc::NontrivialUserDefined, ir::SpecialMemberFunc::NontrivialMembers]
4747 {
4748 record.destructor = definition;
Devin Jeanpierreb1e816a2022-04-29 20:14:22 -07004749 assert_eq!(generate_derives(&record), &["Clone"]);
4750 }
4751 }
4752
Devin Jeanpierre7a7328e2021-09-17 07:10:08 +00004753 #[test]
4754 fn test_ptr_func() -> Result<()> {
Lukasz Anforowiczae9be082022-10-05 07:33:57 -07004755 let ir = ir_from_cc(r#" inline int* Deref(int*const* p); "#)?;
Devin Jeanpierred6da7002021-10-21 12:55:20 +00004756
Devin Jeanpierree9850a72022-06-29 19:04:48 -07004757 let BindingsTokens { rs_api, rs_api_impl } = generate_bindings_tokens(ir)?;
Marcel Hlopko89547752021-12-10 09:39:41 +00004758 assert_rs_matches!(
4759 rs_api,
4760 quote! {
Michael Forsterdb8101a2021-10-08 06:56:03 +00004761 #[inline(always)]
Lukasz Anforowiczf7bdd392022-01-21 00:33:39 +00004762 pub unsafe fn Deref(p: *const *mut i32) -> *mut i32 {
Rosica Dejanovska68c11ac2022-05-12 03:48:59 -07004763 crate::detail::__rust_thunk___Z5DerefPKPi(p)
Marcel Hlopko89547752021-12-10 09:39:41 +00004764 }
4765 }
4766 );
4767 assert_rs_matches!(
4768 rs_api,
4769 quote! {
Michael Forsterdb8101a2021-10-08 06:56:03 +00004770 mod detail {
Googler55647142022-01-11 12:37:39 +00004771 #[allow(unused_imports)]
Devin Jeanpierred4dde0e2021-10-13 20:48:25 +00004772 use super::*;
Michael Forsterdb8101a2021-10-08 06:56:03 +00004773 extern "C" {
Googlera675ae02021-12-07 08:04:59 +00004774 pub(crate) fn __rust_thunk___Z5DerefPKPi(p: *const *mut i32) -> *mut i32;
Marcel Hlopko89547752021-12-10 09:39:41 +00004775 }
4776 }
4777 }
Devin Jeanpierre7a7328e2021-09-17 07:10:08 +00004778 );
Devin Jeanpierre184f9ac2021-09-17 13:47:03 +00004779
Marcel Hlopko89547752021-12-10 09:39:41 +00004780 assert_cc_matches!(
Devin Jeanpierre4f06f832022-04-26 15:51:30 -07004781 rs_api_impl,
Marcel Hlopko89547752021-12-10 09:39:41 +00004782 quote! {
Googlera675ae02021-12-07 08:04:59 +00004783 extern "C" int* __rust_thunk___Z5DerefPKPi(int* const * p) {
Lukasz Anforowicz07b33902022-07-13 15:27:03 -07004784 return Deref(p);
Devin Jeanpierre184f9ac2021-09-17 13:47:03 +00004785 }
Marcel Hlopko89547752021-12-10 09:39:41 +00004786 }
Devin Jeanpierre184f9ac2021-09-17 13:47:03 +00004787 );
Devin Jeanpierre7a7328e2021-09-17 07:10:08 +00004788 Ok(())
4789 }
Michael Forstered642022021-10-04 09:48:25 +00004790
4791 #[test]
Googlerdb111532022-01-05 06:12:13 +00004792 fn test_const_char_ptr_func() -> Result<()> {
4793 // This is a regression test: We used to include the "const" in the name
4794 // of the CcType, which caused a panic in the code generator
4795 // ('"const char" is not a valid Ident').
4796 // It's therefore important that f() is inline so that we need to
4797 // generate a thunk for it (where we then process the CcType).
Lukasz Anforowiczae9be082022-10-05 07:33:57 -07004798 let ir = ir_from_cc(r#" inline void f(const char *str); "#)?;
Googlerdb111532022-01-05 06:12:13 +00004799
Devin Jeanpierree9850a72022-06-29 19:04:48 -07004800 let BindingsTokens { rs_api, rs_api_impl } = generate_bindings_tokens(ir)?;
Googlerdb111532022-01-05 06:12:13 +00004801 assert_rs_matches!(
4802 rs_api,
4803 quote! {
4804 #[inline(always)]
Lukasz Anforowiczf7bdd392022-01-21 00:33:39 +00004805 pub unsafe fn f(str: *const i8) {
Rosica Dejanovska68c11ac2022-05-12 03:48:59 -07004806 crate::detail::__rust_thunk___Z1fPKc(str)
Googlerdb111532022-01-05 06:12:13 +00004807 }
4808 }
4809 );
4810 assert_rs_matches!(
4811 rs_api,
4812 quote! {
4813 extern "C" {
4814 pub(crate) fn __rust_thunk___Z1fPKc(str: *const i8);
4815 }
4816 }
4817 );
4818
4819 assert_cc_matches!(
Devin Jeanpierre4f06f832022-04-26 15:51:30 -07004820 rs_api_impl,
Googlerdb111532022-01-05 06:12:13 +00004821 quote! {
Lukasz Anforowicz07b33902022-07-13 15:27:03 -07004822 extern "C" void __rust_thunk___Z1fPKc(char const * str){ f(str); }
Googlerdb111532022-01-05 06:12:13 +00004823 }
4824 );
4825 Ok(())
4826 }
4827
4828 #[test]
Lukasz Anforowiczcf230fd2022-02-18 19:20:39 +00004829 fn test_func_ptr_where_params_are_primitive_types() -> Result<()> {
4830 let ir = ir_from_cc(r#" int (*get_ptr_to_func())(float, double); "#)?;
Devin Jeanpierree9850a72022-06-29 19:04:48 -07004831 let BindingsTokens { rs_api, rs_api_impl } = generate_bindings_tokens(ir)?;
Lukasz Anforowiczcf230fd2022-02-18 19:20:39 +00004832 assert_rs_matches!(
4833 rs_api,
4834 quote! {
4835 #[inline(always)]
4836 pub fn get_ptr_to_func() -> Option<extern "C" fn (f32, f64) -> i32> {
Rosica Dejanovska68c11ac2022-05-12 03:48:59 -07004837 unsafe { crate::detail::__rust_thunk___Z15get_ptr_to_funcv() }
Lukasz Anforowiczcf230fd2022-02-18 19:20:39 +00004838 }
4839 }
4840 );
4841 assert_rs_matches!(
4842 rs_api,
4843 quote! {
4844 mod detail {
4845 #[allow(unused_imports)]
4846 use super::*;
4847 extern "C" {
4848 #[link_name = "_Z15get_ptr_to_funcv"]
4849 pub(crate) fn __rust_thunk___Z15get_ptr_to_funcv()
4850 -> Option<extern "C" fn(f32, f64) -> i32>;
4851 }
4852 }
4853 }
4854 );
4855 // Verify that no C++ thunk got generated.
4856 assert_cc_not_matches!(rs_api_impl, quote! { __rust_thunk___Z15get_ptr_to_funcv });
4857
4858 // TODO(b/217419782): Add another test for more exotic calling conventions /
4859 // abis.
4860
4861 // TODO(b/217419782): Add another test for pointer to a function that
4862 // takes/returns non-trivially-movable types by value. See also
4863 // <internal link>
4864
4865 Ok(())
4866 }
4867
4868 #[test]
Lukasz Anforowicz92c81c32022-03-04 19:03:56 +00004869 fn test_func_ref() -> Result<()> {
4870 let ir = ir_from_cc(r#" int (&get_ref_to_func())(float, double); "#)?;
Devin Jeanpierree9850a72022-06-29 19:04:48 -07004871 let rs_api = generate_bindings_tokens(ir)?.rs_api;
Lukasz Anforowicz92c81c32022-03-04 19:03:56 +00004872 assert_rs_matches!(
4873 rs_api,
4874 quote! {
4875 #[inline(always)]
4876 pub fn get_ref_to_func() -> extern "C" fn (f32, f64) -> i32 {
Rosica Dejanovska68c11ac2022-05-12 03:48:59 -07004877 unsafe { crate::detail::__rust_thunk___Z15get_ref_to_funcv() }
Lukasz Anforowicz92c81c32022-03-04 19:03:56 +00004878 }
4879 }
4880 );
4881 Ok(())
4882 }
4883
4884 #[test]
Lukasz Anforowiczcf230fd2022-02-18 19:20:39 +00004885 fn test_func_ptr_with_non_static_lifetime() -> Result<()> {
Martin Brænnee5ba6b62022-06-23 07:38:40 -07004886 let ir = ir_from_cc(&with_lifetime_macros(
Lukasz Anforowiczcf230fd2022-02-18 19:20:39 +00004887 r#"
Martin Brænnee5ba6b62022-06-23 07:38:40 -07004888 int (* $a get_ptr_to_func())(float, double); "#,
4889 ))?;
Devin Jeanpierree9850a72022-06-29 19:04:48 -07004890 let rs_api = generate_bindings_tokens(ir)?.rs_api;
Lukasz Anforowiczcf230fd2022-02-18 19:20:39 +00004891 assert_rs_matches!(
4892 rs_api,
4893 quote! {
4894 // Error while generating bindings for item 'get_ptr_to_func':
4895 // Return type is not supported: Function pointers with non-'static lifetimes are not supported: int (*)(float, double)
4896 }
4897 );
4898 Ok(())
4899 }
4900
4901 #[test]
4902 fn test_func_ptr_where_params_are_raw_ptrs() -> Result<()> {
4903 let ir = ir_from_cc(r#" const int* (*get_ptr_to_func())(const int*); "#)?;
Devin Jeanpierree9850a72022-06-29 19:04:48 -07004904 let BindingsTokens { rs_api, rs_api_impl } = generate_bindings_tokens(ir)?;
Lukasz Anforowiczcf230fd2022-02-18 19:20:39 +00004905 assert_rs_matches!(
4906 rs_api,
4907 quote! {
4908 #[inline(always)]
4909 pub fn get_ptr_to_func() -> Option<extern "C" fn (*const i32) -> *const i32> {
Rosica Dejanovska68c11ac2022-05-12 03:48:59 -07004910 unsafe { crate::detail::__rust_thunk___Z15get_ptr_to_funcv() }
Lukasz Anforowiczcf230fd2022-02-18 19:20:39 +00004911 }
4912 }
4913 );
4914 assert_rs_matches!(
4915 rs_api,
4916 quote! {
4917 mod detail {
4918 #[allow(unused_imports)]
4919 use super::*;
4920 extern "C" {
4921 #[link_name = "_Z15get_ptr_to_funcv"]
4922 pub(crate) fn __rust_thunk___Z15get_ptr_to_funcv()
4923 -> Option<extern "C" fn(*const i32) -> *const i32>;
4924 }
4925 }
4926 }
4927 );
4928 // Verify that no C++ thunk got generated.
4929 assert_cc_not_matches!(rs_api_impl, quote! { __rust_thunk___Z15get_ptr_to_funcv });
4930
4931 // TODO(b/217419782): Add another test where params (and the return
4932 // type) are references with lifetimes. Something like this:
4933 // #pragma clang lifetime_elision
4934 // const int& (*get_ptr_to_func())(const int&, const int&); "#)?;
4935 // 1) Need to investigate why this fails - seeing raw pointers in Rust
4936 // seems to indicate that no lifetimes are present at the `importer.cc`
4937 // level. Maybe lifetime elision doesn't support this scenario? Unclear
Googler53f65942022-02-23 11:23:30 +00004938 // how to explicitly apply [[clang::annotate("lifetimes", "a, b -> a")]]
Lukasz Anforowiczcf230fd2022-02-18 19:20:39 +00004939 // to the _inner_ function.
4940 // 2) It is important to have 2 reference parameters, so see if the problem
4941 // of passing `lifetimes` by value would have been caught - see:
4942 // cl/428079010/depot/rs_bindings_from_cc/
4943 // importer.cc?version=s6#823
4944
4945 // TODO(b/217419782): Decide what to do if the C++ pointer is *not*
4946 // annotated with a lifetime - emit `unsafe fn(...) -> ...` in that
4947 // case?
4948
4949 Ok(())
4950 }
4951
4952 #[test]
Lukasz Anforowicz71e9b592022-03-04 19:03:02 +00004953 fn test_func_ptr_with_custom_abi() -> Result<()> {
4954 let ir = ir_from_cc(r#" int (*get_ptr_to_func())(float, double) [[clang::vectorcall]]; "#)?;
4955
4956 // Verify that the test input correctly represents what we intend to
4957 // test - we want [[clang::vectorcall]] to apply to the returned
4958 // function pointer, but *not* apply to the `get_ptr_to_func` function.
Lukasz Anforowicz71e9b592022-03-04 19:03:02 +00004959 assert_ir_matches!(
4960 ir,
4961 quote! {
4962 Func(Func {
4963 name: "get_ptr_to_func", ...
4964 return_type: MappedType {
4965 rs_type: RsType {
4966 name: Some("Option"), ...
4967 type_args: [RsType { name: Some("#funcPtr vectorcall"), ... }], ...
4968 },
4969 cc_type: CcType {
4970 name: Some("*"), ...
4971 type_args: [CcType { name: Some("#funcValue vectorcall"), ... }], ...
4972 },
Lukasz Anforowicz0b6a6ac2022-03-22 22:32:23 +00004973 }, ...
4974 has_c_calling_convention: true, ...
Lukasz Anforowicz71e9b592022-03-04 19:03:02 +00004975 }),
4976 }
4977 );
4978
Devin Jeanpierree9850a72022-06-29 19:04:48 -07004979 let BindingsTokens { rs_api, rs_api_impl } = generate_bindings_tokens(ir)?;
Lukasz Anforowicz71e9b592022-03-04 19:03:02 +00004980 // Check that the custom "vectorcall" ABI gets propagated into the
4981 // return type (i.e. into `extern "vectorcall" fn`).
Lukasz Anforowicz71e9b592022-03-04 19:03:02 +00004982 assert_rs_matches!(
4983 rs_api,
4984 quote! {
4985 #[inline(always)]
4986 pub fn get_ptr_to_func() -> Option<extern "vectorcall" fn (f32, f64) -> i32> {
Rosica Dejanovska68c11ac2022-05-12 03:48:59 -07004987 unsafe { crate::detail::__rust_thunk___Z15get_ptr_to_funcv() }
Lukasz Anforowicz71e9b592022-03-04 19:03:02 +00004988 }
4989 }
4990 );
4991
4992 // The usual `extern "C"` ABI should be used for "get_ptr_to_func".
4993 assert_rs_matches!(
4994 rs_api,
4995 quote! {
4996 mod detail {
4997 #[allow(unused_imports)]
4998 use super::*;
4999 extern "C" {
5000 #[link_name = "_Z15get_ptr_to_funcv"]
5001 pub(crate) fn __rust_thunk___Z15get_ptr_to_funcv()
5002 -> Option<extern "vectorcall" fn(f32, f64) -> i32>;
5003 }
5004 }
5005 }
5006 );
5007
5008 // Verify that no C++ thunk got generated.
Lukasz Anforowicz71e9b592022-03-04 19:03:02 +00005009 assert_cc_not_matches!(rs_api_impl, quote! { __rust_thunk___Z15get_ptr_to_funcv });
5010 Ok(())
5011 }
5012
5013 #[test]
5014 fn test_func_ptr_thunk() -> Result<()> {
5015 // Using an `inline` keyword forces generation of a C++ thunk in
5016 // `rs_api_impl` (i.e. exercises `format_cc_type` and similar code).
5017 let ir = ir_from_cc(
5018 r#"
5019 int multiply(int x, int y);
5020 inline int (*inline_get_pointer_to_function())(int, int) {
5021 return multiply;
5022 }
5023 "#,
5024 )?;
Devin Jeanpierree9850a72022-06-29 19:04:48 -07005025 let rs_api_impl = generate_bindings_tokens(ir)?.rs_api_impl;
Lukasz Anforowicz71e9b592022-03-04 19:03:02 +00005026 assert_cc_matches!(
5027 rs_api_impl,
5028 quote! {
Lukasz Anforowicz23171542022-04-11 15:26:17 -07005029 extern "C" crubit::type_identity_t<int(int , int)>*
Lukasz Anforowicz71e9b592022-03-04 19:03:02 +00005030 __rust_thunk___Z30inline_get_pointer_to_functionv() {
5031 return inline_get_pointer_to_function();
5032 }
5033 }
5034 );
5035 Ok(())
5036 }
5037
5038 #[test]
Lukasz Anforowicz0b6a6ac2022-03-22 22:32:23 +00005039 fn test_func_ptr_with_custom_abi_thunk() -> Result<()> {
5040 // Using an `inline` keyword forces generation of a C++ thunk in
5041 // `rs_api_impl` (i.e. exercises `format_cc_type`,
5042 // `format_cc_call_conv_as_clang_attribute` and similar code).
5043 let ir = ir_from_cc(
5044 r#"
5045 inline int (*inline_get_ptr_to_func())(float, double) [[clang::vectorcall]];
5046 "#,
5047 )?;
5048
5049 // Verify that the test input correctly represents what we intend to
5050 // test - we want [[clang::vectorcall]] to apply to the returned
5051 // function pointer, but *not* apply to the `get_ptr_to_func` function.
5052 assert_ir_matches!(
5053 ir,
5054 quote! {
5055 Func(Func {
5056 name: "inline_get_ptr_to_func", ...
5057 return_type: MappedType {
5058 rs_type: RsType {
5059 name: Some("Option"), ...
5060 type_args: [RsType { name: Some("#funcPtr vectorcall"), ... }], ...
5061 },
5062 cc_type: CcType {
5063 name: Some("*"), ...
5064 type_args: [CcType { name: Some("#funcValue vectorcall"), ... }], ...
5065 },
5066 }, ...
5067 has_c_calling_convention: true, ...
5068 }),
5069 }
5070 );
5071
5072 // This test is quite similar to `test_func_ptr_thunk` - the main
5073 // difference is verification of the `__attribute__((vectorcall))` in
5074 // the expected signature of the generated thunk below.
Devin Jeanpierree9850a72022-06-29 19:04:48 -07005075 let rs_api_impl = generate_bindings_tokens(ir)?.rs_api_impl;
Lukasz Anforowicz0b6a6ac2022-03-22 22:32:23 +00005076 assert_cc_matches!(
5077 rs_api_impl,
5078 quote! {
Lukasz Anforowicz23171542022-04-11 15:26:17 -07005079 extern "C" crubit::type_identity_t<
Lukasz Anforowicz0b6a6ac2022-03-22 22:32:23 +00005080 int(float , double) __attribute__((vectorcall))
5081 >* __rust_thunk___Z22inline_get_ptr_to_funcv() {
5082 return inline_get_ptr_to_func();
5083 }
5084 }
5085 );
5086 Ok(())
5087 }
5088
5089 #[test]
Michael Forstered642022021-10-04 09:48:25 +00005090 fn test_item_order() -> Result<()> {
Marcel Hlopko89547752021-12-10 09:39:41 +00005091 let ir = ir_from_cc(
5092 "int first_func();
5093 struct FirstStruct {};
5094 int second_func();
5095 struct SecondStruct {};",
5096 )?;
Michael Forstered642022021-10-04 09:48:25 +00005097
Devin Jeanpierree9850a72022-06-29 19:04:48 -07005098 let rs_api = rs_tokens_to_formatted_string_for_tests(generate_bindings_tokens(ir)?.rs_api)?;
Marcel Hlopko89547752021-12-10 09:39:41 +00005099
Lukasz Anforowiczdd9ae0f2022-02-17 15:52:53 +00005100 let idx = |s: &str| rs_api.find(s).ok_or_else(|| anyhow!("'{}' missing", s));
Michael Forstered642022021-10-04 09:48:25 +00005101
5102 let f1 = idx("fn first_func")?;
5103 let f2 = idx("fn second_func")?;
5104 let s1 = idx("struct FirstStruct")?;
5105 let s2 = idx("struct SecondStruct")?;
Googlera675ae02021-12-07 08:04:59 +00005106 let t1 = idx("fn __rust_thunk___Z10first_funcv")?;
5107 let t2 = idx("fn __rust_thunk___Z11second_funcv")?;
Michael Forstered642022021-10-04 09:48:25 +00005108
5109 assert!(f1 < s1);
5110 assert!(s1 < f2);
5111 assert!(f2 < s2);
5112 assert!(s2 < t1);
5113 assert!(t1 < t2);
5114
5115 Ok(())
5116 }
Michael Forster028800b2021-10-05 12:39:59 +00005117
5118 #[test]
Devin Jeanpierrec80e6242022-02-03 01:56:40 +00005119 fn test_base_class_subobject_layout() -> Result<()> {
5120 let ir = ir_from_cc(
5121 r#"
5122 // We use a class here to force `Derived::z` to live inside the tail padding of `Base`.
5123 // On the Itanium ABI, this would not happen if `Base` were a POD type.
Devin Jeanpierre56777022022-02-03 01:57:15 +00005124 class Base {__INT64_TYPE__ x; char y;};
5125 struct Derived final : Base {__INT16_TYPE__ z;};
Devin Jeanpierrec80e6242022-02-03 01:56:40 +00005126 "#,
5127 )?;
Devin Jeanpierree9850a72022-06-29 19:04:48 -07005128 let rs_api = generate_bindings_tokens(ir)?.rs_api;
Devin Jeanpierrec80e6242022-02-03 01:56:40 +00005129 assert_rs_matches!(
5130 rs_api,
5131 quote! {
5132 #[repr(C, align(8))]
5133 pub struct Derived {
Devin Jeanpierrecd5ebf32022-07-08 05:31:55 -07005134 __non_field_data: [::std::mem::MaybeUninit<u8>; 10],
Devin Jeanpierrec80e6242022-02-03 01:56:40 +00005135 pub z: i16,
5136 }
5137 }
5138 );
5139 Ok(())
5140 }
5141
5142 /// The same as test_base_class_subobject_layout, but with multiple
5143 /// inheritance.
5144 #[test]
5145 fn test_base_class_multiple_inheritance_subobject_layout() -> Result<()> {
5146 let ir = ir_from_cc(
5147 r#"
Devin Jeanpierre56777022022-02-03 01:57:15 +00005148 class Base1 {__INT64_TYPE__ x;};
Devin Jeanpierrec80e6242022-02-03 01:56:40 +00005149 class Base2 {char y;};
Devin Jeanpierre56777022022-02-03 01:57:15 +00005150 struct Derived final : Base1, Base2 {__INT16_TYPE__ z;};
Devin Jeanpierrec80e6242022-02-03 01:56:40 +00005151 "#,
5152 )?;
Devin Jeanpierree9850a72022-06-29 19:04:48 -07005153 let rs_api = generate_bindings_tokens(ir)?.rs_api;
Devin Jeanpierrec80e6242022-02-03 01:56:40 +00005154 assert_rs_matches!(
5155 rs_api,
5156 quote! {
5157 #[repr(C, align(8))]
5158 pub struct Derived {
Devin Jeanpierrecd5ebf32022-07-08 05:31:55 -07005159 __non_field_data: [::std::mem::MaybeUninit<u8>; 10],
Devin Jeanpierrec80e6242022-02-03 01:56:40 +00005160 pub z: i16,
5161 }
5162 }
5163 );
5164 Ok(())
5165 }
5166
5167 /// The same as test_base_class_subobject_layout, but with a chain of
5168 /// inheritance.
5169 #[test]
5170 fn test_base_class_deep_inheritance_subobject_layout() -> Result<()> {
5171 let ir = ir_from_cc(
5172 r#"
Devin Jeanpierre56777022022-02-03 01:57:15 +00005173 class Base1 {__INT64_TYPE__ x;};
Devin Jeanpierrec80e6242022-02-03 01:56:40 +00005174 class Base2 : Base1 {char y;};
Devin Jeanpierre56777022022-02-03 01:57:15 +00005175 struct Derived final : Base2 {__INT16_TYPE__ z;};
Devin Jeanpierrec80e6242022-02-03 01:56:40 +00005176 "#,
5177 )?;
Devin Jeanpierree9850a72022-06-29 19:04:48 -07005178 let rs_api = generate_bindings_tokens(ir)?.rs_api;
Devin Jeanpierrec80e6242022-02-03 01:56:40 +00005179 assert_rs_matches!(
5180 rs_api,
5181 quote! {
5182 #[repr(C, align(8))]
5183 pub struct Derived {
Devin Jeanpierrecd5ebf32022-07-08 05:31:55 -07005184 __non_field_data: [::std::mem::MaybeUninit<u8>; 10],
Devin Jeanpierrec80e6242022-02-03 01:56:40 +00005185 pub z: i16,
5186 }
5187 }
5188 );
5189 Ok(())
5190 }
5191
5192 /// For derived classes with no data members, we can't use the offset of the
5193 /// first member to determine the size of the base class subobjects.
5194 #[test]
5195 fn test_base_class_subobject_fieldless_layout() -> Result<()> {
5196 let ir = ir_from_cc(
5197 r#"
Devin Jeanpierre56777022022-02-03 01:57:15 +00005198 class Base {__INT64_TYPE__ x; char y;};
Devin Jeanpierrec80e6242022-02-03 01:56:40 +00005199 struct Derived final : Base {};
5200 "#,
5201 )?;
Devin Jeanpierree9850a72022-06-29 19:04:48 -07005202 let rs_api = generate_bindings_tokens(ir)?.rs_api;
Devin Jeanpierrec80e6242022-02-03 01:56:40 +00005203 assert_rs_matches!(
5204 rs_api,
5205 quote! {
5206 #[repr(C, align(8))]
5207 pub struct Derived {
Devin Jeanpierrecd5ebf32022-07-08 05:31:55 -07005208 __non_field_data: [::std::mem::MaybeUninit<u8>; 16],
Devin Jeanpierrec80e6242022-02-03 01:56:40 +00005209 }
5210 }
5211 );
5212 Ok(())
5213 }
5214
5215 #[test]
5216 fn test_base_class_subobject_empty_fieldless() -> Result<()> {
5217 let ir = ir_from_cc(
5218 r#"
5219 class Base {};
5220 struct Derived final : Base {};
5221 "#,
5222 )?;
Devin Jeanpierree9850a72022-06-29 19:04:48 -07005223 let rs_api = generate_bindings_tokens(ir)?.rs_api;
Devin Jeanpierrec80e6242022-02-03 01:56:40 +00005224 assert_rs_matches!(
5225 rs_api,
5226 quote! {
5227 #[repr(C)]
5228 pub struct Derived {
Devin Jeanpierrecd5ebf32022-07-08 05:31:55 -07005229 __non_field_data: [::std::mem::MaybeUninit<u8>; 1],
Devin Jeanpierrec80e6242022-02-03 01:56:40 +00005230 }
5231 }
5232 );
5233 Ok(())
5234 }
5235
5236 #[test]
5237 fn test_base_class_subobject_empty() -> Result<()> {
5238 let ir = ir_from_cc(
5239 r#"
5240 class Base {};
Devin Jeanpierre1221c2a2022-05-05 22:36:22 -07005241 struct Derived final : Base {
5242 __INT16_TYPE__ x;
5243 };
Devin Jeanpierrec80e6242022-02-03 01:56:40 +00005244 "#,
5245 )?;
Devin Jeanpierree9850a72022-06-29 19:04:48 -07005246 let rs_api = generate_bindings_tokens(ir)?.rs_api;
Devin Jeanpierrec80e6242022-02-03 01:56:40 +00005247 assert_rs_matches!(
5248 rs_api,
5249 quote! {
Devin Jeanpierrec80e6242022-02-03 01:56:40 +00005250 pub struct Derived {
Devin Jeanpierrea2be2a22022-05-18 18:59:05 -07005251 pub x: i16,
5252 }
5253 }
5254 );
5255 Ok(())
5256 }
5257
5258 /// Non-aggregate structs can't be directly initialized, because we add
5259 /// a zero-sized private field to the bindings.
5260 #[test]
5261 fn test_non_aggregate_struct_private_field() -> Result<()> {
5262 let ir = ir_from_cc(
5263 r#"
5264 struct NonAggregate {
5265 NonAggregate() {}
5266
5267 __INT16_TYPE__ x = 0;
5268 };
5269 "#,
5270 )?;
Devin Jeanpierree9850a72022-06-29 19:04:48 -07005271 let rs_api = generate_bindings_tokens(ir)?.rs_api;
Devin Jeanpierrea2be2a22022-05-18 18:59:05 -07005272 assert_rs_matches!(
5273 rs_api,
5274 quote! {
5275 pub struct NonAggregate {
Devin Jeanpierrecd5ebf32022-07-08 05:31:55 -07005276 __non_field_data: [::std::mem::MaybeUninit<u8>; 0],
Devin Jeanpierre1221c2a2022-05-05 22:36:22 -07005277 pub x: i16,
Devin Jeanpierrec80e6242022-02-03 01:56:40 +00005278 }
5279 }
5280 );
5281 Ok(())
5282 }
5283
Devin Jeanpierreb69bcae2022-02-03 09:45:50 +00005284 /// When a field is [[no_unique_address]], it occupies the space up to the
5285 /// next field.
5286 #[test]
5287 fn test_no_unique_address() -> Result<()> {
5288 let ir = ir_from_cc(
5289 r#"
5290 class Field1 {__INT64_TYPE__ x;};
5291 class Field2 {char y;};
5292 struct Struct final {
5293 [[no_unique_address]] Field1 field1;
5294 [[no_unique_address]] Field2 field2;
5295 __INT16_TYPE__ z;
5296 };
5297 "#,
5298 )?;
Devin Jeanpierree9850a72022-06-29 19:04:48 -07005299 let rs_api = generate_bindings_tokens(ir)?.rs_api;
Devin Jeanpierreb69bcae2022-02-03 09:45:50 +00005300 assert_rs_matches!(
5301 rs_api,
5302 quote! {
Devin Jeanpierreb69bcae2022-02-03 09:45:50 +00005303 #[repr(C, align(8))]
5304 pub struct Struct {
Devin Jeanpierrecd5ebf32022-07-08 05:31:55 -07005305 pub(crate) field1: [::std::mem::MaybeUninit<u8>; 8],
5306 pub(crate) field2: [::std::mem::MaybeUninit<u8>; 2],
Devin Jeanpierreb69bcae2022-02-03 09:45:50 +00005307 pub z: i16,
5308 }
Devin Jeanpierrec0543eb2022-04-20 16:00:34 -07005309 }
5310 );
Devin Jeanpierre27450132022-04-11 13:52:01 -07005311 assert_rs_matches!(
5312 rs_api,
5313 quote! {
Devin Jeanpierre58181ac2022-02-14 21:30:05 +00005314 impl Struct {
Rosica Dejanovska8283fe62022-05-09 10:04:28 -07005315 pub fn field1(&self) -> &crate::Field1 {
5316 unsafe {&* (&self.field1 as *const _ as *const crate::Field1)}
Devin Jeanpierre58181ac2022-02-14 21:30:05 +00005317 }
Rosica Dejanovska8283fe62022-05-09 10:04:28 -07005318 pub fn field2(&self) -> &crate::Field2 {
5319 unsafe {&* (&self.field2 as *const _ as *const crate::Field2)}
Devin Jeanpierre58181ac2022-02-14 21:30:05 +00005320 }
5321 }
Devin Jeanpierreb69bcae2022-02-03 09:45:50 +00005322 }
5323 );
5324 Ok(())
5325 }
5326
5327 /// When a [[no_unique_address]] field is the last one, it occupies the rest
5328 /// of the object.
5329 #[test]
5330 fn test_no_unique_address_last_field() -> Result<()> {
5331 let ir = ir_from_cc(
5332 r#"
5333 class Field1 {__INT64_TYPE__ x;};
5334 class Field2 {char y;};
5335 struct Struct final {
5336 [[no_unique_address]] Field1 field1;
5337 [[no_unique_address]] Field2 field2;
5338 };
5339 "#,
5340 )?;
Devin Jeanpierree9850a72022-06-29 19:04:48 -07005341 let rs_api = generate_bindings_tokens(ir)?.rs_api;
Devin Jeanpierreb69bcae2022-02-03 09:45:50 +00005342 assert_rs_matches!(
5343 rs_api,
5344 quote! {
Devin Jeanpierreb69bcae2022-02-03 09:45:50 +00005345 #[repr(C, align(8))]
5346 pub struct Struct {
Devin Jeanpierrecd5ebf32022-07-08 05:31:55 -07005347 pub(crate) field1: [::std::mem::MaybeUninit<u8>; 8],
5348 pub(crate) field2: [::std::mem::MaybeUninit<u8>; 8],
Devin Jeanpierreb69bcae2022-02-03 09:45:50 +00005349 }
5350 }
5351 );
5352 Ok(())
5353 }
5354
5355 #[test]
5356 fn test_no_unique_address_empty() -> Result<()> {
5357 let ir = ir_from_cc(
5358 r#"
5359 class Field {};
5360 struct Struct final {
5361 [[no_unique_address]] Field field;
5362 int x;
5363 };
5364 "#,
5365 )?;
Devin Jeanpierree9850a72022-06-29 19:04:48 -07005366 let rs_api = generate_bindings_tokens(ir)?.rs_api;
Devin Jeanpierreb69bcae2022-02-03 09:45:50 +00005367 assert_rs_matches!(
5368 rs_api,
5369 quote! {
5370 #[repr(C, align(4))]
5371 pub struct Struct {
Devin Jeanpierrecd5ebf32022-07-08 05:31:55 -07005372 pub(crate) field: [::std::mem::MaybeUninit<u8>; 0],
Devin Jeanpierreb69bcae2022-02-03 09:45:50 +00005373 pub x: i32,
5374 }
5375 }
5376 );
5377 Ok(())
5378 }
5379
5380 #[test]
5381 fn test_base_class_subobject_empty_last_field() -> Result<()> {
5382 let ir = ir_from_cc(
5383 r#"
5384 class Field {};
5385 struct Struct final {
5386 [[no_unique_address]] Field field;
5387 };
5388 "#,
5389 )?;
Devin Jeanpierree9850a72022-06-29 19:04:48 -07005390 let rs_api = generate_bindings_tokens(ir)?.rs_api;
Devin Jeanpierreb69bcae2022-02-03 09:45:50 +00005391 assert_rs_matches!(
5392 rs_api,
5393 quote! {
5394 #[repr(C)]
5395 pub struct Struct {
Devin Jeanpierrecd5ebf32022-07-08 05:31:55 -07005396 pub(crate) field: [::std::mem::MaybeUninit<u8>; 1],
Devin Jeanpierreb69bcae2022-02-03 09:45:50 +00005397 }
5398 }
5399 );
5400 Ok(())
5401 }
5402
Devin Jeanpierrec80e6242022-02-03 01:56:40 +00005403 #[test]
Teddy Katz76fa42b2022-02-23 01:22:56 +00005404 fn test_generate_enum_basic() -> Result<()> {
5405 let ir = ir_from_cc("enum Color { kRed = 5, kBlue };")?;
Devin Jeanpierree9850a72022-06-29 19:04:48 -07005406 let rs_api = generate_bindings_tokens(ir)?.rs_api;
Teddy Katz76fa42b2022-02-23 01:22:56 +00005407 assert_rs_matches!(
5408 rs_api,
5409 quote! {
5410 #[repr(transparent)]
5411 #[derive(Debug, PartialEq, Eq, Copy, Clone, Hash, PartialOrd, Ord)]
5412 pub struct Color(u32);
5413 impl Color {
5414 pub const kRed: Color = Color(5);
5415 pub const kBlue: Color = Color(6);
5416 }
5417 impl From<u32> for Color {
5418 fn from(value: u32) -> Color {
Marcel Hlopko872b41a2022-05-10 01:49:33 -07005419 Color(value)
Teddy Katz76fa42b2022-02-23 01:22:56 +00005420 }
5421 }
5422 impl From<Color> for u32 {
5423 fn from(value: Color) -> u32 {
Marcel Hlopko872b41a2022-05-10 01:49:33 -07005424 value.0
Teddy Katz76fa42b2022-02-23 01:22:56 +00005425 }
5426 }
5427 }
5428 );
5429 Ok(())
5430 }
5431
5432 #[test]
5433 fn test_generate_scoped_enum_basic() -> Result<()> {
5434 let ir = ir_from_cc("enum class Color { kRed = -5, kBlue };")?;
Devin Jeanpierree9850a72022-06-29 19:04:48 -07005435 let rs_api = generate_bindings_tokens(ir)?.rs_api;
Teddy Katz76fa42b2022-02-23 01:22:56 +00005436 assert_rs_matches!(
5437 rs_api,
5438 quote! {
5439 #[repr(transparent)]
5440 #[derive(Debug, PartialEq, Eq, Copy, Clone, Hash, PartialOrd, Ord)]
5441 pub struct Color(i32);
5442 impl Color {
5443 pub const kRed: Color = Color(-5);
5444 pub const kBlue: Color = Color(-4);
5445 }
5446 impl From<i32> for Color {
5447 fn from(value: i32) -> Color {
Marcel Hlopko872b41a2022-05-10 01:49:33 -07005448 Color(value)
Teddy Katz76fa42b2022-02-23 01:22:56 +00005449 }
5450 }
5451 impl From<Color> for i32 {
5452 fn from(value: Color) -> i32 {
Marcel Hlopko872b41a2022-05-10 01:49:33 -07005453 value.0
Teddy Katz76fa42b2022-02-23 01:22:56 +00005454 }
5455 }
5456 }
5457 );
5458 Ok(())
5459 }
5460
5461 #[test]
5462 fn test_generate_enum_with_64_bit_signed_vals() -> Result<()> {
5463 let ir = ir_from_cc(
5464 "enum Color : long { kViolet = -9223372036854775807 - 1LL, kRed = -5, kBlue, kGreen = 3, kMagenta = 9223372036854775807 };",
5465 )?;
Devin Jeanpierree9850a72022-06-29 19:04:48 -07005466 let rs_api = generate_bindings_tokens(ir)?.rs_api;
Teddy Katz76fa42b2022-02-23 01:22:56 +00005467 assert_rs_matches!(
5468 rs_api,
5469 quote! {
5470 #[repr(transparent)]
5471 #[derive(Debug, PartialEq, Eq, Copy, Clone, Hash, PartialOrd, Ord)]
5472 pub struct Color(i64);
5473 impl Color {
5474 pub const kViolet: Color = Color(-9223372036854775808);
5475 pub const kRed: Color = Color(-5);
5476 pub const kBlue: Color = Color(-4);
5477 pub const kGreen: Color = Color(3);
5478 pub const kMagenta: Color = Color(9223372036854775807);
5479 }
5480 impl From<i64> for Color {
5481 fn from(value: i64) -> Color {
Marcel Hlopko872b41a2022-05-10 01:49:33 -07005482 Color(value)
Teddy Katz76fa42b2022-02-23 01:22:56 +00005483 }
5484 }
5485 impl From<Color> for i64 {
5486 fn from(value: Color) -> i64 {
Marcel Hlopko872b41a2022-05-10 01:49:33 -07005487 value.0
Teddy Katz76fa42b2022-02-23 01:22:56 +00005488 }
5489 }
5490 }
5491 );
5492 Ok(())
5493 }
5494
5495 #[test]
5496 fn test_generate_enum_with_64_bit_unsigned_vals() -> Result<()> {
5497 let ir = ir_from_cc(
5498 "enum Color: unsigned long { kRed, kBlue, kLimeGreen = 18446744073709551615 };",
5499 )?;
Devin Jeanpierree9850a72022-06-29 19:04:48 -07005500 let rs_api = generate_bindings_tokens(ir)?.rs_api;
Teddy Katz76fa42b2022-02-23 01:22:56 +00005501 assert_rs_matches!(
5502 rs_api,
5503 quote! {
5504 #[repr(transparent)]
5505 #[derive(Debug, PartialEq, Eq, Copy, Clone, Hash, PartialOrd, Ord)]
5506 pub struct Color(u64);
5507 impl Color {
5508 pub const kRed: Color = Color(0);
5509 pub const kBlue: Color = Color(1);
5510 pub const kLimeGreen: Color = Color(18446744073709551615);
5511 }
5512 impl From<u64> for Color {
5513 fn from(value: u64) -> Color {
Marcel Hlopko872b41a2022-05-10 01:49:33 -07005514 Color(value)
Teddy Katz76fa42b2022-02-23 01:22:56 +00005515 }
5516 }
5517 impl From<Color> for u64 {
5518 fn from(value: Color) -> u64 {
Marcel Hlopko872b41a2022-05-10 01:49:33 -07005519 value.0
Teddy Katz76fa42b2022-02-23 01:22:56 +00005520 }
5521 }
5522 }
5523 );
5524 Ok(())
5525 }
5526
5527 #[test]
5528 fn test_generate_enum_with_32_bit_signed_vals() -> Result<()> {
5529 let ir = ir_from_cc(
5530 "enum Color { kViolet = -2147483647 - 1, kRed = -5, kBlue, kGreen = 3, kMagenta = 2147483647 };",
5531 )?;
Devin Jeanpierree9850a72022-06-29 19:04:48 -07005532 let rs_api = generate_bindings_tokens(ir)?.rs_api;
Teddy Katz76fa42b2022-02-23 01:22:56 +00005533 assert_rs_matches!(
5534 rs_api,
5535 quote! {
5536 #[repr(transparent)]
5537 #[derive(Debug, PartialEq, Eq, Copy, Clone, Hash, PartialOrd, Ord)]
5538 pub struct Color(i32);
5539 impl Color {
5540 pub const kViolet: Color = Color(-2147483648);
5541 pub const kRed: Color = Color(-5);
5542 pub const kBlue: Color = Color(-4);
5543 pub const kGreen: Color = Color(3);
5544 pub const kMagenta: Color = Color(2147483647);
5545 }
5546 impl From<i32> for Color {
5547 fn from(value: i32) -> Color {
Marcel Hlopko872b41a2022-05-10 01:49:33 -07005548 Color(value)
Teddy Katz76fa42b2022-02-23 01:22:56 +00005549 }
5550 }
5551 impl From<Color> for i32 {
5552 fn from(value: Color) -> i32 {
Marcel Hlopko872b41a2022-05-10 01:49:33 -07005553 value.0
Teddy Katz76fa42b2022-02-23 01:22:56 +00005554 }
5555 }
5556 }
5557 );
5558 Ok(())
5559 }
5560
5561 #[test]
5562 fn test_generate_enum_with_32_bit_unsigned_vals() -> Result<()> {
5563 let ir = ir_from_cc("enum Color: unsigned int { kRed, kBlue, kLimeGreen = 4294967295 };")?;
Devin Jeanpierree9850a72022-06-29 19:04:48 -07005564 let rs_api = generate_bindings_tokens(ir)?.rs_api;
Teddy Katz76fa42b2022-02-23 01:22:56 +00005565 assert_rs_matches!(
5566 rs_api,
5567 quote! {
5568 #[repr(transparent)]
5569 #[derive(Debug, PartialEq, Eq, Copy, Clone, Hash, PartialOrd, Ord)]
5570 pub struct Color(u32);
5571 impl Color {
5572 pub const kRed: Color = Color(0);
5573 pub const kBlue: Color = Color(1);
5574 pub const kLimeGreen: Color = Color(4294967295);
5575 }
5576 impl From<u32> for Color {
5577 fn from(value: u32) -> Color {
Marcel Hlopko872b41a2022-05-10 01:49:33 -07005578 Color(value)
Teddy Katz76fa42b2022-02-23 01:22:56 +00005579 }
5580 }
5581 impl From<Color> for u32 {
5582 fn from(value: Color) -> u32 {
Marcel Hlopko872b41a2022-05-10 01:49:33 -07005583 value.0
Teddy Katz76fa42b2022-02-23 01:22:56 +00005584 }
5585 }
5586 }
5587 );
5588 Ok(())
5589 }
5590
5591 #[test]
Michael Forster409d9412021-10-07 08:35:29 +00005592 fn test_doc_comment_func() -> Result<()> {
Marcel Hlopko89547752021-12-10 09:39:41 +00005593 let ir = ir_from_cc(
5594 "
5595 // Doc Comment
5596 // with two lines
5597 int func();",
5598 )?;
Michael Forster409d9412021-10-07 08:35:29 +00005599
Marcel Hlopko89547752021-12-10 09:39:41 +00005600 assert_rs_matches!(
Devin Jeanpierree9850a72022-06-29 19:04:48 -07005601 generate_bindings_tokens(ir)?.rs_api,
Marcel Hlopko89547752021-12-10 09:39:41 +00005602 // leading space is intentional so there is a space between /// and the text of the
5603 // comment
5604 quote! {
5605 #[doc = " Doc Comment\n with two lines"]
5606 #[inline(always)]
5607 pub fn func
5608 }
Michael Forster409d9412021-10-07 08:35:29 +00005609 );
5610
5611 Ok(())
5612 }
5613
5614 #[test]
5615 fn test_doc_comment_record() -> Result<()> {
Marcel Hlopko89547752021-12-10 09:39:41 +00005616 let ir = ir_from_cc(
5617 "// Doc Comment\n\
5618 //\n\
5619 // * with bullet\n\
Devin Jeanpierre88343c72022-01-15 01:10:23 +00005620 struct SomeStruct final {\n\
Marcel Hlopko89547752021-12-10 09:39:41 +00005621 // Field doc\n\
5622 int field;\
5623 };",
5624 )?;
Michael Forster028800b2021-10-05 12:39:59 +00005625
Marcel Hlopko89547752021-12-10 09:39:41 +00005626 assert_rs_matches!(
Devin Jeanpierree9850a72022-06-29 19:04:48 -07005627 generate_bindings_tokens(ir)?.rs_api,
Marcel Hlopko89547752021-12-10 09:39:41 +00005628 quote! {
5629 #[doc = " Doc Comment\n \n * with bullet"]
5630 #[derive(Clone, Copy)]
5631 #[repr(C)]
5632 pub struct SomeStruct {
5633 # [doc = " Field doc"]
5634 pub field: i32,
5635 }
5636 }
Michael Forstercc5941a2021-10-07 07:12:24 +00005637 );
Michael Forster028800b2021-10-05 12:39:59 +00005638 Ok(())
5639 }
Devin Jeanpierre91de7012021-10-21 12:53:51 +00005640
Devin Jeanpierre96839c12021-12-14 00:27:38 +00005641 #[test]
Teddy Katzd2cd1422022-04-04 09:41:33 -07005642 fn test_basic_union() -> Result<()> {
5643 let ir = ir_from_cc(
5644 r#"
Devin Jeanpierree9be70a2022-07-25 08:58:54 -07005645 #pragma clang lifetime_elision
Teddy Katzd2cd1422022-04-04 09:41:33 -07005646 union SomeUnion {
5647 int some_field;
5648 long long some_bigger_field;
5649 };
5650 "#,
5651 )?;
Devin Jeanpierree9850a72022-06-29 19:04:48 -07005652 let BindingsTokens { rs_api, rs_api_impl } = generate_bindings_tokens(ir)?;
Teddy Katzd2cd1422022-04-04 09:41:33 -07005653
5654 assert_rs_matches!(
5655 rs_api,
5656 quote! {
5657 #[derive(Clone, Copy)]
5658 #[repr(C)]
5659 pub union SomeUnion {
5660 pub some_field: i32,
5661 pub some_bigger_field: i64,
5662 }
5663 }
5664 );
Marcel Hlopko4c29c6f2022-05-04 00:49:14 -07005665 assert_cc_matches!(
5666 rs_api_impl,
5667 quote! {
5668 extern "C" void __rust_thunk___ZN9SomeUnionC1Ev(union SomeUnion*__this) {...}
5669 }
5670 );
Marcel Hlopko4c29c6f2022-05-04 00:49:14 -07005671 assert_cc_matches!(rs_api_impl, quote! { static_assert(sizeof(union SomeUnion)==8) });
5672 assert_cc_matches!(rs_api_impl, quote! { static_assert(alignof(union SomeUnion)==8) });
5673 assert_cc_matches!(
5674 rs_api_impl,
Lukasz Anforowicz30360ba2022-05-23 12:15:12 -07005675 quote! { static_assert(CRUBIT_OFFSET_OF(some_field, union SomeUnion)==0) }
Marcel Hlopko4c29c6f2022-05-04 00:49:14 -07005676 );
5677 assert_cc_matches!(
5678 rs_api_impl,
Lukasz Anforowicz30360ba2022-05-23 12:15:12 -07005679 quote! { static_assert(CRUBIT_OFFSET_OF(some_bigger_field, union SomeUnion)==0) }
Marcel Hlopko4c29c6f2022-05-04 00:49:14 -07005680 );
Teddy Katzd2cd1422022-04-04 09:41:33 -07005681 Ok(())
5682 }
5683
5684 #[test]
Marcel Hlopkof05621b2022-05-25 00:26:06 -07005685 fn test_union_with_opaque_field() -> Result<()> {
5686 let ir = ir_from_cc(
5687 r#"
5688 union MyUnion {
5689 char first_field[56];
5690 int second_field;
5691 };
5692 "#,
5693 )?;
Devin Jeanpierree9850a72022-06-29 19:04:48 -07005694 let rs_api = generate_bindings_tokens(ir)?.rs_api;
Marcel Hlopkof05621b2022-05-25 00:26:06 -07005695
5696 assert_rs_matches!(
5697 rs_api,
5698 quote! {
5699 #[repr(C, align(4))]
5700 pub union MyUnion { ...
Devin Jeanpierrecd5ebf32022-07-08 05:31:55 -07005701 first_field: [::std::mem::MaybeUninit<u8>; 56],
Marcel Hlopkof05621b2022-05-25 00:26:06 -07005702 pub second_field: i32,
5703 }
5704 }
5705 );
5706
5707 assert_rs_matches!(
5708 rs_api,
Devin Jeanpierrecd5ebf32022-07-08 05:31:55 -07005709 quote! { const _: () = assert!(::std::mem::size_of::<crate::MyUnion>() == 56); }
Marcel Hlopkof05621b2022-05-25 00:26:06 -07005710 );
5711 assert_rs_matches!(
5712 rs_api,
Devin Jeanpierrecd5ebf32022-07-08 05:31:55 -07005713 quote! { const _: () = assert!(::std::mem::align_of::<crate::MyUnion>() == 4); }
Marcel Hlopkof05621b2022-05-25 00:26:06 -07005714 );
5715 Ok(())
5716 }
5717
5718 #[test]
Marcel Hlopkofa9a3952022-05-10 01:34:13 -07005719 // TODO(https://github.com/Gilnaa/memoffset/issues/66): generate assertions for unions once
5720 // offsetof supports them.
5721 fn test_currently_no_offset_assertions_for_unions() -> Result<()> {
5722 let ir = ir_from_cc(
5723 r#"
5724 union SomeUnion {
5725 int some_field;
5726 long long some_bigger_field;
5727 };
5728 "#,
5729 )?;
Devin Jeanpierree9850a72022-06-29 19:04:48 -07005730 let BindingsTokens { rs_api, .. } = generate_bindings_tokens(ir)?;
Marcel Hlopkofa9a3952022-05-10 01:34:13 -07005731
5732 assert_rs_not_matches!(rs_api, quote! { offset_of! });
5733 Ok(())
5734 }
5735
5736 #[test]
Teddy Katzd2cd1422022-04-04 09:41:33 -07005737 fn test_union_with_private_fields() -> Result<()> {
5738 let ir = ir_from_cc(
5739 r#"
5740 union SomeUnionWithPrivateFields {
5741 public:
5742 int public_field;
5743 private:
5744 long long private_field;
5745 };
5746 "#,
5747 )?;
Devin Jeanpierree9850a72022-06-29 19:04:48 -07005748 let rs_api = generate_bindings_tokens(ir)?.rs_api;
Teddy Katzd2cd1422022-04-04 09:41:33 -07005749
5750 assert_rs_matches!(
5751 rs_api,
5752 quote! {
5753 #[derive(Clone, Copy)]
Lukasz Anforowiczdf8fcae2022-06-02 14:54:43 -07005754 #[repr(C, align(8))]
Teddy Katzd2cd1422022-04-04 09:41:33 -07005755 pub union SomeUnionWithPrivateFields {
5756 pub public_field: i32,
Lukasz Anforowiczdf8fcae2022-06-02 14:54:43 -07005757 #[doc = " Reason for representing this field as a blob of bytes:\n Types of non-public C++ fields can be elided away"]
Devin Jeanpierrecd5ebf32022-07-08 05:31:55 -07005758 pub(crate) private_field: [::std::mem::MaybeUninit<u8>; 8],
Teddy Katzd2cd1422022-04-04 09:41:33 -07005759 }
5760 }
5761 );
5762
5763 assert_rs_matches!(
5764 rs_api,
5765 quote! {
Devin Jeanpierrecd5ebf32022-07-08 05:31:55 -07005766 const _: () = assert!(::std::mem::size_of::<crate::SomeUnionWithPrivateFields>() == 8);
5767 const _: () = assert!(::std::mem::align_of::<crate::SomeUnionWithPrivateFields>() == 8);
Teddy Katzd2cd1422022-04-04 09:41:33 -07005768 const _: () = {
Rosica Dejanovska8283fe62022-05-09 10:04:28 -07005769 static_assertions::assert_impl_all!(crate::SomeUnionWithPrivateFields: Clone);
Teddy Katzd2cd1422022-04-04 09:41:33 -07005770 };
5771 const _: () = {
Rosica Dejanovska8283fe62022-05-09 10:04:28 -07005772 static_assertions::assert_impl_all!(crate::SomeUnionWithPrivateFields: Copy);
Teddy Katzd2cd1422022-04-04 09:41:33 -07005773 };
5774 const _: () = {
Lukasz Anforowiczb4d17782022-07-07 08:09:13 -07005775 static_assertions::assert_not_impl_any!(crate::SomeUnionWithPrivateFields: Drop);
Teddy Katzd2cd1422022-04-04 09:41:33 -07005776 };
Teddy Katzd2cd1422022-04-04 09:41:33 -07005777 }
5778 );
5779 Ok(())
5780 }
5781
5782 #[test]
Marcel Hlopko45465732022-05-24 00:51:04 -07005783 fn test_nontrivial_unions() -> Result<()> {
5784 let ir = ir_from_cc_dependency(
5785 r#"
5786 union UnionWithNontrivialField {
5787 NonTrivialStruct my_field;
5788 };
5789 "#,
5790 r#"
5791 struct NonTrivialStruct {
5792 NonTrivialStruct(NonTrivialStruct&&);
5793 };
5794 "#,
5795 )?;
Devin Jeanpierree9850a72022-06-29 19:04:48 -07005796 let rs_api = generate_bindings_tokens(ir)?.rs_api;
Marcel Hlopko45465732022-05-24 00:51:04 -07005797
5798 assert_rs_not_matches!(rs_api, quote! {derive ( ... Copy ... )});
5799 assert_rs_not_matches!(rs_api, quote! {derive ( ... Clone ... )});
Devin Jeanpierre190b90a2022-05-24 06:00:34 -07005800 assert_rs_matches!(
5801 rs_api,
5802 quote! {
Devin Jeanpierrecd5ebf32022-07-08 05:31:55 -07005803 #[::ctor::recursively_pinned]
Devin Jeanpierre190b90a2022-05-24 06:00:34 -07005804 #[repr(C)]
5805 pub union UnionWithNontrivialField { ... }
5806 }
5807 );
Marcel Hlopko45465732022-05-24 00:51:04 -07005808 Ok(())
5809 }
5810
5811 #[test]
Devin Jeanpierre1221c2a2022-05-05 22:36:22 -07005812 fn test_empty_struct() -> Result<()> {
5813 let ir = ir_from_cc(
5814 r#"
5815 struct EmptyStruct final {};
5816 "#,
5817 )?;
Devin Jeanpierree9850a72022-06-29 19:04:48 -07005818 let rs_api = generate_bindings_tokens(ir)?.rs_api;
Devin Jeanpierre1221c2a2022-05-05 22:36:22 -07005819
5820 assert_rs_matches!(
5821 rs_api,
5822 quote! {
5823 #[derive(Clone, Copy)]
5824 #[repr(C)]
5825 pub struct EmptyStruct {
Devin Jeanpierrecd5ebf32022-07-08 05:31:55 -07005826 __non_field_data: [::std::mem::MaybeUninit<u8>; 1],
Devin Jeanpierre1221c2a2022-05-05 22:36:22 -07005827 }
5828 }
5829 );
5830
5831 assert_rs_matches!(
5832 rs_api,
5833 quote! {
Devin Jeanpierrecd5ebf32022-07-08 05:31:55 -07005834 const _: () = assert!(::std::mem::size_of::<crate::EmptyStruct>() == 1);
5835 const _: () = assert!(::std::mem::align_of::<crate::EmptyStruct>() == 1);
Devin Jeanpierre1221c2a2022-05-05 22:36:22 -07005836 }
5837 );
5838
5839 Ok(())
5840 }
5841
5842 #[test]
Teddy Katzd2cd1422022-04-04 09:41:33 -07005843 fn test_empty_union() -> Result<()> {
5844 let ir = ir_from_cc(
5845 r#"
5846 union EmptyUnion {};
5847 "#,
5848 )?;
Devin Jeanpierree9850a72022-06-29 19:04:48 -07005849 let rs_api = generate_bindings_tokens(ir)?.rs_api;
Teddy Katzd2cd1422022-04-04 09:41:33 -07005850
5851 assert_rs_matches!(
5852 rs_api,
5853 quote! {
5854 #[derive(Clone, Copy)]
5855 #[repr(C)]
5856 pub union EmptyUnion {
Devin Jeanpierrecd5ebf32022-07-08 05:31:55 -07005857 __non_field_data: [::std::mem::MaybeUninit<u8>; 1],
Teddy Katzd2cd1422022-04-04 09:41:33 -07005858 }
5859 }
5860 );
5861
5862 assert_rs_matches!(
5863 rs_api,
5864 quote! {
Devin Jeanpierrecd5ebf32022-07-08 05:31:55 -07005865 const _: () = assert!(::std::mem::size_of::<crate::EmptyUnion>() == 1);
5866 const _: () = assert!(::std::mem::align_of::<crate::EmptyUnion>() == 1);
Teddy Katzd2cd1422022-04-04 09:41:33 -07005867 }
5868 );
5869
5870 Ok(())
5871 }
5872
5873 #[test]
5874 fn test_union_field_with_nontrivial_destructor() -> Result<()> {
5875 let ir = ir_from_cc(
5876 r#"
5877 struct NontrivialStruct { ~NontrivialStruct(); };
5878 union UnionWithNontrivialField {
5879 int trivial_field;
5880 NontrivialStruct nontrivial_field;
5881 };
5882 "#,
5883 )?;
Devin Jeanpierree9850a72022-06-29 19:04:48 -07005884 let rs_api = generate_bindings_tokens(ir)?.rs_api;
Teddy Katzd2cd1422022-04-04 09:41:33 -07005885
5886 assert_rs_matches!(
5887 rs_api,
5888 quote! {
Teddy Katzd2cd1422022-04-04 09:41:33 -07005889 #[repr(C)]
5890 pub union UnionWithNontrivialField {
5891 pub trivial_field: i32,
Devin Jeanpierrecd5ebf32022-07-08 05:31:55 -07005892 pub nontrivial_field: ::std::mem::ManuallyDrop<crate::NontrivialStruct>,
Teddy Katzd2cd1422022-04-04 09:41:33 -07005893 }
5894 }
5895 );
5896
5897 assert_rs_matches!(
5898 rs_api,
5899 quote! {
Devin Jeanpierrecd5ebf32022-07-08 05:31:55 -07005900 const _: () = assert!(::std::mem::size_of::<crate::UnionWithNontrivialField>() == 4);
5901 const _: () = assert!(::std::mem::align_of::<crate::UnionWithNontrivialField>() == 4);
Teddy Katzd2cd1422022-04-04 09:41:33 -07005902 }
5903 );
5904 Ok(())
5905 }
5906
5907 #[test]
5908 fn test_union_with_constructors() -> Result<()> {
5909 let ir = ir_from_cc(
5910 r#"
5911 #pragma clang lifetime_elision
5912 union UnionWithDefaultConstructors {
5913 int a;
5914 };
5915 "#,
5916 )?;
Devin Jeanpierree9850a72022-06-29 19:04:48 -07005917 let rs_api = generate_bindings_tokens(ir)?.rs_api;
Teddy Katzd2cd1422022-04-04 09:41:33 -07005918
5919 assert_rs_matches!(
5920 rs_api,
5921 quote! {
5922 #[derive(Clone, Copy)]
5923 #[repr(C)]
5924 pub union UnionWithDefaultConstructors {
5925 pub a: i32,
5926 }
5927 }
5928 );
5929
5930 assert_rs_matches!(
5931 rs_api,
5932 quote! {
5933 impl Default for UnionWithDefaultConstructors {
5934 #[inline(always)]
5935 fn default() -> Self {
Devin Jeanpierrecd5ebf32022-07-08 05:31:55 -07005936 let mut tmp = ::std::mem::MaybeUninit::<Self>::zeroed();
Teddy Katzd2cd1422022-04-04 09:41:33 -07005937 unsafe {
Rosica Dejanovska68c11ac2022-05-12 03:48:59 -07005938 crate::detail::__rust_thunk___ZN28UnionWithDefaultConstructorsC1Ev(&mut tmp);
Teddy Katzd2cd1422022-04-04 09:41:33 -07005939 tmp.assume_init()
5940 }
5941 }
5942 }
5943 }
5944 );
5945
5946 assert_rs_matches!(
5947 rs_api,
5948 quote! {
Devin Jeanpierrecd5ebf32022-07-08 05:31:55 -07005949 impl<'b> From<::ctor::RvalueReference<'b, crate::UnionWithDefaultConstructors>> for UnionWithDefaultConstructors {
Teddy Katzd2cd1422022-04-04 09:41:33 -07005950 #[inline(always)]
Devin Jeanpierrecd5ebf32022-07-08 05:31:55 -07005951 fn from(__param_0: ::ctor::RvalueReference<'b, crate::UnionWithDefaultConstructors>) -> Self {
5952 let mut tmp = ::std::mem::MaybeUninit::<Self>::zeroed();
Teddy Katzd2cd1422022-04-04 09:41:33 -07005953 unsafe {
Rosica Dejanovska68c11ac2022-05-12 03:48:59 -07005954 crate::detail::__rust_thunk___ZN28UnionWithDefaultConstructorsC1EOS_(&mut tmp, __param_0);
Teddy Katzd2cd1422022-04-04 09:41:33 -07005955 tmp.assume_init()
5956 }
5957 }
5958 }
5959 }
5960 );
5961
5962 Ok(())
5963 }
5964
5965 #[test]
Devin Jeanpierre56777022022-02-03 01:57:15 +00005966 fn test_unambiguous_public_bases() -> Result<()> {
5967 let ir = ir_from_cc_dependency(
5968 "
5969 struct VirtualBase {};
5970 struct PrivateBase {};
5971 struct ProtectedBase {};
5972 struct UnambiguousPublicBase {};
5973 struct AmbiguousPublicBase {};
5974 struct MultipleInheritance : UnambiguousPublicBase, AmbiguousPublicBase {};
5975 struct Derived : private PrivateBase, protected ProtectedBase, MultipleInheritance, AmbiguousPublicBase, virtual VirtualBase {};
5976 ",
5977 "",
5978 )?;
Devin Jeanpierree9850a72022-06-29 19:04:48 -07005979 let rs_api = generate_bindings_tokens(ir)?.rs_api;
Devin Jeanpierref99db3e2022-04-27 23:10:33 -07005980 assert_rs_matches!(
5981 rs_api,
5982 quote! {
Lukasz Anforowicz90bb7462022-09-01 08:13:28 -07005983 unsafe impl oops::Inherits<crate::VirtualBase> for crate::Derived {
Rosica Dejanovska8283fe62022-05-09 10:04:28 -07005984 unsafe fn upcast_ptr(derived: *const Self) -> *const crate::VirtualBase {
Lukasz Anforowicz3f133972022-09-01 09:01:02 -07005985 crate::detail::__crubit_dynamic_upcast__7Derived__to__11VirtualBase(derived)
Devin Jeanpierref99db3e2022-04-27 23:10:33 -07005986 }
5987 }
5988 }
5989 );
Devin Jeanpierreb368e682022-05-03 02:23:44 -07005990 assert_rs_matches!(
5991 rs_api,
Lukasz Anforowicz90bb7462022-09-01 08:13:28 -07005992 quote! { unsafe impl oops::Inherits<crate::UnambiguousPublicBase> for crate::Derived }
Devin Jeanpierreb368e682022-05-03 02:23:44 -07005993 );
5994 assert_rs_matches!(
5995 rs_api,
Lukasz Anforowicz90bb7462022-09-01 08:13:28 -07005996 quote! { unsafe impl oops::Inherits<crate::MultipleInheritance> for crate::Derived }
Devin Jeanpierreb368e682022-05-03 02:23:44 -07005997 );
5998 assert_rs_not_matches!(
5999 rs_api,
Lukasz Anforowicz90bb7462022-09-01 08:13:28 -07006000 quote! {unsafe impl oops::Inherits<crate::PrivateBase> for crate::Derived}
Devin Jeanpierreb368e682022-05-03 02:23:44 -07006001 );
6002 assert_rs_not_matches!(
6003 rs_api,
Lukasz Anforowicz90bb7462022-09-01 08:13:28 -07006004 quote! {unsafe impl oops::Inherits<crate::ProtectedBase> for crate::Derived}
Devin Jeanpierreb368e682022-05-03 02:23:44 -07006005 );
6006 assert_rs_not_matches!(
6007 rs_api,
Lukasz Anforowicz90bb7462022-09-01 08:13:28 -07006008 quote! {unsafe impl oops::Inherits<crate::AmbiguousPublicBase> for crate::Derived}
Devin Jeanpierreb368e682022-05-03 02:23:44 -07006009 );
Devin Jeanpierre56777022022-02-03 01:57:15 +00006010 Ok(())
6011 }
6012
6013 /// Contrary to intuitions: a base class conversion is ambiguous even if the
6014 /// ambiguity is from a private base class cast that you can't even
6015 /// perform.
6016 ///
6017 /// Explanation (courtesy James Dennett):
6018 ///
6019 /// > Once upon a time, there was a rule in C++ that changing all access
6020 /// > specifiers to "public" would not change the meaning of code.
6021 /// > That's no longer true, but some of its effects can still be seen.
6022 ///
6023 /// So, we need to be sure to not allow casting to privately-ambiguous
6024 /// bases.
6025 #[test]
6026 fn test_unambiguous_public_bases_private_ambiguity() -> Result<()> {
6027 let ir = ir_from_cc_dependency(
6028 "
6029 struct Base {};
6030 struct Intermediate : public Base {};
6031 struct Derived : Base, private Intermediate {};
6032 ",
6033 "",
6034 )?;
Devin Jeanpierree9850a72022-06-29 19:04:48 -07006035 let rs_api = generate_bindings_tokens(ir)?.rs_api;
Rosica Dejanovska8283fe62022-05-09 10:04:28 -07006036 assert_rs_not_matches!(
6037 rs_api,
6038 quote! { unsafe impl oops::Inherits<crate::Base> for Derived }
6039 );
Devin Jeanpierre56777022022-02-03 01:57:15 +00006040 Ok(())
6041 }
6042
6043 #[test]
Devin Jeanpierre96839c12021-12-14 00:27:38 +00006044 fn test_virtual_thunk() -> Result<()> {
6045 let ir = ir_from_cc("struct Polymorphic { virtual void Foo(); };")?;
6046
6047 assert_cc_matches!(
Devin Jeanpierree9850a72022-06-29 19:04:48 -07006048 generate_bindings_tokens(ir)?.rs_api_impl,
Devin Jeanpierre96839c12021-12-14 00:27:38 +00006049 quote! {
Lukasz Anforowiczd4742ff2022-07-11 17:05:02 -07006050 extern "C" void __rust_thunk___ZN11Polymorphic3FooEv(struct Polymorphic * __this)
Devin Jeanpierre96839c12021-12-14 00:27:38 +00006051 }
6052 );
6053 Ok(())
6054 }
6055
Lukasz Anforowicz0b6a6ac2022-03-22 22:32:23 +00006056 #[test]
6057 fn test_custom_abi_thunk() -> Result<()> {
6058 let ir = ir_from_cc(
6059 r#"
6060 float f_vectorcall_calling_convention(float p1, float p2) [[clang::vectorcall]];
6061 double f_c_calling_convention(double p1, double p2);
6062 "#,
6063 )?;
Devin Jeanpierree9850a72022-06-29 19:04:48 -07006064 let BindingsTokens { rs_api, rs_api_impl } = generate_bindings_tokens(ir)?;
Lukasz Anforowicz0b6a6ac2022-03-22 22:32:23 +00006065 assert_rs_matches!(
6066 rs_api,
6067 quote! {
6068 #[inline(always)]
6069 pub fn f_vectorcall_calling_convention(p1: f32, p2: f32) -> f32 {
6070 unsafe {
Rosica Dejanovska68c11ac2022-05-12 03:48:59 -07006071 crate::detail::__rust_thunk___Z31f_vectorcall_calling_conventionff(p1, p2)
Lukasz Anforowicz0b6a6ac2022-03-22 22:32:23 +00006072 }
6073 }
6074 }
6075 );
6076 assert_rs_matches!(
6077 rs_api,
6078 quote! {
6079 #[inline(always)]
6080 pub fn f_c_calling_convention(p1: f64, p2: f64) -> f64 {
Rosica Dejanovska68c11ac2022-05-12 03:48:59 -07006081 unsafe { crate::detail::__rust_thunk___Z22f_c_calling_conventiondd(p1, p2) }
Lukasz Anforowicz0b6a6ac2022-03-22 22:32:23 +00006082 }
6083 }
6084 );
6085 // `link_name` (i.e. no thunk) for `f_c_calling_convention`. No
6086 // `link_name` (i.e. indicates presence of a thunk) for
6087 // `f_vectorcall_calling_convention`.
6088 assert_rs_matches!(
6089 rs_api,
6090 quote! {
6091 mod detail {
6092 #[allow(unused_imports)]
6093 use super::*;
6094 extern "C" {
6095 pub(crate) fn __rust_thunk___Z31f_vectorcall_calling_conventionff(
6096 p1: f32, p2: f32) -> f32;
6097 #[link_name = "_Z22f_c_calling_conventiondd"]
6098 pub(crate) fn __rust_thunk___Z22f_c_calling_conventiondd(
6099 p1: f64, p2: f64) -> f64;
6100 }
6101 }
6102 }
6103 );
6104 // C++ thunk needed for `f_vectorcall_calling_convention`.
6105 assert_cc_matches!(
6106 rs_api_impl,
6107 quote! {
6108 extern "C" float __rust_thunk___Z31f_vectorcall_calling_conventionff(
6109 float p1, float p2) {
Lukasz Anforowicz07b33902022-07-13 15:27:03 -07006110 return f_vectorcall_calling_convention(p1, p2);
Lukasz Anforowicz0b6a6ac2022-03-22 22:32:23 +00006111 }
6112 }
6113 );
6114 // No C++ thunk expected for `f_c_calling_convention`.
6115 assert_cc_not_matches!(rs_api_impl, quote! { f_c_calling_convention });
6116 Ok(())
6117 }
6118
Devin Jeanpierree6e16652021-12-22 15:54:46 +00006119 /// A trivially relocatable final struct is safe to use in Rust as normal,
6120 /// and is Unpin.
6121 #[test]
6122 fn test_no_negative_impl_unpin() -> Result<()> {
6123 let ir = ir_from_cc("struct Trivial final {};")?;
Devin Jeanpierree9850a72022-06-29 19:04:48 -07006124 let rs_api = generate_bindings_tokens(ir)?.rs_api;
Devin Jeanpierrecd5ebf32022-07-08 05:31:55 -07006125 assert_rs_not_matches!(rs_api, quote! {#[::ctor::recursively_pinned]});
Devin Jeanpierree6e16652021-12-22 15:54:46 +00006126 Ok(())
6127 }
6128
6129 /// A non-final struct, even if it's trivial, is not usable by mut
6130 /// reference, and so is !Unpin.
6131 #[test]
6132 fn test_negative_impl_unpin_nonfinal() -> Result<()> {
6133 let ir = ir_from_cc("struct Nonfinal {};")?;
Devin Jeanpierree9850a72022-06-29 19:04:48 -07006134 let rs_api = generate_bindings_tokens(ir)?.rs_api;
Devin Jeanpierrecd5ebf32022-07-08 05:31:55 -07006135 assert_rs_matches!(rs_api, quote! {#[::ctor::recursively_pinned]});
Devin Jeanpierree6e16652021-12-22 15:54:46 +00006136 Ok(())
6137 }
6138
Devin Jeanpierre91de7012021-10-21 12:53:51 +00006139 /// At the least, a trivial type should have no drop impl if or until we add
6140 /// empty drop impls.
6141 #[test]
6142 fn test_no_impl_drop() -> Result<()> {
Googler7cced422021-12-06 11:58:39 +00006143 let ir = ir_from_cc("struct Trivial {};")?;
Devin Jeanpierree9850a72022-06-29 19:04:48 -07006144 let rs_api = generate_bindings_tokens(ir)?.rs_api;
Devin Jeanpierre215ee8e2022-05-16 16:09:41 -07006145 assert_rs_not_matches!(rs_api, quote! {impl Drop});
6146 assert_rs_not_matches!(rs_api, quote! {impl ::ctor::PinnedDrop});
Devin Jeanpierre91de7012021-10-21 12:53:51 +00006147 Ok(())
6148 }
6149
Devin Jeanpierre7e9a1de2021-12-03 08:04:22 +00006150 /// User-defined destructors *must* become Drop impls with ManuallyDrop
6151 /// fields
Devin Jeanpierre91de7012021-10-21 12:53:51 +00006152 #[test]
Devin Jeanpierre7e9a1de2021-12-03 08:04:22 +00006153 fn test_impl_drop_user_defined_destructor() -> Result<()> {
Googler7cced422021-12-06 11:58:39 +00006154 let ir = ir_from_cc(
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00006155 r#" struct NontrivialStruct { ~NontrivialStruct(); };
6156 struct UserDefinedDestructor {
Devin Jeanpierre91de7012021-10-21 12:53:51 +00006157 ~UserDefinedDestructor();
Devin Jeanpierre7e9a1de2021-12-03 08:04:22 +00006158 int x;
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00006159 NontrivialStruct nts;
Devin Jeanpierre91de7012021-10-21 12:53:51 +00006160 };"#,
6161 )?;
Devin Jeanpierree9850a72022-06-29 19:04:48 -07006162 let rs_api = generate_bindings_tokens(ir)?.rs_api;
Lukasz Anforowicz6d553632022-01-06 21:36:14 +00006163 assert_rs_matches!(
6164 rs_api,
6165 quote! {
Devin Jeanpierre215ee8e2022-05-16 16:09:41 -07006166 impl ::ctor::PinnedDrop for UserDefinedDestructor {
Lukasz Anforowicz6d553632022-01-06 21:36:14 +00006167 #[inline(always)]
Devin Jeanpierrecd5ebf32022-07-08 05:31:55 -07006168 unsafe fn pinned_drop<'a>(self: ::std::pin::Pin<&'a mut Self>) {
Devin Jeanpierre215ee8e2022-05-16 16:09:41 -07006169 crate::detail::__rust_thunk___ZN21UserDefinedDestructorD1Ev(self)
Lukasz Anforowicz6d553632022-01-06 21:36:14 +00006170 }
6171 }
6172 }
6173 );
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00006174 assert_rs_matches!(rs_api, quote! {pub x: i32,});
Rosica Dejanovskaaccf00b2022-04-01 13:28:04 -07006175 assert_rs_matches!(
6176 rs_api,
Devin Jeanpierrecd5ebf32022-07-08 05:31:55 -07006177 quote! {pub nts: ::std::mem::ManuallyDrop<crate::NontrivialStruct>,}
Rosica Dejanovskaaccf00b2022-04-01 13:28:04 -07006178 );
Devin Jeanpierre7e9a1de2021-12-03 08:04:22 +00006179 Ok(())
6180 }
6181
Lukasz Anforowicz6d553632022-01-06 21:36:14 +00006182 /// nontrivial types without user-defined destructors should invoke
6183 /// the C++ destructor to preserve the order of field destructions.
Devin Jeanpierre7e9a1de2021-12-03 08:04:22 +00006184 #[test]
6185 fn test_impl_drop_nontrivial_member_destructor() -> Result<()> {
6186 // TODO(jeanpierreda): This would be cleaner if the UserDefinedDestructor code were
6187 // omitted. For example, we simulate it so that UserDefinedDestructor
6188 // comes from another library.
Googler7cced422021-12-06 11:58:39 +00006189 let ir = ir_from_cc(
Devin Jeanpierre88343c72022-01-15 01:10:23 +00006190 r#"struct UserDefinedDestructor final {
Devin Jeanpierre7e9a1de2021-12-03 08:04:22 +00006191 ~UserDefinedDestructor();
6192 };
Devin Jeanpierre88343c72022-01-15 01:10:23 +00006193 struct TrivialStruct final { int i; };
6194 struct NontrivialMembers final {
Devin Jeanpierre7e9a1de2021-12-03 08:04:22 +00006195 UserDefinedDestructor udd;
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00006196 TrivialStruct ts;
Devin Jeanpierre7e9a1de2021-12-03 08:04:22 +00006197 int x;
6198 };"#,
6199 )?;
Devin Jeanpierree9850a72022-06-29 19:04:48 -07006200 let rs_api = generate_bindings_tokens(ir)?.rs_api;
Lukasz Anforowicz6d553632022-01-06 21:36:14 +00006201 assert_rs_matches!(
6202 rs_api,
6203 quote! {
Devin Jeanpierre215ee8e2022-05-16 16:09:41 -07006204 impl ::ctor::PinnedDrop for NontrivialMembers {
Lukasz Anforowicz6d553632022-01-06 21:36:14 +00006205 #[inline(always)]
Devin Jeanpierrecd5ebf32022-07-08 05:31:55 -07006206 unsafe fn pinned_drop<'a>(self: ::std::pin::Pin<&'a mut Self>) {
Devin Jeanpierre215ee8e2022-05-16 16:09:41 -07006207 crate::detail::__rust_thunk___ZN17NontrivialMembersD1Ev(self)
Lukasz Anforowicz6d553632022-01-06 21:36:14 +00006208 }
6209 }
6210 }
6211 );
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00006212 assert_rs_matches!(rs_api, quote! {pub x: i32,});
Rosica Dejanovska8283fe62022-05-09 10:04:28 -07006213 assert_rs_matches!(rs_api, quote! {pub ts: crate::TrivialStruct,});
Lukasz Anforowicz6d553632022-01-06 21:36:14 +00006214 assert_rs_matches!(
6215 rs_api,
Devin Jeanpierrecd5ebf32022-07-08 05:31:55 -07006216 quote! {pub udd: ::std::mem::ManuallyDrop<crate::UserDefinedDestructor>,}
Lukasz Anforowicz6d553632022-01-06 21:36:14 +00006217 );
Devin Jeanpierre7e9a1de2021-12-03 08:04:22 +00006218 Ok(())
6219 }
6220
6221 /// Trivial types (at least those that are mapped to Copy rust types) do not
6222 /// get a Drop impl.
6223 #[test]
6224 fn test_impl_drop_trivial() -> Result<()> {
Googler7cced422021-12-06 11:58:39 +00006225 let ir = ir_from_cc(
Devin Jeanpierre88343c72022-01-15 01:10:23 +00006226 r#"struct Trivial final {
Devin Jeanpierre7e9a1de2021-12-03 08:04:22 +00006227 ~Trivial() = default;
6228 int x;
6229 };"#,
6230 )?;
Devin Jeanpierree9850a72022-06-29 19:04:48 -07006231 let BindingsTokens { rs_api, rs_api_impl } = generate_bindings_tokens(ir)?;
Marcel Hlopko89547752021-12-10 09:39:41 +00006232 assert_rs_not_matches!(rs_api, quote! {impl Drop});
Devin Jeanpierre215ee8e2022-05-16 16:09:41 -07006233 assert_rs_not_matches!(rs_api, quote! {impl ::ctor::PinnedDrop});
Marcel Hlopko89547752021-12-10 09:39:41 +00006234 assert_rs_matches!(rs_api, quote! {pub x: i32});
Devin Jeanpierree9be70a2022-07-25 08:58:54 -07006235 assert_cc_not_matches!(rs_api_impl, quote! { std::destroy_at });
Devin Jeanpierre91de7012021-10-21 12:53:51 +00006236 Ok(())
6237 }
Devin Jeanpierre45cb1162021-10-27 10:54:28 +00006238
6239 #[test]
Lukasz Anforowicze643ec92021-12-22 15:45:15 +00006240 fn test_impl_default_explicitly_defaulted_constructor() -> Result<()> {
6241 let ir = ir_from_cc(
Lukasz Anforowicz95551272022-01-20 00:02:24 +00006242 r#"#pragma clang lifetime_elision
6243 struct DefaultedConstructor final {
Lukasz Anforowicze643ec92021-12-22 15:45:15 +00006244 DefaultedConstructor() = default;
6245 };"#,
6246 )?;
Devin Jeanpierree9850a72022-06-29 19:04:48 -07006247 let BindingsTokens { rs_api, rs_api_impl } = generate_bindings_tokens(ir)?;
Lukasz Anforowicze643ec92021-12-22 15:45:15 +00006248 assert_rs_matches!(
6249 rs_api,
6250 quote! {
6251 impl Default for DefaultedConstructor {
6252 #[inline(always)]
6253 fn default() -> Self {
Devin Jeanpierrecd5ebf32022-07-08 05:31:55 -07006254 let mut tmp = ::std::mem::MaybeUninit::<Self>::zeroed();
Lukasz Anforowicze643ec92021-12-22 15:45:15 +00006255 unsafe {
Rosica Dejanovska68c11ac2022-05-12 03:48:59 -07006256 crate::detail::__rust_thunk___ZN20DefaultedConstructorC1Ev(&mut tmp);
Lukasz Anforowicze643ec92021-12-22 15:45:15 +00006257 tmp.assume_init()
6258 }
6259 }
6260 }
6261 }
6262 );
Lukasz Anforowicze643ec92021-12-22 15:45:15 +00006263 assert_cc_matches!(
6264 rs_api_impl,
6265 quote! {
6266 extern "C" void __rust_thunk___ZN20DefaultedConstructorC1Ev(
Lukasz Anforowiczd4742ff2022-07-11 17:05:02 -07006267 struct DefaultedConstructor* __this) {
Lukasz Anforowicz07b33902022-07-13 15:27:03 -07006268 crubit::construct_at(__this);
Lukasz Anforowicze643ec92021-12-22 15:45:15 +00006269 }
6270 }
6271 );
6272 Ok(())
6273 }
6274
6275 #[test]
Lukasz Anforowicz326c4e42022-01-27 14:43:00 +00006276 fn test_impl_clone_that_propagates_lifetime() -> Result<()> {
6277 // This test covers the case where a single lifetime applies to 1)
6278 // the `__this` parameter and 2) other constructor parameters. For
6279 // example, maybe the newly constructed object needs to have the
6280 // same lifetime as the constructor's parameter. (This might require
6281 // annotating the whole C++ struct with a lifetime, so maybe the
6282 // example below is not fully realistic/accurate...).
Devin Jeanpierre8cd8ae72022-06-29 18:59:40 -07006283 let ir = ir_from_cc(&with_lifetime_macros(
Lukasz Anforowicz326c4e42022-01-27 14:43:00 +00006284 r#"#pragma clang lifetime_elision
6285 struct Foo final {
Martin Brænnee5ba6b62022-06-23 07:38:40 -07006286 Foo(const int& $a i) $a;
Lukasz Anforowicz326c4e42022-01-27 14:43:00 +00006287 };"#,
Martin Brænnee5ba6b62022-06-23 07:38:40 -07006288 ))?;
Devin Jeanpierre8cd8ae72022-06-29 18:59:40 -07006289 let ctor: &Func = ir
6290 .items()
Lukasz Anforowicz326c4e42022-01-27 14:43:00 +00006291 .filter_map(|item| match item {
Devin Jeanpierre8cd8ae72022-06-29 18:59:40 -07006292 Item::Func(func) => Some(&**func),
Lukasz Anforowicz326c4e42022-01-27 14:43:00 +00006293 _ => None,
6294 })
6295 .find(|f| {
6296 matches!(&f.name, UnqualifiedIdentifier::Constructor)
6297 && f.params.get(1).map(|p| p.identifier.identifier == "i").unwrap_or_default()
6298 })
6299 .unwrap();
6300 {
6301 // Double-check that the test scenario set up above uses the same lifetime
6302 // for both of the constructor's parameters: `__this` and `i`.
6303 assert_eq!(ctor.params.len(), 2);
6304 let this_lifetime: LifetimeId =
6305 *ctor.params[0].type_.rs_type.lifetime_args.first().unwrap();
6306 let i_lifetime: LifetimeId =
Devin Jeanpierre8cd8ae72022-06-29 18:59:40 -07006307 *ctor.params[1].type_.rs_type.lifetime_args.first().unwrap();
Lukasz Anforowicz326c4e42022-01-27 14:43:00 +00006308 assert_eq!(i_lifetime, this_lifetime);
6309 }
6310
6311 // Before cl/423346348 the generated Rust code would incorrectly look
6312 // like this (note the mismatched 'a and 'b lifetimes):
6313 // fn from<'b>(i: &'a i32) -> Self
6314 // After this CL, this scenario will result in an explicit error.
Devin Jeanpierree9850a72022-06-29 19:04:48 -07006315 let rs_api = generate_bindings_tokens(ir)?.rs_api;
Devin Jeanpierred7b48102022-03-31 04:15:03 -07006316 assert_rs_not_matches!(rs_api, quote! {impl From});
Lukasz Anforowiczae9be082022-10-05 07:33:57 -07006317 assert_rs_matches!(rs_api, {
6318 let txt = "google3/ir_from_cc_virtual_header.h;l=34\n\
6319 Error while generating bindings for item 'Foo::Foo':\n\
6320 The lifetime of `__this` is \
6321 unexpectedly also used by another parameter: Lifetime(\"a\")";
6322 quote! { __COMMENT__ #txt }
6323 });
Lukasz Anforowicz326c4e42022-01-27 14:43:00 +00006324 Ok(())
6325 }
6326
6327 #[test]
Lukasz Anforowicz9bab8352021-12-22 17:35:31 +00006328 fn test_impl_default_non_trivial_struct() -> Result<()> {
6329 let ir = ir_from_cc(
Lukasz Anforowicz71716b72022-01-26 17:05:05 +00006330 r#"#pragma clang lifetime_elision
6331 struct NonTrivialStructWithConstructors final {
Lukasz Anforowicz9bab8352021-12-22 17:35:31 +00006332 NonTrivialStructWithConstructors();
6333 ~NonTrivialStructWithConstructors(); // Non-trivial
6334 };"#,
6335 )?;
Devin Jeanpierree9850a72022-06-29 19:04:48 -07006336 let rs_api = generate_bindings_tokens(ir)?.rs_api;
Lukasz Anforowicz9bab8352021-12-22 17:35:31 +00006337 assert_rs_not_matches!(rs_api, quote! {impl Default});
6338 Ok(())
6339 }
6340
6341 #[test]
Lukasz Anforowicz71716b72022-01-26 17:05:05 +00006342 fn test_impl_from_for_explicit_conversion_constructor() -> Result<()> {
6343 let ir = ir_from_cc(
6344 r#"#pragma clang lifetime_elision
6345 struct SomeStruct final {
6346 explicit SomeStruct(int i);
6347 };"#,
6348 )?;
Devin Jeanpierree9850a72022-06-29 19:04:48 -07006349 let rs_api = generate_bindings_tokens(ir)?.rs_api;
Lukasz Anforowicz71716b72022-01-26 17:05:05 +00006350 // As discussed in b/214020567 for now we only generate `From::from` bindings
6351 // for *implicit* C++ conversion constructors.
6352 assert_rs_not_matches!(rs_api, quote! {impl From});
6353 Ok(())
6354 }
6355
6356 #[test]
6357 fn test_impl_from_for_implicit_conversion_constructor() -> Result<()> {
6358 let ir = ir_from_cc(
6359 r#"#pragma clang lifetime_elision
6360 struct SomeStruct final {
6361 SomeStruct(int i); // implicit - no `explicit` keyword
6362 };"#,
6363 )?;
Devin Jeanpierree9850a72022-06-29 19:04:48 -07006364 let rs_api = generate_bindings_tokens(ir)?.rs_api;
Lukasz Anforowicz71716b72022-01-26 17:05:05 +00006365 // As discussed in b/214020567 we generate `From::from` bindings for
6366 // *implicit* C++ conversion constructors.
6367 assert_rs_matches!(
6368 rs_api,
6369 quote! {
6370 impl From<i32> for SomeStruct {
6371 #[inline(always)]
6372 fn from(i: i32) -> Self {
Devin Jeanpierrecd5ebf32022-07-08 05:31:55 -07006373 let mut tmp = ::std::mem::MaybeUninit::<Self>::zeroed();
Lukasz Anforowicz71716b72022-01-26 17:05:05 +00006374 unsafe {
Rosica Dejanovska68c11ac2022-05-12 03:48:59 -07006375 crate::detail::__rust_thunk___ZN10SomeStructC1Ei(&mut tmp, i);
Lukasz Anforowicz71716b72022-01-26 17:05:05 +00006376 tmp.assume_init()
6377 }
6378 }
6379 }
6380 }
6381 );
6382 Ok(())
6383 }
6384
6385 #[test]
Lukasz Anforowicz5e623782022-03-14 16:52:23 +00006386 fn test_impl_from_for_implicit_conversion_from_reference() -> Result<()> {
6387 let ir = ir_from_cc(
6388 r#"#pragma clang lifetime_elision
6389 struct SomeOtherStruct final { int i; };
6390 struct StructUnderTest final {
6391 StructUnderTest(const SomeOtherStruct& other); // implicit - no `explicit` keyword
6392 };"#,
6393 )?;
Devin Jeanpierree9850a72022-06-29 19:04:48 -07006394 let rs_api = generate_bindings_tokens(ir)?.rs_api;
Lukasz Anforowicz5e623782022-03-14 16:52:23 +00006395 // This is a regression test for b/223800038: We want to ensure that the
6396 // code says `impl<'b>` (instead of incorrectly declaring that lifetime
6397 // in `fn from<'b>`).
6398 assert_rs_matches!(
6399 rs_api,
6400 quote! {
Rosica Dejanovska8283fe62022-05-09 10:04:28 -07006401 impl<'b> From<&'b crate::SomeOtherStruct> for StructUnderTest {
Lukasz Anforowicz5e623782022-03-14 16:52:23 +00006402 #[inline(always)]
Rosica Dejanovska8283fe62022-05-09 10:04:28 -07006403 fn from(other: &'b crate::SomeOtherStruct) -> Self {
Devin Jeanpierrecd5ebf32022-07-08 05:31:55 -07006404 let mut tmp = ::std::mem::MaybeUninit::<Self>::zeroed();
Lukasz Anforowicz5e623782022-03-14 16:52:23 +00006405 unsafe {
Rosica Dejanovska68c11ac2022-05-12 03:48:59 -07006406 crate::detail::__rust_thunk___ZN15StructUnderTestC1ERK15SomeOtherStruct(
Lukasz Anforowicz5e623782022-03-14 16:52:23 +00006407 &mut tmp, other);
6408 tmp.assume_init()
6409 }
6410 }
6411 }
6412 },
6413 );
6414 Ok(())
6415 }
6416
Devin Jeanpierre5ff493e2022-06-08 12:40:23 -07006417 /// Methods with missing lifetimes for `self` should give a useful error
6418 /// message.
6419 #[test]
6420 fn test_eq_nolifetime() -> Result<()> {
6421 // Missing lifetimes currently only causes hard errors for trait impls,
6422 // not For inherent methods.
6423 let ir = ir_from_cc("struct SomeStruct{SomeStruct& operator=(const SomeStruct&);};")?;
6424
Devin Jeanpierree9850a72022-06-29 19:04:48 -07006425 let rs_api = rs_tokens_to_formatted_string_for_tests(generate_bindings_tokens(ir)?.rs_api)?;
Devin Jeanpierre5ff493e2022-06-08 12:40:23 -07006426 assert!(rs_api.contains(
6427 "// Error while generating bindings for item 'SomeStruct::operator=':\n\
6428 // `self` has no lifetime. Use lifetime annotations or \
6429 `#pragma clang lifetime_elision` to create bindings for this function."
6430 ));
6431 Ok(())
6432 }
6433
Lukasz Anforowicz5e623782022-03-14 16:52:23 +00006434 #[test]
Lukasz Anforowicz732ca642022-02-03 20:58:38 +00006435 fn test_impl_eq_for_member_function() -> Result<()> {
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +00006436 let ir = ir_from_cc(
6437 r#"#pragma clang lifetime_elision
6438 struct SomeStruct final {
6439 inline bool operator==(const SomeStruct& other) const {
6440 return i == other.i;
6441 }
6442 int i;
6443 };"#,
6444 )?;
Devin Jeanpierree9850a72022-06-29 19:04:48 -07006445 let BindingsTokens { rs_api, rs_api_impl } = generate_bindings_tokens(ir)?;
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +00006446 assert_rs_matches!(
6447 rs_api,
6448 quote! {
Rosica Dejanovska8283fe62022-05-09 10:04:28 -07006449 impl PartialEq<crate::SomeStruct> for SomeStruct {
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +00006450 #[inline(always)]
Rosica Dejanovska8283fe62022-05-09 10:04:28 -07006451 fn eq<'a, 'b>(&'a self, other: &'b crate::SomeStruct) -> bool {
Rosica Dejanovska68c11ac2022-05-12 03:48:59 -07006452 unsafe { crate::detail::__rust_thunk___ZNK10SomeStructeqERKS_(self, other) }
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +00006453 }
6454 }
6455 }
6456 );
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +00006457 assert_cc_matches!(
6458 rs_api_impl,
6459 quote! {
6460 extern "C" bool __rust_thunk___ZNK10SomeStructeqERKS_(
Lukasz Anforowicz07b33902022-07-13 15:27:03 -07006461 const struct SomeStruct* __this, const struct SomeStruct* other) {
6462 return __this->operator==(*other);
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +00006463 }
6464 }
6465 );
6466 Ok(())
6467 }
6468
6469 #[test]
Lukasz Anforowicz732ca642022-02-03 20:58:38 +00006470 fn test_impl_eq_for_free_function() -> Result<()> {
6471 let ir = ir_from_cc(
6472 r#"#pragma clang lifetime_elision
6473 struct SomeStruct final { int i; };
6474 bool operator==(const SomeStruct& lhs, const SomeStruct& rhs) {
6475 return lhs.i == rhs.i;
6476 }"#,
6477 )?;
Devin Jeanpierree9850a72022-06-29 19:04:48 -07006478 let rs_api = generate_bindings_tokens(ir)?.rs_api;
Lukasz Anforowicz732ca642022-02-03 20:58:38 +00006479 assert_rs_matches!(
6480 rs_api,
6481 quote! {
Rosica Dejanovska8283fe62022-05-09 10:04:28 -07006482 impl PartialEq<crate::SomeStruct> for SomeStruct {
Lukasz Anforowicz732ca642022-02-03 20:58:38 +00006483 #[inline(always)]
Rosica Dejanovska8283fe62022-05-09 10:04:28 -07006484 fn eq<'a, 'b>(&'a self, rhs: &'b crate::SomeStruct) -> bool {
Rosica Dejanovska68c11ac2022-05-12 03:48:59 -07006485 unsafe { crate::detail::__rust_thunk___ZeqRK10SomeStructS1_(self, rhs) }
Lukasz Anforowicz732ca642022-02-03 20:58:38 +00006486 }
6487 }
6488 }
6489 );
6490 Ok(())
6491 }
6492
6493 #[test]
Felipe de A. Mello Pereiraec5f22c2022-11-02 04:04:01 -07006494 fn test_impl_eq_for_free_function_different_types() -> Result<()> {
6495 let ir = ir_from_cc(
6496 r#"#pragma clang lifetime_elision
6497 struct SomeStruct final { int i; };
6498 struct SomeOtherStruct final { int i; };
6499 bool operator==(const SomeStruct& lhs, const SomeOtherStruct& rhs) {
6500 return lhs.i == rhs.i;
6501 }"#,
6502 )?;
6503 let rs_api = generate_bindings_tokens(ir)?.rs_api;
6504 assert_rs_matches!(
6505 rs_api,
6506 quote! {
6507 impl PartialEq<crate::SomeOtherStruct> for SomeStruct {
6508 #[inline(always)]
6509 fn eq<'a, 'b>(&'a self, rhs: &'b crate::SomeOtherStruct) -> bool {
6510 unsafe { crate::detail::__rust_thunk___ZeqRK10SomeStructRK15SomeOtherStruct(self, rhs) }
6511 }
6512 }
6513 }
6514 );
6515 Ok(())
6516 }
6517
6518 #[test]
6519 fn test_impl_eq_for_free_function_by_value() -> Result<()> {
6520 let ir = ir_from_cc(
6521 r#"#pragma clang lifetime_elision
6522 struct SomeStruct final { int i; };
6523 bool operator==(SomeStruct lhs, SomeStruct rhs) {
6524 return lhs.i == rhs.i;
6525 }"#,
6526 )?;
6527 let rs_api = generate_bindings_tokens(ir)?.rs_api;
6528 assert_rs_matches!(
6529 rs_api,
6530 quote! {
6531 impl PartialEq<crate::SomeStruct> for SomeStruct {
6532 #[inline(always)]
6533 fn eq(& self, rhs: & crate::SomeStruct) -> bool {
6534 unsafe { crate::detail::__rust_thunk___Zeq10SomeStructS_(self.clone(), rhs.clone()) }
6535 }
6536 }
6537 }
6538 );
6539 Ok(())
6540 }
6541
6542 #[test]
Felipe de A. Mello Pereira69736292022-10-11 02:03:43 -07006543 fn test_impl_lt_for_member_function() -> Result<()> {
6544 let ir = ir_from_cc(
6545 r#"#pragma clang lifetime_elision
6546 struct SomeStruct final {
6547 inline bool operator==(const SomeStruct& other) const {
6548 return i == other.i;
6549 }
6550 inline bool operator<(const SomeStruct& other) const {
6551 return i < other.i;
6552 }
6553 int i;
6554 };"#,
6555 )?;
6556 let BindingsTokens { rs_api, rs_api_impl } = generate_bindings_tokens(ir)?;
6557 assert_rs_matches!(
6558 rs_api,
6559 quote! {
6560 impl PartialOrd<crate::SomeStruct> for SomeStruct {
6561 #[inline(always)]
Felipe de A. Mello Pereiraec5f22c2022-11-02 04:04:01 -07006562 fn partial_cmp(&self, other: &crate::SomeStruct) -> Option<core::cmp::Ordering> {
Felipe de A. Mello Pereira69736292022-10-11 02:03:43 -07006563 if self == other {
6564 return Some(core::cmp::Ordering::Equal);
6565 }
6566 if self < other {
6567 return Some(core::cmp::Ordering::Less);
6568 }
6569 if other < self {
6570 return Some(core::cmp::Ordering::Greater);
6571 }
6572 None
6573 }
6574 #[inline(always)]
6575 fn lt<'a, 'b>(&'a self, other: &'b crate::SomeStruct) -> bool {
6576 unsafe { crate::detail::__rust_thunk___ZNK10SomeStructltERKS_(self, other) }
6577 }
6578 }
6579 }
6580 );
6581 assert_cc_matches!(
6582 rs_api_impl,
6583 quote! {
6584 extern "C" bool __rust_thunk___ZNK10SomeStructltERKS_(
6585 const struct SomeStruct* __this, const struct SomeStruct* other) {
6586 return __this->operator<(*other);
6587 }
6588 }
6589 );
6590 Ok(())
6591 }
6592
6593 #[test]
6594 fn test_impl_lt_for_free_function() -> Result<()> {
6595 let ir = ir_from_cc(
6596 r#"#pragma clang lifetime_elision
6597 struct SomeStruct final {
6598 inline bool operator==(const SomeStruct& other) const {
6599 return i == other.i;
6600 }
6601 int i;
6602 };
6603 bool operator<(const SomeStruct& lhs, const SomeStruct& rhs) {
6604 return lhs.i < rhs.i;
6605 }"#,
6606 )?;
6607 let rs_api = generate_bindings_tokens(ir)?.rs_api;
6608 assert_rs_matches!(
6609 rs_api,
6610 quote! {
6611 impl PartialOrd<crate::SomeStruct> for SomeStruct {
6612 #[inline(always)]
Felipe de A. Mello Pereiraec5f22c2022-11-02 04:04:01 -07006613 fn partial_cmp(&self, other: &crate::SomeStruct) -> Option<core::cmp::Ordering> {
Felipe de A. Mello Pereira69736292022-10-11 02:03:43 -07006614 if self == other {
6615 return Some(core::cmp::Ordering::Equal);
6616 }
6617 if self < other {
6618 return Some(core::cmp::Ordering::Less);
6619 }
6620 if other < self {
6621 return Some(core::cmp::Ordering::Greater);
6622 }
6623 None
6624 }
6625 #[inline(always)]
6626 fn lt<'a, 'b>(&'a self, rhs: &'b crate::SomeStruct) -> bool {
6627 unsafe { crate::detail::__rust_thunk___ZltRK10SomeStructS1_(self, rhs) }
6628 }
6629 }
6630 }
6631 );
6632 Ok(())
6633 }
6634
6635 #[test]
Felipe de A. Mello Pereiraec5f22c2022-11-02 04:04:01 -07006636 fn test_impl_lt_for_free_function_by_value() -> Result<()> {
6637 let ir = ir_from_cc(
6638 r#"#pragma clang lifetime_elision
6639 struct SomeStruct final { int i; };
6640 bool operator==(SomeStruct lhs, SomeStruct rhs) {
6641 return lhs.i == rhs.i;
6642 }
6643 bool operator<(SomeStruct lhs, SomeStruct rhs) {
6644 return lhs.i < rhs.i;
6645 }"#,
6646 )?;
6647 let rs_api = generate_bindings_tokens(ir)?.rs_api;
6648 assert_rs_matches!(
6649 rs_api,
6650 quote! {
6651 impl PartialOrd<crate::SomeStruct> for SomeStruct {
6652 #[inline(always)]
6653 fn partial_cmp(&self, other: &crate::SomeStruct) -> Option<core::cmp::Ordering> {
6654 if self == other {
6655 return Some(core::cmp::Ordering::Equal);
6656 }
6657 if self < other {
6658 return Some(core::cmp::Ordering::Less);
6659 }
6660 if other < self {
6661 return Some(core::cmp::Ordering::Greater);
6662 }
6663 None
6664 }
6665 #[inline(always)]
6666 fn lt(& self, rhs: & crate::SomeStruct) -> bool {
6667 unsafe { crate::detail::__rust_thunk___Zlt10SomeStructS_(self.clone(), rhs.clone()) }
6668 }
6669 }
6670 }
6671 );
6672 Ok(())
6673 }
6674
6675 #[test]
Devin Jeanpierre9ced4ef2022-06-08 12:39:10 -07006676 fn test_assign() -> Result<()> {
6677 let ir = ir_from_cc(
6678 r#"
6679 #pragma clang lifetime_elision
6680 struct SomeStruct {
6681 SomeStruct& operator=(const SomeStruct& other);
6682 };"#,
6683 )?;
Devin Jeanpierree9850a72022-06-29 19:04:48 -07006684 let BindingsTokens { rs_api, .. } = generate_bindings_tokens(ir)?;
Devin Jeanpierre9ced4ef2022-06-08 12:39:10 -07006685 assert_rs_matches!(
6686 rs_api,
6687 quote! {
6688 impl<'b> ::ctor::Assign<&'b crate::SomeStruct> for SomeStruct {
6689 #[inline(always)]
Devin Jeanpierrecd5ebf32022-07-08 05:31:55 -07006690 fn assign<'a>(self: ::std::pin::Pin<&'a mut Self>, other: &'b crate::SomeStruct) {
Devin Jeanpierre9ced4ef2022-06-08 12:39:10 -07006691 unsafe {
6692 crate::detail::__rust_thunk___ZN10SomeStructaSERKS_(self, other);
6693 }
6694 }
6695 }
6696 }
6697 );
6698 Ok(())
6699 }
6700
6701 #[test]
6702 fn test_assign_nonreference_other() -> Result<()> {
6703 let ir = ir_from_cc(
6704 r#"
6705 #pragma clang lifetime_elision
6706 struct SomeStruct {
6707 SomeStruct& operator=(int other);
6708 };"#,
6709 )?;
Devin Jeanpierree9850a72022-06-29 19:04:48 -07006710 let BindingsTokens { rs_api, .. } = generate_bindings_tokens(ir)?;
Devin Jeanpierre9ced4ef2022-06-08 12:39:10 -07006711 assert_rs_matches!(
6712 rs_api,
6713 quote! {
6714 impl<'b> ::ctor::Assign<&'b crate::SomeStruct> for SomeStruct {
6715 #[inline(always)]
Devin Jeanpierrecd5ebf32022-07-08 05:31:55 -07006716 fn assign<'a>(self: ::std::pin::Pin<&'a mut Self>, __param_0: &'b crate::SomeStruct) {
Devin Jeanpierre9ced4ef2022-06-08 12:39:10 -07006717 unsafe {
6718 crate::detail::__rust_thunk___ZN10SomeStructaSERKS_(self, __param_0);
6719 }
6720 }
6721 }
6722 }
6723 );
6724 Ok(())
6725 }
6726
6727 #[test]
6728 fn test_assign_nonreference_return() -> Result<()> {
6729 let ir = ir_from_cc(
6730 r#"
6731 #pragma clang lifetime_elision
6732 struct SomeStruct {
6733 int operator=(const SomeStruct& other);
6734 };"#,
6735 )?;
Devin Jeanpierree9850a72022-06-29 19:04:48 -07006736 let BindingsTokens { rs_api, .. } = generate_bindings_tokens(ir)?;
Devin Jeanpierre9ced4ef2022-06-08 12:39:10 -07006737 assert_rs_matches!(
6738 rs_api,
6739 quote! {
6740 impl<'b> ::ctor::Assign<&'b crate::SomeStruct> for SomeStruct {
6741 #[inline(always)]
Devin Jeanpierrecd5ebf32022-07-08 05:31:55 -07006742 fn assign<'a>(self: ::std::pin::Pin<&'a mut Self>, other: &'b crate::SomeStruct) {
Devin Jeanpierre9ced4ef2022-06-08 12:39:10 -07006743 unsafe {
6744 crate::detail::__rust_thunk___ZN10SomeStructaSERKS_(self, other);
6745 }
6746 }
6747 }
6748 }
6749 );
6750 Ok(())
6751 }
6752
6753 #[test]
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +00006754 fn test_impl_eq_non_const_member_function() -> Result<()> {
6755 let ir = ir_from_cc(
6756 r#"#pragma clang lifetime_elision
6757 struct SomeStruct final {
6758 bool operator==(const SomeStruct& other) /* no `const` here */;
6759 };"#,
6760 )?;
Devin Jeanpierree9850a72022-06-29 19:04:48 -07006761 let rs_api = generate_bindings_tokens(ir)?.rs_api;
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +00006762 assert_rs_not_matches!(rs_api, quote! {impl PartialEq});
6763 Ok(())
6764 }
6765
6766 #[test]
Felipe de A. Mello Pereira69736292022-10-11 02:03:43 -07006767 fn test_impl_lt_different_operands() -> Result<()> {
6768 let ir = ir_from_cc(
6769 r#"#pragma clang lifetime_elision
6770 struct SomeStruct1 final {
6771 int i;
6772 };
6773 struct SomeStruct2 final {
6774 inline bool operator==(const SomeStruct1& other) const {
6775 return i == other.i;
6776 }
6777 inline bool operator<(const SomeStruct1& other) const {
6778 return i < other.i;
6779 };
6780 int i;
6781 };"#,
6782 )?;
6783 let rs_api = generate_bindings_tokens(ir)?.rs_api;
6784 assert_rs_not_matches!(rs_api, quote! {impl PartialOrd});
6785 Ok(())
6786 }
6787
6788 #[test]
6789 fn test_impl_lt_non_const_member_function() -> Result<()> {
6790 let ir = ir_from_cc(
6791 r#"#pragma clang lifetime_elision
6792 struct SomeStruct final {
6793 inline bool operator==(const SomeStruct& other) const {
6794 return i == other.i;
6795 }
6796 int i;
6797 bool operator<(const SomeStruct& other) /* no `const` here */;
6798 };"#,
6799 )?;
6800 let rs_api = generate_bindings_tokens(ir)?.rs_api;
6801 assert_rs_not_matches!(rs_api, quote! {impl PartialOrd});
6802 Ok(())
6803 }
6804
6805 #[test]
6806 fn test_impl_lt_rhs_by_value() -> Result<()> {
6807 let ir = ir_from_cc(
6808 r#"#pragma clang lifetime_elision
6809 struct SomeStruct final {
6810 inline bool operator==(const SomeStruct& other) const {
6811 return i == other.i;
6812 }
6813 int i;
6814 bool operator<(SomeStruct other) const;
6815 };"#,
6816 )?;
6817 let rs_api = generate_bindings_tokens(ir)?.rs_api;
6818 assert_rs_not_matches!(rs_api, quote! {impl PartialOrd});
6819 Ok(())
6820 }
6821
6822 #[test]
6823 fn test_impl_lt_missing_eq_impl() -> Result<()> {
6824 let ir = ir_from_cc(
6825 r#"#pragma clang lifetime_elision
6826 struct SomeStruct final {
6827 inline bool operator<(const SomeStruct& other) const {
6828 return i < other.i;
6829 }
6830 int i;
6831 };"#,
6832 )?;
6833 let rs_api = generate_bindings_tokens(ir)?.rs_api;
6834 assert_rs_not_matches!(rs_api, quote! {impl PartialOrd});
6835 Ok(())
6836 }
6837
6838 #[test]
Dmitri Gribenko67cbfd22022-03-24 13:39:34 +00006839 fn test_thunk_ident_function() -> Result<()> {
6840 let ir = ir_from_cc("inline int foo() {}")?;
6841 let func = retrieve_func(&ir, "foo");
Devin Jeanpierre409f6f62022-07-07 02:00:26 -07006842 assert_eq!(thunk_ident(&func), make_rs_ident("__rust_thunk___Z3foov"));
Dmitri Gribenko67cbfd22022-03-24 13:39:34 +00006843 Ok(())
Devin Jeanpierre45cb1162021-10-27 10:54:28 +00006844 }
6845
6846 #[test]
6847 fn test_thunk_ident_special_names() {
Marcel Hlopko4b13b962021-12-06 12:40:56 +00006848 let ir = ir_from_cc("struct Class {};").unwrap();
Devin Jeanpierre45cb1162021-10-27 10:54:28 +00006849
Googler45ad2752021-12-06 12:12:35 +00006850 let destructor =
6851 ir.functions().find(|f| f.name == UnqualifiedIdentifier::Destructor).unwrap();
Lukasz Anforowiczdd9ae0f2022-02-17 15:52:53 +00006852 assert_eq!(thunk_ident(destructor), make_rs_ident("__rust_thunk___ZN5ClassD1Ev"));
Devin Jeanpierre45cb1162021-10-27 10:54:28 +00006853
Lukasz Anforowicz49b5bbc2022-02-04 23:40:10 +00006854 let default_constructor = ir
6855 .functions()
6856 .find(|f| f.name == UnqualifiedIdentifier::Constructor && f.params.len() == 1)
6857 .unwrap();
Lukasz Anforowiczdd9ae0f2022-02-17 15:52:53 +00006858 assert_eq!(thunk_ident(default_constructor), make_rs_ident("__rust_thunk___ZN5ClassC1Ev"));
Devin Jeanpierre45cb1162021-10-27 10:54:28 +00006859 }
Googler7cced422021-12-06 11:58:39 +00006860
6861 #[test]
Marcel Hlopko89547752021-12-10 09:39:41 +00006862 fn test_elided_lifetimes() -> Result<()> {
Googler7cced422021-12-06 11:58:39 +00006863 let ir = ir_from_cc(
6864 r#"#pragma clang lifetime_elision
Devin Jeanpierre88343c72022-01-15 01:10:23 +00006865 struct S final {
Googler7cced422021-12-06 11:58:39 +00006866 int& f(int& i);
6867 };"#,
Marcel Hlopko89547752021-12-10 09:39:41 +00006868 )?;
Devin Jeanpierree9850a72022-06-29 19:04:48 -07006869 let rs_api = generate_bindings_tokens(ir)?.rs_api;
Marcel Hlopko89547752021-12-10 09:39:41 +00006870 assert_rs_matches!(
6871 rs_api,
6872 quote! {
Lukasz Anforowicz231a3bb2022-01-12 14:05:59 +00006873 pub fn f<'a, 'b>(&'a mut self, i: &'b mut i32) -> &'a mut i32 { ... }
Marcel Hlopko89547752021-12-10 09:39:41 +00006874 }
Googler7cced422021-12-06 11:58:39 +00006875 );
Marcel Hlopko89547752021-12-10 09:39:41 +00006876 assert_rs_matches!(
6877 rs_api,
6878 quote! {
Rosica Dejanovska8283fe62022-05-09 10:04:28 -07006879 pub(crate) fn __rust_thunk___ZN1S1fERi<'a, 'b>(__this: &'a mut crate::S, i: &'b mut i32)
Googler6804a012022-01-05 07:04:36 +00006880 -> &'a mut i32;
Marcel Hlopko89547752021-12-10 09:39:41 +00006881 }
Googler7cced422021-12-06 11:58:39 +00006882 );
Marcel Hlopko89547752021-12-10 09:39:41 +00006883 Ok(())
Googler7cced422021-12-06 11:58:39 +00006884 }
Lukasz Anforowiczdaff0402021-12-23 00:37:50 +00006885
6886 #[test]
Googler386e5942022-02-24 08:53:29 +00006887 fn test_annotated_lifetimes() -> Result<()> {
Martin Brænnee5ba6b62022-06-23 07:38:40 -07006888 let ir = ir_from_cc(&with_lifetime_macros(
6889 r#"
6890 int& $a f(int& $a i1, int& $a i2);
Googler386e5942022-02-24 08:53:29 +00006891 "#,
Martin Brænnee5ba6b62022-06-23 07:38:40 -07006892 ))?;
Devin Jeanpierree9850a72022-06-29 19:04:48 -07006893 let rs_api = generate_bindings_tokens(ir)?.rs_api;
Googler386e5942022-02-24 08:53:29 +00006894 assert_rs_matches!(
6895 rs_api,
6896 quote! {
6897 pub fn f<'a>(i1: &'a mut i32, i2: &'a mut i32) -> &'a mut i32 { ... }
6898 }
6899 );
6900 assert_rs_matches!(
6901 rs_api,
6902 quote! {
6903 pub(crate) fn __rust_thunk___Z1fRiS_<'a>(i1: &'a mut i32, i2: &'a mut i32)
6904 -> &'a mut i32;
6905 }
6906 );
6907 Ok(())
6908 }
6909
6910 #[test]
Lukasz Anforowiczdaff0402021-12-23 00:37:50 +00006911 fn test_format_generic_params() -> Result<()> {
Felipe de A. Mello Pereiraec5f22c2022-11-02 04:04:01 -07006912 assert_rs_matches!(
6913 format_generic_params(/* lifetimes= */ &[], std::iter::empty::<syn::Ident>()),
6914 quote! {}
6915 );
Lukasz Anforowiczdaff0402021-12-23 00:37:50 +00006916
Marcel Hlopkoeaae9b72022-01-21 15:54:11 +00006917 let idents = ["T1", "T2"].iter().map(|s| make_rs_ident(s));
Felipe de A. Mello Pereiraec5f22c2022-11-02 04:04:01 -07006918 assert_rs_matches!(
6919 format_generic_params(/* lifetimes= */ &[], idents),
6920 quote! { < T1, T2 > }
6921 );
Lukasz Anforowiczdaff0402021-12-23 00:37:50 +00006922
Felipe de A. Mello Pereiraec5f22c2022-11-02 04:04:01 -07006923 let lifetimes = ["a", "b", "_"].iter().map(|s| Lifetime::new(s)).collect::<Vec<_>>();
6924 assert_rs_matches!(
6925 format_generic_params(&lifetimes, std::iter::empty::<syn::Ident>()),
6926 quote! { < 'a, 'b > }
6927 );
Lukasz Anforowiczdaff0402021-12-23 00:37:50 +00006928
6929 Ok(())
6930 }
Googlerd03d05b2022-01-07 10:10:57 +00006931
6932 #[test]
Devin Jeanpierread125742022-04-11 13:50:28 -07006933 fn test_format_tuple_except_singleton() {
6934 fn format(xs: &[TokenStream]) -> TokenStream {
6935 format_tuple_except_singleton(xs)
6936 }
6937 assert_rs_matches!(format(&[]), quote! {()});
6938 assert_rs_matches!(format(&[quote! {a}]), quote! {a});
6939 assert_rs_matches!(format(&[quote! {a}, quote! {b}]), quote! {(a, b)});
6940 }
6941
6942 #[test]
Googlerd03d05b2022-01-07 10:10:57 +00006943 fn test_overloaded_functions() -> Result<()> {
6944 // TODO(b/213280424): We don't support creating bindings for overloaded
6945 // functions yet, except in the case of overloaded constructors with a
6946 // single parameter.
6947 let ir = ir_from_cc(
Lukasz Anforowicz55673c92022-01-27 19:37:26 +00006948 r#" #pragma clang lifetime_elision
Devin Jeanpierred5a53622022-10-04 22:00:02 -07006949 void f() {}
6950 void f(int i) {}
Devin Jeanpierre88343c72022-01-15 01:10:23 +00006951 struct S1 final {
Devin Jeanpierred5a53622022-10-04 22:00:02 -07006952 void f() {}
6953 void f(int i) {}
Googlerd03d05b2022-01-07 10:10:57 +00006954 };
Devin Jeanpierre88343c72022-01-15 01:10:23 +00006955 struct S2 final {
Googlerd03d05b2022-01-07 10:10:57 +00006956 void f();
6957 };
Devin Jeanpierre88343c72022-01-15 01:10:23 +00006958 struct S3 final {
Googlerd03d05b2022-01-07 10:10:57 +00006959 S3(int i);
6960 S3(double d);
6961 };
Lukasz Anforowicz8d064202022-09-01 07:31:06 -07006962
6963 namespace foo { void not_overloaded(); }
6964 namespace bar { void not_overloaded(); }
Googlerd03d05b2022-01-07 10:10:57 +00006965 "#,
6966 )?;
Devin Jeanpierred5a53622022-10-04 22:00:02 -07006967 let BindingsTokens { rs_api, rs_api_impl } = generate_bindings_tokens(ir)?;
Googlerd03d05b2022-01-07 10:10:57 +00006968
6969 // Cannot overload free functions.
Lukasz Anforowiczae9be082022-10-05 07:33:57 -07006970 assert_cc_matches!(rs_api, {
6971 let txt = "google3/ir_from_cc_virtual_header.h;l=4\n\
6972 Error while generating bindings for item 'f':\n\
6973 Cannot generate bindings for overloaded function";
6974 quote! { __COMMENT__ #txt }
6975 });
Googlerd03d05b2022-01-07 10:10:57 +00006976 assert_rs_not_matches!(rs_api, quote! {pub fn f()});
6977 assert_rs_not_matches!(rs_api, quote! {pub fn f(i: i32)});
6978
Lukasz Anforowiczae9be082022-10-05 07:33:57 -07006979 assert_cc_matches!(rs_api, {
6980 let txt = "google3/ir_from_cc_virtual_header.h;l=7\n\
6981 Error while generating bindings for item 'S1::f':\n\
6982 Cannot generate bindings for overloaded function";
6983 quote! { __COMMENT__ #txt }
6984 });
Googlerd03d05b2022-01-07 10:10:57 +00006985 assert_rs_not_matches!(rs_api, quote! {pub fn f(... S1 ...)});
6986
Devin Jeanpierred5a53622022-10-04 22:00:02 -07006987 // And thunks aren't generated for either.
6988 assert_cc_not_matches!(rs_api_impl, quote! {f});
6989
Googlerd03d05b2022-01-07 10:10:57 +00006990 // But we can import member functions that have the same name as a free
6991 // function.
Lukasz Anforowicz55673c92022-01-27 19:37:26 +00006992 assert_rs_matches!(rs_api, quote! {pub fn f<'a>(&'a mut self)});
Googlerd03d05b2022-01-07 10:10:57 +00006993
6994 // We can also import overloaded single-parameter constructors.
6995 assert_rs_matches!(rs_api, quote! {impl From<i32> for S3});
6996 assert_rs_matches!(rs_api, quote! {impl From<f64> for S3});
Lukasz Anforowicz8d064202022-09-01 07:31:06 -07006997
6998 // And we can import functions that have the same name + signature, but that are
6999 // in 2 different namespaces.
7000 assert_rs_matches!(rs_api, quote! { pub fn not_overloaded() });
Googlerd03d05b2022-01-07 10:10:57 +00007001 Ok(())
7002 }
Googlerdcca7f72022-01-10 12:30:43 +00007003
7004 #[test]
7005 fn test_type_alias() -> Result<()> {
7006 let ir = ir_from_cc(
7007 r#"
Lukasz Anforowiczc503af42022-03-04 23:18:15 +00007008 // MyTypedefDecl doc comment
Googlerdcca7f72022-01-10 12:30:43 +00007009 typedef int MyTypedefDecl;
Lukasz Anforowiczc503af42022-03-04 23:18:15 +00007010
Googlerdcca7f72022-01-10 12:30:43 +00007011 using MyTypeAliasDecl = int;
Googler6a0a5252022-01-11 14:08:09 +00007012 using MyTypeAliasDecl_Alias = MyTypeAliasDecl;
7013
Devin Jeanpierre88343c72022-01-15 01:10:23 +00007014 struct S final {};
Googler6a0a5252022-01-11 14:08:09 +00007015 using S_Alias = S;
7016 using S_Alias_Alias = S_Alias;
7017
7018 inline void f(MyTypedefDecl t) {}
Googlerdcca7f72022-01-10 12:30:43 +00007019 "#,
7020 )?;
Devin Jeanpierree9850a72022-06-29 19:04:48 -07007021 let BindingsTokens { rs_api, rs_api_impl } = generate_bindings_tokens(ir)?;
Lukasz Anforowiczc503af42022-03-04 23:18:15 +00007022 assert_rs_matches!(
7023 rs_api,
7024 quote! {
7025 #[doc = " MyTypedefDecl doc comment"]
7026 pub type MyTypedefDecl = i32;
7027 }
7028 );
Googler6a0a5252022-01-11 14:08:09 +00007029 assert_rs_matches!(rs_api, quote! { pub type MyTypeAliasDecl = i32; });
Rosica Dejanovska8283fe62022-05-09 10:04:28 -07007030 assert_rs_matches!(
7031 rs_api,
7032 quote! { pub type MyTypeAliasDecl_Alias = crate::MyTypeAliasDecl; }
7033 );
7034 assert_rs_matches!(rs_api, quote! { pub type S_Alias = crate::S; });
7035 assert_rs_matches!(rs_api, quote! { pub type S_Alias_Alias = crate::S_Alias; });
7036 assert_rs_matches!(rs_api, quote! { pub fn f(t: crate::MyTypedefDecl) });
Googler6a0a5252022-01-11 14:08:09 +00007037 assert_cc_matches!(
Devin Jeanpierre4f06f832022-04-26 15:51:30 -07007038 rs_api_impl,
Googler6a0a5252022-01-11 14:08:09 +00007039 quote! {
Lukasz Anforowicz07b33902022-07-13 15:27:03 -07007040 extern "C" void __rust_thunk___Z1fi(MyTypedefDecl t) { f(t); }
Googler6a0a5252022-01-11 14:08:09 +00007041 }
7042 );
Googlerdcca7f72022-01-10 12:30:43 +00007043 Ok(())
7044 }
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00007045
7046 #[test]
7047 fn test_rs_type_kind_implements_copy() -> Result<()> {
Lukasz Anforowicz20651e32022-02-10 14:52:15 +00007048 let template = r#" LIFETIMES
Devin Jeanpierre88343c72022-01-15 01:10:23 +00007049 struct [[clang::trivial_abi]] TrivialStruct final { int i; };
7050 struct [[clang::trivial_abi]] UserDefinedCopyConstructor final {
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00007051 UserDefinedCopyConstructor(const UserDefinedCopyConstructor&);
7052 };
7053 using IntAlias = int;
7054 using TrivialAlias = TrivialStruct;
7055 using NonTrivialAlias = UserDefinedCopyConstructor;
7056 void func(PARAM_TYPE some_param);
7057 "#;
7058 assert_impl_all!(i32: Copy);
7059 assert_impl_all!(&i32: Copy);
Lukasz Anforowiczb4d17782022-07-07 08:09:13 -07007060 assert_not_impl_any!(&mut i32: Copy);
Lukasz Anforowicz20651e32022-02-10 14:52:15 +00007061 assert_impl_all!(Option<&i32>: Copy);
Lukasz Anforowiczb4d17782022-07-07 08:09:13 -07007062 assert_not_impl_any!(Option<&mut i32>: Copy);
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00007063 assert_impl_all!(*const i32: Copy);
7064 assert_impl_all!(*mut i32: Copy);
Lukasz Anforowicz20651e32022-02-10 14:52:15 +00007065 struct Test {
7066 // Test inputs:
7067 cc: &'static str,
7068 lifetimes: bool,
7069 // Expected test outputs:
7070 rs: &'static str,
7071 is_copy: bool,
7072 }
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00007073 let tests = vec![
7074 // Validity of the next few tests is verified via
7075 // `assert_[not_]impl_all!` static assertions above.
Lukasz Anforowicz20651e32022-02-10 14:52:15 +00007076 Test { cc: "int", lifetimes: true, rs: "i32", is_copy: true },
Lukasz Anforowiczae9be082022-10-05 07:33:57 -07007077 Test { cc: "const int&", lifetimes: true, rs: "& 'a i32", is_copy: true },
7078 Test { cc: "int&", lifetimes: true, rs: "& 'a mut i32", is_copy: false },
7079 Test { cc: "const int*", lifetimes: true, rs: "Option < & 'a i32 >", is_copy: true },
7080 Test { cc: "int*", lifetimes: true, rs: "Option < & 'a mut i32 >", is_copy: false },
7081 Test { cc: "const int*", lifetimes: false, rs: "* const i32", is_copy: true },
7082 Test { cc: "int*", lifetimes: false, rs: "* mut i32", is_copy: true },
Googler73021682022-10-28 02:39:25 -07007083 Test {
7084 cc: "void*",
7085 lifetimes: false,
7086 rs: "* mut :: std :: os :: raw :: c_void",
7087 is_copy: true,
7088 },
7089 Test {
7090 cc: "const void*",
7091 lifetimes: false,
7092 rs: "* const :: std :: os :: raw :: c_void",
7093 is_copy: true,
7094 },
7095 Test {
7096 cc: "void* const*",
7097 lifetimes: false,
7098 rs: "* const * mut :: std :: os :: raw :: c_void",
7099 is_copy: true,
7100 },
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00007101 // Tests below have been thought-through and verified "manually".
Lukasz Anforowicz20651e32022-02-10 14:52:15 +00007102 // TrivialStruct is expected to derive Copy.
Marcel Hlopko36234892022-05-10 00:39:54 -07007103 Test {
7104 cc: "TrivialStruct",
7105 lifetimes: true,
Lukasz Anforowiczae9be082022-10-05 07:33:57 -07007106 rs: "crate :: TrivialStruct",
Marcel Hlopko36234892022-05-10 00:39:54 -07007107 is_copy: true,
7108 },
Lukasz Anforowicz20651e32022-02-10 14:52:15 +00007109 Test {
7110 cc: "UserDefinedCopyConstructor",
7111 lifetimes: true,
Lukasz Anforowiczae9be082022-10-05 07:33:57 -07007112 rs: "crate :: UserDefinedCopyConstructor",
Lukasz Anforowicz20651e32022-02-10 14:52:15 +00007113 is_copy: false,
7114 },
Lukasz Anforowiczae9be082022-10-05 07:33:57 -07007115 Test { cc: "IntAlias", lifetimes: true, rs: "crate :: IntAlias", is_copy: true },
Googler84cd5602022-10-07 08:25:46 -07007116 Test {
7117 cc: "TrivialAlias",
7118 lifetimes: true,
7119 rs: "crate :: TrivialAlias",
7120 is_copy: true,
7121 },
Marcel Hlopko36234892022-05-10 00:39:54 -07007122 Test {
7123 cc: "NonTrivialAlias",
7124 lifetimes: true,
Lukasz Anforowiczae9be082022-10-05 07:33:57 -07007125 rs: "crate :: NonTrivialAlias",
Marcel Hlopko36234892022-05-10 00:39:54 -07007126 is_copy: false,
7127 },
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00007128 ];
Lukasz Anforowicz20651e32022-02-10 14:52:15 +00007129 for test in tests.iter() {
7130 let test_name = format!("cc='{}', lifetimes={}", test.cc, test.lifetimes);
7131 let cc_input = template.replace("PARAM_TYPE", test.cc).replace(
7132 "LIFETIMES",
7133 if test.lifetimes { "#pragma clang lifetime_elision" } else { "" },
7134 );
Devin Jeanpierre409f6f62022-07-07 02:00:26 -07007135 let db = db_from_cc(&cc_input)?;
7136 let ir = db.ir();
7137
Lukasz Anforowicz9c663ca2022-02-09 01:33:31 +00007138 let f = retrieve_func(&ir, "func");
Devin Jeanpierre409f6f62022-07-07 02:00:26 -07007139 let t = db.rs_type_kind(f.params[0].type_.rs_type.clone())?;
Lukasz Anforowicz20651e32022-02-10 14:52:15 +00007140
Lukasz Anforowiczae9be082022-10-05 07:33:57 -07007141 let fmt = t.to_token_stream().to_string();
Lukasz Anforowicz20651e32022-02-10 14:52:15 +00007142 assert_eq!(test.rs, fmt, "Testing: {}", test_name);
7143
7144 assert_eq!(test.is_copy, t.implements_copy(), "Testing: {}", test_name);
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00007145 }
7146 Ok(())
7147 }
Lukasz Anforowicza94ab702022-01-14 22:40:25 +00007148
7149 #[test]
7150 fn test_rs_type_kind_is_shared_ref_to_with_lifetimes() -> Result<()> {
Devin Jeanpierre409f6f62022-07-07 02:00:26 -07007151 let db = db_from_cc(
Lukasz Anforowicza94ab702022-01-14 22:40:25 +00007152 "#pragma clang lifetime_elision
7153 struct SomeStruct {};
7154 void foo(const SomeStruct& foo_param);
7155 void bar(SomeStruct& bar_param);",
7156 )?;
Devin Jeanpierre409f6f62022-07-07 02:00:26 -07007157 let ir = db.ir();
Lukasz Anforowicza94ab702022-01-14 22:40:25 +00007158 let record = ir.records().next().unwrap();
Lukasz Anforowicz9c663ca2022-02-09 01:33:31 +00007159 let foo_func = retrieve_func(&ir, "foo");
7160 let bar_func = retrieve_func(&ir, "bar");
Lukasz Anforowicza94ab702022-01-14 22:40:25 +00007161
7162 // const-ref + lifetimes in C++ ===> shared-ref in Rust
7163 assert_eq!(foo_func.params.len(), 1);
7164 let foo_param = &foo_func.params[0];
7165 assert_eq!(&foo_param.identifier.identifier, "foo_param");
Devin Jeanpierre409f6f62022-07-07 02:00:26 -07007166 let foo_type = db.rs_type_kind(foo_param.type_.rs_type.clone())?;
Lukasz Anforowicza94ab702022-01-14 22:40:25 +00007167 assert!(foo_type.is_shared_ref_to(record));
7168 assert!(matches!(foo_type, RsTypeKind::Reference { mutability: Mutability::Const, .. }));
7169
7170 // non-const-ref + lifetimes in C++ ===> mutable-ref in Rust
7171 assert_eq!(bar_func.params.len(), 1);
7172 let bar_param = &bar_func.params[0];
7173 assert_eq!(&bar_param.identifier.identifier, "bar_param");
Devin Jeanpierre409f6f62022-07-07 02:00:26 -07007174 let bar_type = db.rs_type_kind(bar_param.type_.rs_type.clone())?;
Lukasz Anforowicza94ab702022-01-14 22:40:25 +00007175 assert!(!bar_type.is_shared_ref_to(record));
7176 assert!(matches!(bar_type, RsTypeKind::Reference { mutability: Mutability::Mut, .. }));
7177
7178 Ok(())
7179 }
7180
7181 #[test]
7182 fn test_rs_type_kind_is_shared_ref_to_without_lifetimes() -> Result<()> {
Devin Jeanpierre409f6f62022-07-07 02:00:26 -07007183 let db = db_from_cc(
Lukasz Anforowicza94ab702022-01-14 22:40:25 +00007184 "struct SomeStruct {};
7185 void foo(const SomeStruct& foo_param);",
7186 )?;
Devin Jeanpierre409f6f62022-07-07 02:00:26 -07007187 let ir = db.ir();
Lukasz Anforowicza94ab702022-01-14 22:40:25 +00007188 let record = ir.records().next().unwrap();
Lukasz Anforowicz9c663ca2022-02-09 01:33:31 +00007189 let foo_func = retrieve_func(&ir, "foo");
Lukasz Anforowicza94ab702022-01-14 22:40:25 +00007190
7191 // const-ref + *no* lifetimes in C++ ===> const-pointer in Rust
7192 assert_eq!(foo_func.params.len(), 1);
7193 let foo_param = &foo_func.params[0];
7194 assert_eq!(&foo_param.identifier.identifier, "foo_param");
Devin Jeanpierre409f6f62022-07-07 02:00:26 -07007195 let foo_type = db.rs_type_kind(foo_param.type_.rs_type.clone())?;
Lukasz Anforowicza94ab702022-01-14 22:40:25 +00007196 assert!(!foo_type.is_shared_ref_to(record));
7197 assert!(matches!(foo_type, RsTypeKind::Pointer { mutability: Mutability::Const, .. }));
7198
7199 Ok(())
7200 }
Marcel Hlopkoeaae9b72022-01-21 15:54:11 +00007201
7202 #[test]
Lukasz Anforowicz90bdb962022-02-14 21:07:45 +00007203 fn test_rs_type_kind_dfs_iter_ordering() {
7204 // Set up a test input representing: A<B<C>, D<E>>.
7205 let a = {
7206 let b = {
Devin Jeanpierreb2b6cf82022-07-07 01:49:27 -07007207 let c = RsTypeKind::Other { name: "C".into(), type_args: Rc::from([]) };
7208 RsTypeKind::Other { name: "B".into(), type_args: Rc::from([c]) }
Lukasz Anforowicz90bdb962022-02-14 21:07:45 +00007209 };
7210 let d = {
Devin Jeanpierreb2b6cf82022-07-07 01:49:27 -07007211 let e = RsTypeKind::Other { name: "E".into(), type_args: Rc::from([]) };
7212 RsTypeKind::Other { name: "D".into(), type_args: Rc::from([e]) }
Lukasz Anforowicz90bdb962022-02-14 21:07:45 +00007213 };
Devin Jeanpierreb2b6cf82022-07-07 01:49:27 -07007214 RsTypeKind::Other { name: "A".into(), type_args: Rc::from([b, d]) }
Lukasz Anforowicz90bdb962022-02-14 21:07:45 +00007215 };
7216 let dfs_names = a
7217 .dfs_iter()
7218 .map(|t| match t {
Devin Jeanpierre8cd8ae72022-06-29 18:59:40 -07007219 RsTypeKind::Other { name, .. } => &**name,
Lukasz Anforowicz90bdb962022-02-14 21:07:45 +00007220 _ => unreachable!("Only 'other' types are used in this test"),
7221 })
7222 .collect_vec();
7223 assert_eq!(vec!["A", "B", "C", "D", "E"], dfs_names);
7224 }
7225
7226 #[test]
Lukasz Anforowiczcf230fd2022-02-18 19:20:39 +00007227 fn test_rs_type_kind_dfs_iter_ordering_for_func_ptr() {
7228 // Set up a test input representing: fn(A, B) -> C
7229 let f = {
Devin Jeanpierreb2b6cf82022-07-07 01:49:27 -07007230 let a = RsTypeKind::Other { name: "A".into(), type_args: Rc::from(&[][..]) };
7231 let b = RsTypeKind::Other { name: "B".into(), type_args: Rc::from(&[][..]) };
7232 let c = RsTypeKind::Other { name: "C".into(), type_args: Rc::from(&[][..]) };
Devin Jeanpierre8cd8ae72022-06-29 18:59:40 -07007233 RsTypeKind::FuncPtr {
7234 abi: "blah".into(),
Devin Jeanpierreb2b6cf82022-07-07 01:49:27 -07007235 param_types: Rc::from([a, b]),
7236 return_type: Rc::new(c),
Devin Jeanpierre8cd8ae72022-06-29 18:59:40 -07007237 }
Lukasz Anforowiczcf230fd2022-02-18 19:20:39 +00007238 };
7239 let dfs_names = f
7240 .dfs_iter()
7241 .map(|t| match t {
7242 RsTypeKind::FuncPtr { .. } => "fn",
Devin Jeanpierre8cd8ae72022-06-29 18:59:40 -07007243 RsTypeKind::Other { name, .. } => &**name,
Lukasz Anforowiczcf230fd2022-02-18 19:20:39 +00007244 _ => unreachable!("Only FuncPtr and Other kinds are used in this test"),
7245 })
7246 .collect_vec();
7247 assert_eq!(vec!["fn", "A", "B", "C"], dfs_names);
7248 }
7249
7250 #[test]
Lukasz Anforowicz90bdb962022-02-14 21:07:45 +00007251 fn test_rs_type_kind_lifetimes() -> Result<()> {
Devin Jeanpierre409f6f62022-07-07 02:00:26 -07007252 let db = db_from_cc(
Lukasz Anforowicz90bdb962022-02-14 21:07:45 +00007253 r#"
7254 #pragma clang lifetime_elision
7255 using TypeAlias = int&;
7256 struct SomeStruct {};
Devin Jeanpierre48cb5bc2022-06-02 00:50:43 -07007257 void foo(int a, int& b, int&& c, int* d, int** e, TypeAlias f, SomeStruct g); "#,
Lukasz Anforowicz90bdb962022-02-14 21:07:45 +00007258 )?;
Devin Jeanpierre409f6f62022-07-07 02:00:26 -07007259 let ir = db.ir();
Devin Jeanpierre48cb5bc2022-06-02 00:50:43 -07007260 let func = retrieve_func(&ir, "foo");
Devin Jeanpierre409f6f62022-07-07 02:00:26 -07007261 let ret = db.rs_type_kind(func.return_type.rs_type.clone())?;
7262 let a = db.rs_type_kind(func.params[0].type_.rs_type.clone())?;
7263 let b = db.rs_type_kind(func.params[1].type_.rs_type.clone())?;
7264 let c = db.rs_type_kind(func.params[2].type_.rs_type.clone())?;
7265 let d = db.rs_type_kind(func.params[3].type_.rs_type.clone())?;
7266 let e = db.rs_type_kind(func.params[4].type_.rs_type.clone())?;
7267 let f = db.rs_type_kind(func.params[5].type_.rs_type.clone())?;
7268 let g = db.rs_type_kind(func.params[6].type_.rs_type.clone())?;
Lukasz Anforowicz90bdb962022-02-14 21:07:45 +00007269
7270 assert_eq!(0, ret.lifetimes().count()); // No lifetimes on `void`.
7271 assert_eq!(0, a.lifetimes().count()); // No lifetimes on `int`.
7272 assert_eq!(1, b.lifetimes().count()); // `&'a i32` has a single lifetime.
Devin Jeanpierre48cb5bc2022-06-02 00:50:43 -07007273 assert_eq!(1, c.lifetimes().count()); // `RvalueReference<'a, i32>` has a single lifetime.
7274 assert_eq!(1, d.lifetimes().count()); // `Option<&'b i32>` has a single lifetime.
7275 assert_eq!(2, e.lifetimes().count()); // `&'c Option<&'d i32>` has two lifetimes.
7276 assert_eq!(1, f.lifetimes().count()); // Lifetime of underlying type should show through.
7277 assert_eq!(0, g.lifetimes().count()); // No lifetimes on structs (yet).
Lukasz Anforowicz90bdb962022-02-14 21:07:45 +00007278 Ok(())
7279 }
7280
7281 #[test]
7282 fn test_rs_type_kind_lifetimes_raw_ptr() -> Result<()> {
Devin Jeanpierre409f6f62022-07-07 02:00:26 -07007283 let db = db_from_cc("void foo(int* a);")?;
7284 let ir = db.ir();
Lukasz Anforowicz90bdb962022-02-14 21:07:45 +00007285 let f = retrieve_func(&ir, "foo");
Devin Jeanpierre409f6f62022-07-07 02:00:26 -07007286 let a = db.rs_type_kind(f.params[0].type_.rs_type.clone())?;
Lukasz Anforowicz90bdb962022-02-14 21:07:45 +00007287 assert_eq!(0, a.lifetimes().count()); // No lifetimes on `int*`.
7288 Ok(())
7289 }
7290
7291 #[test]
Marcel Hlopkoeaae9b72022-01-21 15:54:11 +00007292 fn test_rust_keywords_are_escaped_in_rs_api_file() -> Result<()> {
7293 let ir = ir_from_cc("struct type { int dyn; };")?;
Devin Jeanpierree9850a72022-06-29 19:04:48 -07007294 let rs_api = generate_bindings_tokens(ir)?.rs_api;
Marcel Hlopkoeaae9b72022-01-21 15:54:11 +00007295 assert_rs_matches!(rs_api, quote! { struct r#type { ... r#dyn: i32 ... } });
7296 Ok(())
7297 }
7298
7299 #[test]
7300 fn test_rust_keywords_are_not_escaped_in_rs_api_impl_file() -> Result<()> {
7301 let ir = ir_from_cc("struct type { int dyn; };")?;
Devin Jeanpierree9850a72022-06-29 19:04:48 -07007302 let rs_api_impl = generate_bindings_tokens(ir)?.rs_api_impl;
Lukasz Anforowicz4ee9c222022-04-13 09:33:36 -07007303 assert_cc_matches!(
7304 rs_api_impl,
Lukasz Anforowiczd4742ff2022-07-11 17:05:02 -07007305 quote! { static_assert(CRUBIT_OFFSET_OF(dyn, struct type) ... ) }
Lukasz Anforowicz4ee9c222022-04-13 09:33:36 -07007306 );
Marcel Hlopkoeaae9b72022-01-21 15:54:11 +00007307 Ok(())
7308 }
Marcel Hlopko14ee3c82022-02-09 09:46:23 +00007309
7310 #[test]
7311 fn test_no_aligned_attr() {
7312 let ir = ir_from_cc("struct SomeStruct {};").unwrap();
Devin Jeanpierree9850a72022-06-29 19:04:48 -07007313 let rs_api = generate_bindings_tokens(ir).unwrap().rs_api;
Marcel Hlopko14ee3c82022-02-09 09:46:23 +00007314
7315 assert_rs_matches! {rs_api, quote! {
7316 #[repr(C)]
7317 pub struct SomeStruct { ... }
7318 }};
7319 }
7320
7321 #[test]
7322 fn test_aligned_attr() {
7323 let ir = ir_from_cc("struct SomeStruct {} __attribute__((aligned(64)));").unwrap();
Devin Jeanpierree9850a72022-06-29 19:04:48 -07007324 let rs_api = generate_bindings_tokens(ir).unwrap().rs_api;
Marcel Hlopko14ee3c82022-02-09 09:46:23 +00007325
7326 assert_rs_matches! {rs_api, quote! {
7327 #[repr(C, align(64))]
7328 pub struct SomeStruct { ... }
7329 }
7330 };
7331 }
Devin Jeanpierre149950d2022-02-22 21:02:02 +00007332
7333 /// !Unpin references should not be pinned.
7334 #[test]
7335 fn test_nonunpin_ref_param() -> Result<()> {
Devin Jeanpierree9850a72022-06-29 19:04:48 -07007336 let rs_api = generate_bindings_tokens(ir_from_cc(
Devin Jeanpierre149950d2022-02-22 21:02:02 +00007337 r#"
7338 #pragma clang lifetime_elision
7339 struct S {~S();};
7340 void Function(const S& s);
7341 "#,
Devin Jeanpierre4f06f832022-04-26 15:51:30 -07007342 )?)?
7343 .rs_api;
Devin Jeanpierre149950d2022-02-22 21:02:02 +00007344 assert_rs_matches!(
Devin Jeanpierre448322b2022-04-26 15:43:40 -07007345 rs_api,
Devin Jeanpierre149950d2022-02-22 21:02:02 +00007346 quote! {
Rosica Dejanovska8283fe62022-05-09 10:04:28 -07007347 fn Function<'a>(s: &'a crate::S) { ... }
Devin Jeanpierre149950d2022-02-22 21:02:02 +00007348 }
7349 );
7350 Ok(())
7351 }
7352
7353 /// !Unpin mut references must be pinned.
7354 #[test]
7355 fn test_nonunpin_mut_param() -> Result<()> {
Devin Jeanpierree9850a72022-06-29 19:04:48 -07007356 let rs_api = generate_bindings_tokens(ir_from_cc(
Devin Jeanpierre149950d2022-02-22 21:02:02 +00007357 r#"
7358 #pragma clang lifetime_elision
7359 struct S {~S();};
7360 void Function(S& s);
7361 "#,
Devin Jeanpierre4f06f832022-04-26 15:51:30 -07007362 )?)?
7363 .rs_api;
Devin Jeanpierre149950d2022-02-22 21:02:02 +00007364 assert_rs_matches!(
Devin Jeanpierre448322b2022-04-26 15:43:40 -07007365 rs_api,
Devin Jeanpierre149950d2022-02-22 21:02:02 +00007366 quote! {
Devin Jeanpierrecd5ebf32022-07-08 05:31:55 -07007367 fn Function<'a>(s: ::std::pin::Pin<&'a mut crate::S>) { ... }
Devin Jeanpierre149950d2022-02-22 21:02:02 +00007368 }
7369 );
7370 Ok(())
7371 }
7372
7373 /// !Unpin &self should not be pinned.
7374 #[test]
7375 fn test_nonunpin_ref_self() -> Result<()> {
Devin Jeanpierree9850a72022-06-29 19:04:48 -07007376 let rs_api = generate_bindings_tokens(ir_from_cc(
Devin Jeanpierre149950d2022-02-22 21:02:02 +00007377 r#"
7378 #pragma clang lifetime_elision
7379 struct S {
7380 ~S();
7381 void Function() const;
7382 };
7383 "#,
Devin Jeanpierre4f06f832022-04-26 15:51:30 -07007384 )?)?
7385 .rs_api;
Devin Jeanpierre149950d2022-02-22 21:02:02 +00007386 assert_rs_matches!(
Devin Jeanpierre448322b2022-04-26 15:43:40 -07007387 rs_api,
Devin Jeanpierre149950d2022-02-22 21:02:02 +00007388 quote! {
7389 fn Function<'a>(&'a self) { ... }
7390 }
7391 );
7392 Ok(())
7393 }
7394
7395 /// !Unpin &mut self must be pinned.
7396 #[test]
7397 fn test_nonunpin_mut_self() -> Result<()> {
Devin Jeanpierree9850a72022-06-29 19:04:48 -07007398 let rs_api = generate_bindings_tokens(ir_from_cc(
Devin Jeanpierre149950d2022-02-22 21:02:02 +00007399 r#"
7400 #pragma clang lifetime_elision
7401 struct S {
7402 ~S();
7403 void Function();
7404 };
7405 "#,
Devin Jeanpierre4f06f832022-04-26 15:51:30 -07007406 )?)?
7407 .rs_api;
Devin Jeanpierre149950d2022-02-22 21:02:02 +00007408 assert_rs_matches!(
Devin Jeanpierre448322b2022-04-26 15:43:40 -07007409 rs_api,
Devin Jeanpierre149950d2022-02-22 21:02:02 +00007410 quote! {
Devin Jeanpierrecd5ebf32022-07-08 05:31:55 -07007411 fn Function<'a>(self: ::std::pin::Pin<&'a mut Self>) { ... }
Devin Jeanpierre149950d2022-02-22 21:02:02 +00007412 }
7413 );
7414 Ok(())
7415 }
7416
7417 /// Drop::drop must not use self : Pin<...>.
7418 #[test]
7419 fn test_nonunpin_drop() -> Result<()> {
Devin Jeanpierree9850a72022-06-29 19:04:48 -07007420 let rs_api = generate_bindings_tokens(ir_from_cc(
Devin Jeanpierre149950d2022-02-22 21:02:02 +00007421 r#"
7422 struct S {~S();};
7423 "#,
Devin Jeanpierre4f06f832022-04-26 15:51:30 -07007424 )?)?
7425 .rs_api;
Devin Jeanpierre149950d2022-02-22 21:02:02 +00007426 assert_rs_matches!(
Devin Jeanpierre448322b2022-04-26 15:43:40 -07007427 rs_api,
Devin Jeanpierre149950d2022-02-22 21:02:02 +00007428 quote! {
Devin Jeanpierrecd5ebf32022-07-08 05:31:55 -07007429 unsafe fn pinned_drop<'a>(self: ::std::pin::Pin<&'a mut Self>) { ... }
Devin Jeanpierre149950d2022-02-22 21:02:02 +00007430 }
7431 );
7432 Ok(())
7433 }
Devin Jeanpierre6f607372022-03-22 21:34:38 +00007434
7435 #[test]
Devin Jeanpierread125742022-04-11 13:50:28 -07007436 fn test_nonunpin_0_arg_constructor() -> Result<()> {
7437 let ir = ir_from_cc(
7438 r#"#pragma clang lifetime_elision
7439 // This type must be `!Unpin`.
7440 struct HasConstructor {explicit HasConstructor() {}};"#,
7441 )?;
Devin Jeanpierree9850a72022-06-29 19:04:48 -07007442 let rs_api = generate_bindings_tokens(ir)?.rs_api;
Devin Jeanpierrecd5ebf32022-07-08 05:31:55 -07007443 assert_rs_matches!(rs_api, quote! {#[::ctor::recursively_pinned]});
Devin Jeanpierread125742022-04-11 13:50:28 -07007444 assert_rs_matches!(
7445 rs_api,
7446 quote! {
Devin Jeanpierrecd5ebf32022-07-08 05:31:55 -07007447 impl ::ctor::CtorNew<()> for HasConstructor {
7448 type CtorType = impl ::ctor::Ctor<Output = Self>;
Devin Jeanpierread125742022-04-11 13:50:28 -07007449
7450 #[inline (always)]
7451 fn ctor_new(args: ()) -> Self::CtorType {
7452 let () = args;
Devin Jeanpierre93927e82022-08-01 14:05:54 -07007453 unsafe {
7454 ::ctor::FnCtor::new(move |dest: ::std::pin::Pin<&mut ::std::mem::MaybeUninit<crate::HasConstructor>>| {
Devin Jeanpierrecd5ebf32022-07-08 05:31:55 -07007455 crate::detail::__rust_thunk___ZN14HasConstructorC1Ev(::std::pin::Pin::into_inner_unchecked(dest));
Devin Jeanpierre93927e82022-08-01 14:05:54 -07007456 })
7457 }
Devin Jeanpierread125742022-04-11 13:50:28 -07007458 }
7459 }
7460 }
7461 );
7462 Ok(())
7463 }
7464
7465 #[test]
7466 fn test_nonunpin_1_arg_constructor() -> Result<()> {
Devin Jeanpierre6f607372022-03-22 21:34:38 +00007467 let ir = ir_from_cc(
7468 r#"#pragma clang lifetime_elision
7469 // This type must be `!Unpin`.
7470 struct HasConstructor {explicit HasConstructor(unsigned char input) {}};"#,
7471 )?;
Devin Jeanpierree9850a72022-06-29 19:04:48 -07007472 let rs_api = generate_bindings_tokens(ir)?.rs_api;
Devin Jeanpierrecd5ebf32022-07-08 05:31:55 -07007473 assert_rs_matches!(rs_api, quote! {#[::ctor::recursively_pinned]});
Devin Jeanpierre6f607372022-03-22 21:34:38 +00007474 assert_rs_matches!(
7475 rs_api,
7476 quote! {
Devin Jeanpierrecd5ebf32022-07-08 05:31:55 -07007477 impl ::ctor::CtorNew<u8> for HasConstructor {
7478 type CtorType = impl ::ctor::Ctor<Output = Self>;
Devin Jeanpierre6f607372022-03-22 21:34:38 +00007479
7480 #[inline (always)]
Devin Jeanpierread125742022-04-11 13:50:28 -07007481 fn ctor_new(args: u8) -> Self::CtorType {
7482 let input = args;
Devin Jeanpierre93927e82022-08-01 14:05:54 -07007483 unsafe {
7484 ::ctor::FnCtor::new(move |dest: ::std::pin::Pin<&mut ::std::mem::MaybeUninit<crate::HasConstructor>>| {
Devin Jeanpierrecd5ebf32022-07-08 05:31:55 -07007485 crate::detail::__rust_thunk___ZN14HasConstructorC1Eh(::std::pin::Pin::into_inner_unchecked(dest), input);
Devin Jeanpierre93927e82022-08-01 14:05:54 -07007486 })
7487 }
Devin Jeanpierre6f607372022-03-22 21:34:38 +00007488 }
7489 }
7490 }
7491 );
7492 Ok(())
7493 }
Devin Jeanpierread125742022-04-11 13:50:28 -07007494
7495 #[test]
7496 fn test_nonunpin_2_arg_constructor() -> Result<()> {
7497 let ir = ir_from_cc(
7498 r#"#pragma clang lifetime_elision
7499 // This type must be `!Unpin`.
7500 struct HasConstructor {explicit HasConstructor(unsigned char input1, signed char input2) {}};"#,
7501 )?;
Devin Jeanpierree9850a72022-06-29 19:04:48 -07007502 let rs_api = generate_bindings_tokens(ir)?.rs_api;
Devin Jeanpierrecd5ebf32022-07-08 05:31:55 -07007503 assert_rs_matches!(rs_api, quote! {#[::ctor::recursively_pinned]});
Devin Jeanpierread125742022-04-11 13:50:28 -07007504 assert_rs_matches!(
7505 rs_api,
7506 quote! {
Devin Jeanpierrecd5ebf32022-07-08 05:31:55 -07007507 impl ::ctor::CtorNew<(u8, i8)> for HasConstructor {
7508 type CtorType = impl ::ctor::Ctor<Output = Self>;
Devin Jeanpierread125742022-04-11 13:50:28 -07007509
7510 #[inline (always)]
7511 fn ctor_new(args: (u8, i8)) -> Self::CtorType {
7512 let (input1, input2) = args;
Devin Jeanpierre93927e82022-08-01 14:05:54 -07007513 unsafe {
7514 ::ctor::FnCtor::new(move |dest: ::std::pin::Pin<&mut ::std::mem::MaybeUninit<crate::HasConstructor>>| {
Devin Jeanpierrecd5ebf32022-07-08 05:31:55 -07007515 crate::detail::__rust_thunk___ZN14HasConstructorC1Eha(::std::pin::Pin::into_inner_unchecked(dest), input1, input2);
Devin Jeanpierre93927e82022-08-01 14:05:54 -07007516 })
7517 }
Devin Jeanpierread125742022-04-11 13:50:28 -07007518 }
7519 }
7520 }
7521 );
7522 Ok(())
7523 }
Rosica Dejanovskadd9a9032022-04-12 07:34:41 -07007524
Devin Jeanpierre6bb81802022-08-10 02:08:47 -07007525 /// Traits which monomorphize the `Ctor` parameter into the caller must
7526 /// synthesize an RvalueReference parameter, with an appropriate
7527 /// lifetime parameter.
7528 #[test]
7529 fn test_nonunpin_by_value_params() -> Result<()> {
7530 let ir = ir_from_cc(
7531 r#"#pragma clang lifetime_elision
7532 // This type must be `!Unpin`.
7533 struct HasConstructor {
7534 // int& x is here to create a 'b lifetime, which collides with a synthesized
7535 // lifetime name. But that's OK! We handle collisions!
7536 // (`a` would also work, but that's just because the left hand doesn't know what
7537 // the right is doing: the `a` lifetime is present in some places, but eventually
7538 // removed from the public interface.)
7539 explicit HasConstructor(const int& x, HasConstructor y, HasConstructor b) {}
7540 };"#,
7541 )?;
7542 let rs_api = generate_bindings_tokens(ir)?.rs_api;
7543 assert_rs_matches!(rs_api, quote! {#[::ctor::recursively_pinned]});
7544 assert_rs_matches!(
7545 rs_api,
7546 quote! {
7547 impl <'b, 'y, 'b_2> ::ctor::CtorNew<(
7548 &'b i32,
7549 ::ctor::RvalueReference<'y, crate::HasConstructor>,
7550 ::ctor::RvalueReference<'b_2, crate::HasConstructor>)
7551 > for HasConstructor {
7552 // The captures are why we need explicit lifetimes for the two rvalue reference
7553 // parameters.
7554 type CtorType = impl ::ctor::Ctor<Output = Self>
7555 + ::ctor::Captures<'b>
7556 + ::ctor::Captures<'y>
7557 + ::ctor::Captures<'b_2>;
7558
7559 #[inline (always)]
7560 fn ctor_new(args: (
7561 &'b i32,
7562 ::ctor::RvalueReference<'y, crate::HasConstructor>,
7563 ::ctor::RvalueReference<'b_2, crate::HasConstructor>)
7564 ) -> Self::CtorType {
7565 let (x, y, b) = args;
7566 unsafe {
7567 ::ctor::FnCtor::new(move |dest: ::std::pin::Pin<&mut ::std::mem::MaybeUninit<crate::HasConstructor>>| {
7568 crate::detail::__rust_thunk___ZN14HasConstructorC1ERKiS_S_(::std::pin::Pin::into_inner_unchecked(dest), x, y, b);
7569 })
7570 }
7571 }
7572 }
7573 }
7574 );
7575 Ok(())
7576 }
7577
Rosica Dejanovskadd9a9032022-04-12 07:34:41 -07007578 #[test]
Devin Jeanpierreb8ce2c12022-07-13 10:34:01 -07007579 fn test_nonunpin_return() -> Result<()> {
7580 let ir = ir_from_cc(
7581 r#"#pragma clang lifetime_elision
7582 // This type must be `!Unpin`.
7583 struct Nontrivial {~Nontrivial();};
Lukasz Anforowicz07b33902022-07-13 15:27:03 -07007584
Devin Jeanpierreb8ce2c12022-07-13 10:34:01 -07007585 Nontrivial ReturnsByValue(const int& x, const int& y);
7586 "#,
7587 )?;
7588 let BindingsTokens { rs_api, rs_api_impl } = generate_bindings_tokens(ir)?;
7589 assert_rs_matches!(
7590 rs_api,
7591 quote! {
7592 pub fn ReturnsByValue<'a, 'b>(x: &'a i32, y: &'b i32)
Devin Jeanpierre83cb2dc2022-07-15 01:50:28 -07007593 -> impl ::ctor::Ctor<Output=crate::Nontrivial>
7594 + ::ctor::Captures<'a>
7595 + ::ctor::Captures<'b> {
Devin Jeanpierreb8ce2c12022-07-13 10:34:01 -07007596 unsafe {
7597 ::ctor::FnCtor::new(move |dest: ::std::pin::Pin<&mut ::std::mem::MaybeUninit<crate::Nontrivial>>| {
7598 crate::detail::__rust_thunk___Z14ReturnsByValueRKiS0_(::std::pin::Pin::into_inner_unchecked(dest), x, y);
7599 })
7600 }
7601
7602 }
7603 }
7604 );
7605
7606 assert_cc_matches!(
7607 rs_api_impl,
7608 quote! {
Lukasz Anforowicz07b33902022-07-13 15:27:03 -07007609 extern "C" void __rust_thunk___Z14ReturnsByValueRKiS0_(
7610 struct Nontrivial* __return, int const* x, int const* y) {
Devin Jeanpierre521b4ee2022-07-27 07:19:50 -07007611 new(__return) auto(ReturnsByValue(*x, *y));
Devin Jeanpierreb8ce2c12022-07-13 10:34:01 -07007612 }
7613 }
7614 );
7615 Ok(())
7616 }
7617
7618 /// Assignment is special in that it discards the return type.
7619 /// So if the return type is !Unpin, it needs to emplace!() it.
7620 #[test]
Devin Jeanpierre11ce2b92022-07-27 07:54:21 -07007621 fn test_nonunpin_return_assign() -> Result<()> {
Devin Jeanpierreb8ce2c12022-07-13 10:34:01 -07007622 let ir = ir_from_cc(
7623 r#"#pragma clang lifetime_elision
7624 // This type must be `!Unpin`.
7625 struct Nontrivial {
7626 ~Nontrivial();
7627 Nontrivial operator=(const Nontrivial& other);
7628 };
7629 "#,
7630 )?;
7631 let BindingsTokens { rs_api, rs_api_impl } = generate_bindings_tokens(ir)?;
7632 assert_rs_matches!(
7633 rs_api,
7634 quote! {
7635 impl<'b> ::ctor::Assign<&'b crate::Nontrivial> for Nontrivial {
7636 #[inline(always)]
7637 fn assign<'a>(self: ::std::pin::Pin<&'a mut Self>, other: &'b crate::Nontrivial) {
7638 unsafe {
7639 let _ = ::ctor::emplace!(::ctor::FnCtor::new(
7640 move |dest: ::std::pin::Pin<&mut ::std::mem::MaybeUninit<crate::Nontrivial>>| {
7641 crate::detail::__rust_thunk___ZN10NontrivialaSERKS_(
7642 ::std::pin::Pin::into_inner_unchecked(dest),
7643 self,
7644 other
7645 );
7646 }
7647 ));
7648 }
7649 }
7650 }
7651 }
7652 );
7653
7654 assert_cc_matches!(
7655 rs_api_impl,
7656 quote! {
7657 extern "C" void __rust_thunk___ZN10NontrivialaSERKS_(
7658 struct Nontrivial* __return, struct Nontrivial* __this,
Lukasz Anforowicz07b33902022-07-13 15:27:03 -07007659 const struct Nontrivial* other
Devin Jeanpierreb8ce2c12022-07-13 10:34:01 -07007660 ) {
Devin Jeanpierre521b4ee2022-07-27 07:19:50 -07007661 new(__return) auto(__this->operator=(*other));
Devin Jeanpierreb8ce2c12022-07-13 10:34:01 -07007662 }
7663 }
7664 );
7665 Ok(())
7666 }
7667
7668 #[test]
Devin Jeanpierre11ce2b92022-07-27 07:54:21 -07007669 fn test_nonunpin_param() -> Result<()> {
7670 let ir = ir_from_cc(
7671 r#"#pragma clang lifetime_elision
7672 // This type must be `!Unpin`.
7673 struct Nontrivial {
7674 Nontrivial(Nontrivial&&);
7675 ~Nontrivial();
7676 };
7677
7678 void TakesByValue(Nontrivial x);
7679 "#,
7680 )?;
7681 let BindingsTokens { rs_api, rs_api_impl } = generate_bindings_tokens(ir)?;
7682 assert_rs_matches!(
7683 rs_api,
7684 quote! {
7685 pub fn TakesByValue(x: impl ::ctor::Ctor<Output=crate::Nontrivial>) {
7686 unsafe {
7687 crate::detail::__rust_thunk___Z12TakesByValue10Nontrivial(::std::pin::Pin::into_inner_unchecked(::ctor::emplace!(x)))
7688 }
7689 }
7690 }
7691 );
7692
7693 assert_cc_matches!(
7694 rs_api_impl,
7695 quote! {
7696 extern "C" void __rust_thunk___Z12TakesByValue10Nontrivial(struct Nontrivial*x) {
7697 TakesByValue(std::move(*x));
7698 }
7699 }
7700 );
7701 Ok(())
7702 }
7703
7704 #[test]
7705 fn test_nonunpin_trait_param() -> Result<()> {
7706 let ir = ir_from_cc(
7707 r#"#pragma clang lifetime_elision
7708 // This type must be `!Unpin`.
7709 struct Nontrivial {
7710 Nontrivial(Nontrivial&&);
7711 Nontrivial& operator=(Nontrivial) {}
7712 ~Nontrivial();
7713 };
Devin Jeanpierre2b1e46d2022-08-01 13:42:12 -07007714
7715 struct Trivial final {
7716 /*implicit*/ Trivial(Nontrivial) {}
7717 };
Devin Jeanpierre11ce2b92022-07-27 07:54:21 -07007718 "#,
7719 )?;
7720 let rs_api = generate_bindings_tokens(ir)?.rs_api;
Devin Jeanpierre2b1e46d2022-08-01 13:42:12 -07007721 assert_rs_matches!(
7722 rs_api,
7723 quote! {
Devin Jeanpierre6bb81802022-08-10 02:08:47 -07007724 impl<'__param_0> From<::ctor::RvalueReference<'__param_0, crate::Nontrivial>> for Trivial {
Devin Jeanpierre2b1e46d2022-08-01 13:42:12 -07007725 #[inline(always)]
Devin Jeanpierre6bb81802022-08-10 02:08:47 -07007726 fn from(__param_0: ::ctor::RvalueReference<'__param_0, crate::Nontrivial>) -> Self {
Devin Jeanpierre2b1e46d2022-08-01 13:42:12 -07007727 let mut tmp = ::std::mem::MaybeUninit::<Self>::zeroed();
7728 unsafe {
7729 crate::detail::__rust_thunk___ZN7TrivialC1E10Nontrivial(
7730 &mut tmp,
7731 __param_0
7732 );
7733 tmp.assume_init()
7734 }
7735 }
7736 }
7737 }
7738 );
Devin Jeanpierre11ce2b92022-07-27 07:54:21 -07007739 Ok(())
7740 }
7741
7742 #[test]
7743 fn test_nonmovable_param() -> Result<()> {
7744 let ir = ir_from_cc(
7745 r#"#pragma clang lifetime_elision
7746 // This type must be `!Unpin` and non-move constructible.
7747 struct Nonmovable {
7748 Nonmovable(Nonmovable&&) = delete;
7749 };
7750
7751 void TakesByValue(Nonmovable) {}
7752 "#,
7753 )?;
7754 let BindingsTokens { rs_api, rs_api_impl } = generate_bindings_tokens(ir)?;
7755 // Bindings for TakesByValue cannot be generated.
7756 assert_rs_not_matches!(rs_api, quote! {TakesByValue});
7757 assert_cc_not_matches!(rs_api_impl, quote! {TakesByValue});
7758 Ok(())
7759 }
7760
7761 #[test]
Lukasz Anforowicz07b33902022-07-13 15:27:03 -07007762 fn test_function_returning_rvalue_reference() -> Result<()> {
7763 let ir = ir_from_cc(
7764 r#"#pragma clang lifetime_elision
7765 struct SomeStruct final {
7766 // Inline to force generation (and test coverage) of C++ thunks.
7767 inline SomeStruct&& GetRValueReference() {
7768 return static_cast<SomeStruct&&>(*this);
7769 }
7770 int field;
7771 };
7772 "#,
7773 )?;
7774 let BindingsTokens { rs_api, rs_api_impl } = generate_bindings_tokens(ir)?;
7775 assert_rs_matches!(
7776 rs_api,
7777 quote! {
7778 impl SomeStruct {
7779 ...
7780 #[inline(always)]
7781 pub fn GetRValueReference<'a>(&'a mut self)
7782 -> ::ctor::RvalueReference<'a, crate::SomeStruct> {
7783 unsafe {
7784 crate::detail::__rust_thunk___ZN10SomeStruct18GetRValueReferenceEv(self)
7785 }
7786 }
7787 }
7788 }
7789 );
7790 assert_rs_matches!(
7791 rs_api,
7792 quote! {
7793 extern "C" {
7794 ...
7795 pub(crate) fn __rust_thunk___ZN10SomeStruct18GetRValueReferenceEv<'a>(
7796 __this: &'a mut crate::SomeStruct
7797 ) -> ::ctor::RvalueReference<'a, crate::SomeStruct>;
7798 ...
7799 }
7800 }
7801 );
7802
7803 // Note that you can't just convert directly from xvalue to lvalue:
7804 //
7805 // return &static_cast<SomeStruct&>(__this->GetRValueReference());
7806 //
7807 // For the above, Clang will emit an error that "non-const lvalue reference to
7808 // type 'struct SomeStruct' cannot bind to a temporary of type
7809 // 'SomeStruct'" (This is somewhat misleading, because there are no
7810 // temporaries here). We must first bind the return value to a name
7811 // (`lvalue` below), so that it becomes an lvalue. Only then can it be
7812 // converted to a pointer.
7813 assert_cc_matches!(
7814 rs_api_impl,
7815 quote! {
7816 extern "C" struct SomeStruct*
7817 __rust_thunk___ZN10SomeStruct18GetRValueReferenceEv(struct SomeStruct* __this) {
7818 struct SomeStruct&& lvalue = __this->GetRValueReference();
7819 return &lvalue;
7820 }
7821 }
7822 );
7823
7824 Ok(())
7825 }
7826
7827 #[test]
Devin Jeanpierrec0543eb2022-04-20 16:00:34 -07007828 fn test_forward_declared() -> Result<()> {
7829 let ir = ir_from_cc(
7830 r#"#pragma clang lifetime_elision
7831 struct ForwardDeclared;"#,
7832 )?;
Devin Jeanpierree9850a72022-06-29 19:04:48 -07007833 let rs_api = generate_bindings_tokens(ir)?.rs_api;
Devin Jeanpierrec0543eb2022-04-20 16:00:34 -07007834 assert_rs_matches!(
7835 rs_api,
7836 quote! {
7837 forward_declare::forward_declare!(pub ForwardDeclared = forward_declare::symbol!("ForwardDeclared"));
7838 }
7839 );
7840 assert_rs_not_matches!(rs_api, quote! {struct ForwardDeclared});
7841 Ok(())
7842 }
7843
7844 #[test]
Rosica Dejanovskadd9a9032022-04-12 07:34:41 -07007845 fn test_namespace_module_items() -> Result<()> {
Devin Jeanpierree9850a72022-06-29 19:04:48 -07007846 let rs_api = generate_bindings_tokens(ir_from_cc(
Rosica Dejanovskadd9a9032022-04-12 07:34:41 -07007847 r#"
7848 namespace test_namespace_bindings {
7849 int func();
7850 struct S {};
7851 namespace inner {
7852 int inner_func();
7853 struct InnerS {};
7854 }
7855 }
7856 "#,
Devin Jeanpierre4f06f832022-04-26 15:51:30 -07007857 )?)?
7858 .rs_api;
Rosica Dejanovskadd9a9032022-04-12 07:34:41 -07007859 assert_rs_matches!(
Devin Jeanpierre448322b2022-04-26 15:43:40 -07007860 rs_api,
Rosica Dejanovskadd9a9032022-04-12 07:34:41 -07007861 quote! {
7862 pub mod test_namespace_bindings {
7863 ...
7864 pub fn func() -> i32 { ... }
7865 ...
7866 pub struct S { ... }
7867 ...
7868 pub mod inner {
7869 ...
7870 pub fn inner_func() -> i32 { ... }
7871 ...
7872 pub struct InnerS { ... }
7873 ...
7874 }
7875 ...
7876 }
7877 }
7878 );
7879 Ok(())
7880 }
7881
7882 #[test]
Rosica Dejanovska5d9faaf2022-05-11 04:30:04 -07007883 fn test_detail_outside_of_namespace_module() -> Result<()> {
Devin Jeanpierree9850a72022-06-29 19:04:48 -07007884 let rs_api = generate_bindings_tokens(ir_from_cc(
Rosica Dejanovskadd9a9032022-04-12 07:34:41 -07007885 r#"
7886 namespace test_namespace_bindings {
7887 int f();
7888 }
7889 "#,
Devin Jeanpierre4f06f832022-04-26 15:51:30 -07007890 )?)?
7891 .rs_api;
Rosica Dejanovskadd9a9032022-04-12 07:34:41 -07007892 assert_rs_matches!(
Devin Jeanpierre448322b2022-04-26 15:43:40 -07007893 rs_api,
Rosica Dejanovskadd9a9032022-04-12 07:34:41 -07007894 quote! {
7895 pub mod test_namespace_bindings {
7896 ...
Rosica Dejanovskadd9a9032022-04-12 07:34:41 -07007897 }
Rosica Dejanovska5d9faaf2022-05-11 04:30:04 -07007898 ...
7899 mod detail {
7900 #[allow(unused_imports)]
7901 use super::*;
7902 extern "C" {
7903 #[link_name = "_ZN23test_namespace_bindings1fEv"]
7904 pub(crate) fn __rust_thunk___ZN23test_namespace_bindings1fEv() -> i32;
7905 }
7906 }
7907 ...
Rosica Dejanovskadd9a9032022-04-12 07:34:41 -07007908 }
7909 );
7910 Ok(())
7911 }
7912
7913 #[test]
Rosica Dejanovska5d9faaf2022-05-11 04:30:04 -07007914 fn test_assertions_outside_of_namespace_module() -> Result<()> {
Devin Jeanpierree9850a72022-06-29 19:04:48 -07007915 let rs_api = generate_bindings_tokens(ir_from_cc(
Rosica Dejanovskadd9a9032022-04-12 07:34:41 -07007916 r#"
7917 namespace test_namespace_bindings {
7918 struct S {
7919 int i;
7920 };
7921 }
7922 "#,
Devin Jeanpierre4f06f832022-04-26 15:51:30 -07007923 )?)?
7924 .rs_api;
Rosica Dejanovskadd9a9032022-04-12 07:34:41 -07007925 assert_rs_matches!(
Devin Jeanpierre448322b2022-04-26 15:43:40 -07007926 rs_api,
Rosica Dejanovskadd9a9032022-04-12 07:34:41 -07007927 quote! {
7928 pub mod test_namespace_bindings {
7929 ...
Rosica Dejanovskadd9a9032022-04-12 07:34:41 -07007930 }
Rosica Dejanovska5d9faaf2022-05-11 04:30:04 -07007931 ...
Devin Jeanpierrecd5ebf32022-07-08 05:31:55 -07007932 const _: () = assert!(::std::mem::size_of::<crate::test_namespace_bindings::S>() == 4);
7933 const _: () = assert!(::std::mem::align_of::<crate::test_namespace_bindings::S>() == 4);
Rosica Dejanovska5d9faaf2022-05-11 04:30:04 -07007934 ...
Lukasz Anforowiczed94b242022-09-07 11:47:58 -07007935 const _: () = assert!(memoffset::offset_of!(crate::test_namespace_bindings::S, i) == 0);
Rosica Dejanovskadd9a9032022-04-12 07:34:41 -07007936 }
7937 );
7938 Ok(())
7939 }
Rosica Dejanovska93aeafb2022-06-01 07:05:31 -07007940
7941 #[test]
7942 fn test_reopened_namespaces() -> Result<()> {
Devin Jeanpierree9850a72022-06-29 19:04:48 -07007943 let rs_api = generate_bindings_tokens(ir_from_cc(
Rosica Dejanovska93aeafb2022-06-01 07:05:31 -07007944 r#"
7945 namespace test_namespace_bindings {
7946 namespace inner {}
7947 } // namespace test_namespace_bindings
7948
7949 namespace test_namespace_bindings {
7950 namespace inner {}
7951 } // namespace test_namespace_bindings"#,
7952 )?)?
7953 .rs_api;
7954
7955 assert_rs_matches!(
7956 rs_api,
7957 quote! {
7958 ...
7959 pub mod test_namespace_bindings_0 {
7960 pub mod inner_0 {} ...
7961 }
7962 ...
7963 pub mod test_namespace_bindings {
7964 pub use super::test_namespace_bindings_0::*;
7965 ...
7966 pub mod inner {
7967 pub use super::inner_0::*;
7968 ...
7969 }
7970 }
7971 ...
7972 }
7973 );
7974 Ok(())
7975 }
Rosica Dejanovskaa08b3862022-06-02 08:22:49 -07007976
7977 #[test]
7978 fn test_qualified_identifiers_in_impl_file() -> Result<()> {
Devin Jeanpierree9850a72022-06-29 19:04:48 -07007979 let rs_api_impl = generate_bindings_tokens(ir_from_cc(
Rosica Dejanovskaa08b3862022-06-02 08:22:49 -07007980 r#"
7981 namespace test_namespace_bindings {
7982 inline void f() {};
Devin Jeanpierrecc61dad2022-07-19 01:40:09 -07007983 struct S final {};
Rosica Dejanovskaa08b3862022-06-02 08:22:49 -07007984 }
7985 inline void useS(test_namespace_bindings::S s) {};"#,
7986 )?)?
7987 .rs_api_impl;
7988
7989 assert_cc_matches!(
7990 rs_api_impl,
7991 quote! {
7992 extern "C" void __rust_thunk___ZN23test_namespace_bindings1fEv() {
7993 test_namespace_bindings::f();
7994 }
7995 ...
Rosica Dejanovskaa08b3862022-06-02 08:22:49 -07007996 extern "C" void __rust_thunk___Z4useSN23test_namespace_bindings1SE(
Lukasz Anforowicz07b33902022-07-13 15:27:03 -07007997 struct test_namespace_bindings::S s) { useS(s); }
Rosica Dejanovskaa08b3862022-06-02 08:22:49 -07007998 ...
7999 }
8000 );
8001 Ok(())
8002 }
Rosica Dejanovska4180ffe2022-06-09 06:01:05 -07008003
8004 #[test]
8005 fn test_inline_namespace() -> Result<()> {
Devin Jeanpierree9850a72022-06-29 19:04:48 -07008006 let rs_api = generate_bindings_tokens(ir_from_cc(
Rosica Dejanovska4180ffe2022-06-09 06:01:05 -07008007 r#"
8008 namespace test_namespace_bindings {
8009 inline namespace inner {
Devin Jeanpierrecc61dad2022-07-19 01:40:09 -07008010 struct MyStruct final {};
Rosica Dejanovska4180ffe2022-06-09 06:01:05 -07008011 }
8012 void processMyStruct(MyStruct s);
8013 }
8014 void processMyStructOutsideNamespace(test_namespace_bindings::inner::MyStruct s);
8015 void processMyStructSkipInlineNamespaceQualifier(test_namespace_bindings::MyStruct s);
8016 "#,
8017 )?)?
8018 .rs_api;
8019
8020 assert_rs_matches!(
8021 rs_api,
8022 quote! {
8023 ...
8024 pub mod test_namespace_bindings {
8025 ...
8026 pub mod inner {
8027 ...
8028 pub struct MyStruct {...} ...
8029 }
Devin Jeanpierrefe6aaea2022-09-09 12:33:50 -07008030 pub use inner::*;
Rosica Dejanovska4180ffe2022-06-09 06:01:05 -07008031 ...
8032 pub fn processMyStruct(s: crate::test_namespace_bindings::inner::MyStruct)
8033 ...
8034 }
8035 ...
8036 pub fn processMyStructOutsideNamespace(s: crate::test_namespace_bindings::inner::MyStruct)
8037 ...
8038 pub fn processMyStructSkipInlineNamespaceQualifier(s: crate::test_namespace_bindings::inner::MyStruct)
8039 ...
8040 }
8041 );
8042 Ok(())
8043 }
Rosica Dejanovska2708a5f2022-06-22 06:54:59 -07008044
8045 #[test]
Devin Jeanpierrefe6aaea2022-09-09 12:33:50 -07008046 fn test_inline_namespace_not_marked_inline() -> Result<()> {
8047 let rs_api = generate_bindings_tokens(ir_from_cc(
8048 r#"
8049 inline namespace my_inline {}
8050 namespace foo {}
8051 namespace my_inline { // still an inline namespace!
8052 struct MyStruct final {};
8053 }
8054 "#,
8055 )?)?
8056 .rs_api;
8057
8058 assert_rs_matches!(
8059 rs_api,
8060 quote! {
8061 ...
8062 pub mod my_inline_0 {}
8063 pub mod foo {}
8064 pub mod my_inline {
8065 pub use super::my_inline_0::*;
8066 ...
8067 pub struct MyStruct {...}
8068 ...
8069 }
8070 pub use my_inline::*;
8071 ...
8072 }
8073 );
8074 Ok(())
8075 }
8076
8077 #[test]
Devin Jeanpierre42238592022-10-04 20:27:44 -07008078 fn test_private_struct_not_present() -> Result<()> {
8079 let ir = ir_from_cc(&with_lifetime_macros(
8080 r#"#pragma clang lifetime_elision
8081 template <typename T> class MyTemplate {};
8082 class HasPrivateType {
8083 private:
8084 struct PrivateType {
8085 using Foo = MyTemplate<PrivateType>;
8086 Foo* get();
8087 };
8088 protected:
8089 HasPrivateType(MyTemplate<PrivateType> x) {}
8090 };"#,
8091 ))?;
8092 let rs_api = generate_bindings_tokens(ir)?.rs_api;
8093
8094 assert_rs_not_matches!(
8095 rs_api,
8096 quote! { __CcTemplateInst10MyTemplateIN14HasPrivateType11PrivateTypeEE }
8097 );
8098 Ok(())
8099 }
8100
8101 #[test]
Rosica Dejanovska2708a5f2022-06-22 06:54:59 -07008102 fn test_implicit_template_specializations_are_sorted_by_mangled_name() -> Result<()> {
Devin Jeanpierree9850a72022-06-29 19:04:48 -07008103 let bindings = generate_bindings_tokens(ir_from_cc(
Rosica Dejanovska2708a5f2022-06-22 06:54:59 -07008104 r#"
8105 template <typename T>
8106 struct MyStruct {
8107 T getT();
8108 };
8109
8110 using Alias1 = MyStruct<int>;
8111 using Alias2 = MyStruct<double>;
8112
8113 namespace test_namespace_bindings {
8114 using Alias3 = MyStruct<bool>;
8115 }
8116 "#,
8117 )?)?;
8118
8119 // Mangled name order: bool < double < int
8120 let my_struct_bool = make_rs_ident("__CcTemplateInst8MyStructIbE");
8121 let my_struct_double = make_rs_ident("__CcTemplateInst8MyStructIdE");
8122 let my_struct_int = make_rs_ident("__CcTemplateInst8MyStructIiE");
8123
8124 assert_rs_matches!(
8125 &bindings.rs_api,
8126 quote! {
8127 ...
8128 pub struct #my_struct_bool {...}
8129 ...
8130 pub struct #my_struct_double {...}
8131 ...
8132 pub struct #my_struct_int {...}
8133 ...
Devin Jeanpierrecd5ebf32022-07-08 05:31:55 -07008134 const _: () = assert!(::std::mem::size_of::<crate::#my_struct_bool>() == 1);
Rosica Dejanovska2708a5f2022-06-22 06:54:59 -07008135 ...
Devin Jeanpierrecd5ebf32022-07-08 05:31:55 -07008136 const _: () = assert!(::std::mem::size_of::<crate::#my_struct_double>() == 1);
Rosica Dejanovska2708a5f2022-06-22 06:54:59 -07008137 ...
Devin Jeanpierrecd5ebf32022-07-08 05:31:55 -07008138 const _: () = assert!(::std::mem::size_of::<crate::#my_struct_int>() == 1);
Rosica Dejanovska2708a5f2022-06-22 06:54:59 -07008139 ...
8140 }
8141 );
8142
Rosica Dejanovska2708a5f2022-06-22 06:54:59 -07008143 // User defined methods in mangled name order
8144 let my_struct_bool_method =
8145 make_rs_ident("__rust_thunk___ZN8MyStructIbE4getTEv__2f_2ftest_3atesting_5ftarget");
8146 let my_struct_double_method =
8147 make_rs_ident("__rust_thunk___ZN8MyStructIdE4getTEv__2f_2ftest_3atesting_5ftarget");
8148 let my_struct_int_method =
8149 make_rs_ident("__rust_thunk___ZN8MyStructIiE4getTEv__2f_2ftest_3atesting_5ftarget");
8150
8151 assert_cc_matches!(
8152 &bindings.rs_api_impl,
8153 quote! {
8154 ...
Lukasz Anforowiczd4742ff2022-07-11 17:05:02 -07008155 extern "C" bool #my_struct_bool_method(struct MyStruct<bool>*__this) {...} ...
8156 extern "C" double #my_struct_double_method(struct MyStruct<double>*__this) {...} ...
8157 extern "C" int #my_struct_int_method(struct MyStruct<int>*__this) {...} ...
Rosica Dejanovska2708a5f2022-06-22 06:54:59 -07008158 }
8159 );
8160 Ok(())
8161 }
Rosica Dejanovskaf9787c92022-06-22 12:14:57 -07008162
8163 #[test]
8164 fn test_implicit_template_specialization_namespace_qualifier() -> Result<()> {
Devin Jeanpierree9850a72022-06-29 19:04:48 -07008165 let rs_api = generate_bindings_tokens(ir_from_cc(
Rosica Dejanovskaf9787c92022-06-22 12:14:57 -07008166 r#" #pragma clang lifetime_elision
8167 namespace test_namespace_bindings {
8168 template <typename T>
8169 struct MyTemplate final {
8170 T value_;
8171 };
8172
8173 using MyTypeAlias = MyTemplate<int>;
8174 }"#,
8175 )?)?
8176 .rs_api;
8177
8178 assert_rs_matches!(
8179 rs_api,
8180 quote! {
8181 ...
8182 pub mod test_namespace_bindings {
8183 ...
8184 pub type MyTypeAlias = crate::__CcTemplateInstN23test_namespace_bindings10MyTemplateIiEE;
8185 ...
8186 }
8187 ...
8188 pub struct __CcTemplateInstN23test_namespace_bindings10MyTemplateIiEE {
8189 pub value_: i32,
8190 }
8191 ...
8192 }
8193 );
8194 Ok(())
8195 }
Rosica Dejanovskae12d7172022-06-22 12:20:17 -07008196
8197 #[test]
8198 fn test_forward_declared_class_template_specialization_symbol() -> Result<()> {
Devin Jeanpierree9850a72022-06-29 19:04:48 -07008199 let rs_api = generate_bindings_tokens(ir_from_cc(
Rosica Dejanovskae12d7172022-06-22 12:20:17 -07008200 r#"
8201 namespace test_namespace_bindings {
8202 template <typename T>
8203 struct MyTemplate {
8204 void processT(T t);
8205 };
8206
8207 struct Param {};
8208
8209 template<> struct MyTemplate<Param>;
Lukasz Anforowicz38310f32022-09-09 11:17:52 -07008210
8211 using MyTypeAlias = MyTemplate<Param>;
Rosica Dejanovskae12d7172022-06-22 12:20:17 -07008212 }"#,
8213 )?)?
8214 .rs_api;
8215
8216 assert_rs_matches!(
8217 rs_api,
8218 quote! {
8219 ...
Lukasz Anforowicz38310f32022-09-09 11:17:52 -07008220 forward_declare::forward_declare!(pub __CcTemplateInstN23test_namespace_bindings10MyTemplateINS_5ParamEEE = forward_declare::symbol!("__CcTemplateInstN23test_namespace_bindings10MyTemplateINS_5ParamEEE"));
Rosica Dejanovskae12d7172022-06-22 12:20:17 -07008221 ...
8222 }
8223 );
8224 Ok(())
8225 }
Devin Jeanpierre7ed8c6f2022-08-01 13:41:02 -07008226
8227 #[test]
8228 fn test_lifetime_elision_for_references() {
8229 let type_args: &[RsTypeKind] = &[];
8230 let referent = Rc::new(RsTypeKind::Other { name: "T".into(), type_args: type_args.into() });
8231 let reference = RsTypeKind::Reference {
8232 referent: referent,
8233 mutability: Mutability::Const,
Devin Jeanpierre4e94a082022-08-10 01:58:35 -07008234 lifetime: Lifetime::new("_"),
Devin Jeanpierre7ed8c6f2022-08-01 13:41:02 -07008235 };
8236 assert_rs_matches!(quote! {#reference}, quote! {&T});
8237 }
8238
8239 #[test]
8240 fn test_lifetime_elision_for_rvalue_references() {
8241 let type_args: &[RsTypeKind] = &[];
8242 let referent = Rc::new(RsTypeKind::Other { name: "T".into(), type_args: type_args.into() });
8243 let reference = RsTypeKind::RvalueReference {
8244 referent: referent,
8245 mutability: Mutability::Mut,
Devin Jeanpierre4e94a082022-08-10 01:58:35 -07008246 lifetime: Lifetime::new("_"),
Devin Jeanpierre7ed8c6f2022-08-01 13:41:02 -07008247 };
8248 assert_rs_matches!(quote! {#reference}, quote! {RvalueReference<'_, T>});
8249 }
Marcel Hlopko42abfc82021-08-09 07:03:17 +00008250}