blob: 4bb4dfe1b082b7849c18932d2134cb7f2e4809ca [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
Devin Jeanpierre3a0cc5a2022-07-12 09:36:34 -07005use arc_anyhow::{anyhow, bail, ensure, Context, Result};
Marcel Hlopko884ae7f2021-08-18 13:58:22 +00006use ffi_types::*;
Marcel Hlopko42abfc82021-08-09 07:03:17 +00007use ir::*;
8use itertools::Itertools;
Michael VanBemmel5014b3e2022-08-03 16:27:34 -07009use once_cell::sync::Lazy;
Googler5ea88642021-09-29 08:05:59 +000010use proc_macro2::{Ident, Literal, TokenStream};
Devin Jeanpierre92ca2612022-04-06 11:35:13 -070011use quote::{format_ident, quote, ToTokens};
Devin Jeanpierrece549202022-07-25 08:54:34 -070012use salsa_utils::RcEq;
Michael VanBemmel32c26df2022-08-03 16:08:58 -070013use std::collections::{BTreeSet, HashMap, HashSet};
Lukasz Anforowicz54ff3182022-05-06 07:17:58 -070014use std::ffi::{OsStr, OsString};
Devin Jeanpierre6bb81802022-08-10 02:08:47 -070015use std::fmt::Write as _;
Michael Forster82c02d32022-05-20 21:47:33 -070016use std::iter::{self, Iterator};
Marcel Hlopko42abfc82021-08-09 07:03:17 +000017use std::panic::catch_unwind;
18use std::process;
Michael VanBemmel7a4d4c02022-07-27 13:21:47 -070019use std::ptr;
Devin Jeanpierre8cd8ae72022-06-29 18:59:40 -070020use std::rc::Rc;
Lukasz Anforowicz54ff3182022-05-06 07:17:58 -070021use token_stream_printer::{rs_tokens_to_formatted_string, tokens_to_string, RustfmtConfig};
Marcel Hlopko42abfc82021-08-09 07:03:17 +000022
Marcel Hlopko45fba972021-08-23 19:52:20 +000023/// FFI equivalent of `Bindings`.
24#[repr(C)]
25pub struct FfiBindings {
26 rs_api: FfiU8SliceBox,
27 rs_api_impl: FfiU8SliceBox,
28}
29
30/// Deserializes IR from `json` and generates bindings source code.
Marcel Hlopko42abfc82021-08-09 07:03:17 +000031///
32/// This function panics on error.
33///
Lukasz Anforowiczdd9ae0f2022-02-17 15:52:53 +000034/// # Safety
35///
36/// Expectations:
Lukasz Anforowicz54ff3182022-05-06 07:17:58 -070037/// * `json` should be a FfiU8Slice for a valid array of bytes with the given
38/// size.
Lukasz Anforowiczdd907702022-05-06 09:24:07 -070039/// * `crubit_support_path` should be a FfiU8Slice for a valid array of bytes
40/// representing an UTF8-encoded string
Lukasz Anforowiczd7d68f02022-05-26 07:41:02 -070041/// * `rustfmt_exe_path` and `rustfmt_config_path` should both be a
42/// FfiU8Slice for a valid array of bytes representing an UTF8-encoded
43/// string (without the UTF-8 requirement, it seems that Rust doesn't offer
44/// a way to convert to OsString on Windows)
45/// * `json`, `crubit_support_path`, `rustfmt_exe_path`, and
46/// `rustfmt_config_path` shouldn't change during the call.
Lukasz Anforowiczdd9ae0f2022-02-17 15:52:53 +000047///
Marcel Hlopko42abfc82021-08-09 07:03:17 +000048/// Ownership:
Michael Forsterbee84482021-10-13 08:35:38 +000049/// * function doesn't take ownership of (in other words it borrows) the
Lukasz Anforowiczd7d68f02022-05-26 07:41:02 -070050/// input params: `json`, `crubit_support_path`, `rustfmt_exe_path`, and
51/// `rustfmt_config_path`
Marcel Hlopko42abfc82021-08-09 07:03:17 +000052/// * function passes ownership of the returned value to the caller
Marcel Hlopko42abfc82021-08-09 07:03:17 +000053#[no_mangle]
Lukasz Anforowicz54ff3182022-05-06 07:17:58 -070054pub unsafe extern "C" fn GenerateBindingsImpl(
55 json: FfiU8Slice,
Lukasz Anforowiczdd907702022-05-06 09:24:07 -070056 crubit_support_path: FfiU8Slice,
Lukasz Anforowiczd7d68f02022-05-26 07:41:02 -070057 rustfmt_exe_path: FfiU8Slice,
Lukasz Anforowicz54ff3182022-05-06 07:17:58 -070058 rustfmt_config_path: FfiU8Slice,
59) -> FfiBindings {
60 let json: &[u8] = json.as_slice();
Lukasz Anforowiczdd907702022-05-06 09:24:07 -070061 let crubit_support_path: &str = std::str::from_utf8(crubit_support_path.as_slice()).unwrap();
Lukasz Anforowiczd7d68f02022-05-26 07:41:02 -070062 let rustfmt_exe_path: OsString =
63 std::str::from_utf8(rustfmt_exe_path.as_slice()).unwrap().into();
Lukasz Anforowicz54ff3182022-05-06 07:17:58 -070064 let rustfmt_config_path: OsString =
65 std::str::from_utf8(rustfmt_config_path.as_slice()).unwrap().into();
Marcel Hlopko42abfc82021-08-09 07:03:17 +000066 catch_unwind(|| {
Marcel Hlopko45fba972021-08-23 19:52:20 +000067 // It is ok to abort here.
Marcel Hlopko36234892022-05-10 00:39:54 -070068 let Bindings { rs_api, rs_api_impl } =
Devin Jeanpierre108e9c02022-06-02 07:10:09 -070069 generate_bindings(json, crubit_support_path, &rustfmt_exe_path, &rustfmt_config_path)
70 .unwrap();
Marcel Hlopko45fba972021-08-23 19:52:20 +000071 FfiBindings {
72 rs_api: FfiU8SliceBox::from_boxed_slice(rs_api.into_bytes().into_boxed_slice()),
73 rs_api_impl: FfiU8SliceBox::from_boxed_slice(
74 rs_api_impl.into_bytes().into_boxed_slice(),
75 ),
76 }
Marcel Hlopko42abfc82021-08-09 07:03:17 +000077 })
78 .unwrap_or_else(|_| process::abort())
79}
80
Devin Jeanpierre52a14c32022-06-29 19:12:11 -070081#[salsa::query_group(BindingsGeneratorStorage)]
82trait BindingsGenerator {
83 #[salsa::input]
84 fn ir(&self) -> Rc<IR>;
85
Devin Jeanpierre3a0cc5a2022-07-12 09:36:34 -070086 fn rs_type_kind(&self, rs_type: RsType) -> Result<RsTypeKind>;
Devin Jeanpierre409f6f62022-07-07 02:00:26 -070087
Devin Jeanpierre52a14c32022-06-29 19:12:11 -070088 fn generate_func(
89 &self,
90 func: Rc<Func>,
Devin Jeanpierrece549202022-07-25 08:54:34 -070091 ) -> Result<Option<RcEq<(RsSnippet, RsSnippet, Rc<FunctionId>)>>>;
Devin Jeanpierreab85d442022-06-29 19:16:41 -070092
93 fn overloaded_funcs(&self) -> Rc<HashSet<Rc<FunctionId>>>;
Devin Jeanpierre52a14c32022-06-29 19:12:11 -070094}
95
96#[salsa::database(BindingsGeneratorStorage)]
97#[derive(Default)]
98struct Database {
99 storage: salsa::Storage<Self>,
100}
101
102impl salsa::Database for Database {}
103
Marcel Hlopko45fba972021-08-23 19:52:20 +0000104/// Source code for generated bindings.
105struct Bindings {
106 // Rust source code.
107 rs_api: String,
108 // C++ source code.
109 rs_api_impl: String,
Marcel Hlopko42abfc82021-08-09 07:03:17 +0000110}
111
Devin Jeanpierre4f06f832022-04-26 15:51:30 -0700112/// Source code for generated bindings, as tokens.
113struct BindingsTokens {
114 // Rust source code.
115 rs_api: TokenStream,
116 // C++ source code.
117 rs_api_impl: TokenStream,
118}
119
Lukasz Anforowiczdd907702022-05-06 09:24:07 -0700120fn generate_bindings(
121 json: &[u8],
122 crubit_support_path: &str,
Lukasz Anforowiczd7d68f02022-05-26 07:41:02 -0700123 rustfmt_exe_path: &OsStr,
Lukasz Anforowiczdd907702022-05-06 09:24:07 -0700124 rustfmt_config_path: &OsStr,
125) -> Result<Bindings> {
Devin Jeanpierree9850a72022-06-29 19:04:48 -0700126 let ir = Rc::new(deserialize_ir(json)?);
Marcel Hlopkoca84ff42021-12-09 14:15:14 +0000127
Lukasz Anforowiczdd907702022-05-06 09:24:07 -0700128 let BindingsTokens { rs_api, rs_api_impl } =
Devin Jeanpierree9850a72022-06-29 19:04:48 -0700129 generate_bindings_tokens(ir.clone(), crubit_support_path)?;
Lukasz Anforowicz54ff3182022-05-06 07:17:58 -0700130 let rs_api = {
Lukasz Anforowiczd7d68f02022-05-26 07:41:02 -0700131 let rustfmt_config = RustfmtConfig::new(rustfmt_exe_path, rustfmt_config_path);
Lukasz Anforowicz54ff3182022-05-06 07:17:58 -0700132 rs_tokens_to_formatted_string(rs_api, &rustfmt_config)?
133 };
134
Marcel Hlopkoca84ff42021-12-09 14:15:14 +0000135 // The code is formatted with a non-default rustfmt configuration. Prevent
Lukasz Anforowicz5b3f5302022-02-07 01:04:47 +0000136 // downstream workflows from reformatting with a different configuration by
137 // marking the output with `@generated`. See also
138 // https://rust-lang.github.io/rustfmt/?version=v1.4.38&search=#format_generated_files
139 //
140 // TODO(lukasza): It would be nice to include "by $argv[0]"" in the
141 // @generated comment below. OTOH, `std::env::current_exe()` in our
142 // current build environment returns a guid-like path... :-/
Lukasz Anforowicz72c4d222022-02-18 19:07:28 +0000143 //
144 // TODO(lukasza): Try to remove `#![rustfmt:skip]` - in theory it shouldn't
145 // be needed when `@generated` comment/keyword is present...
Lukasz Anforowicz5b3f5302022-02-07 01:04:47 +0000146 let rs_api = format!(
Lukasz Anforowicz72c4d222022-02-18 19:07:28 +0000147 "// Automatically @generated Rust bindings for C++ target\n\
148 // {target}\n\
149 #![rustfmt::skip]\n\
150 {code}",
Lukasz Anforowicz5b3f5302022-02-07 01:04:47 +0000151 target = ir.current_target().0,
Lukasz Anforowicz54ff3182022-05-06 07:17:58 -0700152 code = rs_api,
Lukasz Anforowicz5b3f5302022-02-07 01:04:47 +0000153 );
Devin Jeanpierre4f06f832022-04-26 15:51:30 -0700154 let rs_api_impl = tokens_to_string(rs_api_impl)?;
Marcel Hlopkoca84ff42021-12-09 14:15:14 +0000155
Marcel Hlopko45fba972021-08-23 19:52:20 +0000156 Ok(Bindings { rs_api, rs_api_impl })
157}
158
Devin Jeanpierre6d5e7cc2021-10-21 12:56:07 +0000159/// Rust source code with attached information about how to modify the parent
160/// crate.
Devin Jeanpierre273eeae2021-10-06 13:29:35 +0000161///
Michael Forsterbee84482021-10-13 08:35:38 +0000162/// For example, the snippet `vec![].into_raw_parts()` is not valid unless the
163/// `vec_into_raw_parts` feature is enabled. So such a snippet should be
164/// represented as:
Devin Jeanpierre273eeae2021-10-06 13:29:35 +0000165///
166/// ```
167/// RsSnippet {
Marcel Hlopkoeaae9b72022-01-21 15:54:11 +0000168/// features: btree_set![make_rs_ident("vec_into_raw_parts")],
Devin Jeanpierre273eeae2021-10-06 13:29:35 +0000169/// tokens: quote!{vec![].into_raw_parts()},
170/// }
171/// ```
Lukasz Anforowiczeb19ac62022-02-05 00:10:16 +0000172#[derive(Clone, Debug)]
Devin Jeanpierre273eeae2021-10-06 13:29:35 +0000173struct RsSnippet {
174 /// Rust feature flags used by this snippet.
175 features: BTreeSet<Ident>,
176 /// The snippet itself, as a token stream.
177 tokens: TokenStream,
178}
179
180impl From<TokenStream> for RsSnippet {
181 fn from(tokens: TokenStream) -> Self {
182 RsSnippet { features: BTreeSet::new(), tokens }
183 }
184}
185
Michael Forsterbee84482021-10-13 08:35:38 +0000186/// If we know the original C++ function is codegenned and already compatible
187/// with `extern "C"` calling convention we skip creating/calling the C++ thunk
188/// since we can call the original C++ directly.
Devin Jeanpierreb8ce2c12022-07-13 10:34:01 -0700189fn can_skip_cc_thunk(db: &dyn BindingsGenerator, func: &Func) -> bool {
Devin Jeanpierre96839c12021-12-14 00:27:38 +0000190 // ## Inline functions
191 //
Michael Forsterbee84482021-10-13 08:35:38 +0000192 // Inline functions may not be codegenned in the C++ library since Clang doesn't
193 // know if Rust calls the function or not. Therefore in order to make inline
194 // functions callable from Rust we need to generate a C++ file that defines
195 // a thunk that delegates to the original inline function. When compiled,
196 // Clang will emit code for this thunk and Rust code will call the
Marcel Hlopko3164eee2021-08-24 20:09:22 +0000197 // thunk when the user wants to call the original inline function.
198 //
Michael Forsterbee84482021-10-13 08:35:38 +0000199 // This is not great runtime-performance-wise in regular builds (inline function
200 // will not be inlined, there will always be a function call), but it is
201 // correct. ThinLTO builds will be able to see through the thunk and inline
202 // code across the language boundary. For non-ThinLTO builds we plan to
203 // implement <internal link> which removes the runtime performance overhead.
Devin Jeanpierre96839c12021-12-14 00:27:38 +0000204 if func.is_inline {
205 return false;
206 }
Lukasz Anforowiczb1ff2e52022-05-16 10:54:23 -0700207 // ## Member functions (or descendants) of class templates
208 //
209 // A thunk is required to force/guarantee template instantiation.
210 if func.is_member_or_descendant_of_class_template {
211 return false;
212 }
Devin Jeanpierre96839c12021-12-14 00:27:38 +0000213 // ## Virtual functions
214 //
215 // When calling virtual `A::Method()`, it's not necessarily the case that we'll
216 // specifically call the concrete `A::Method` impl. For example, if this is
217 // called on something whose dynamic type is some subclass `B` with an
218 // overridden `B::Method`, then we'll call that.
219 //
220 // We must reuse the C++ dynamic dispatching system. In this case, the easiest
221 // way to do it is by resorting to a C++ thunk, whose implementation will do
222 // the lookup.
223 //
224 // In terms of runtime performance, since this only occurs for virtual function
225 // calls, which are already slow, it may not be such a big deal. We can
226 // benchmark it later. :)
227 if let Some(meta) = &func.member_func_metadata {
228 if let Some(inst_meta) = &meta.instance_method_metadata {
229 if inst_meta.is_virtual {
230 return false;
231 }
232 }
233 }
Lukasz Anforowicz0b6a6ac2022-03-22 22:32:23 +0000234 // ## Custom calling convention requires a thunk.
235 //
236 // The thunk has the "C" calling convention, and internally can call the
237 // C++ function using any of the calling conventions supported by the C++
238 // compiler (which might not always match the set supported by Rust - e.g.,
239 // abi.rs doesn't contain "swiftcall" from
240 // clang::FunctionType::getNameForCallConv)
241 if !func.has_c_calling_convention {
242 return false;
243 }
Devin Jeanpierre96839c12021-12-14 00:27:38 +0000244
Devin Jeanpierreb8ce2c12022-07-13 10:34:01 -0700245 // ## Nontrivial return types.
246 //
247 // If the function returns a value which is nontrivial for the purpose of calls,
248 // then in the underlying ABI, it is actually returned via a hidden pointer
249 // parameter not exposed anywhere in the Clang AST or the Crubit IR. For
250 // now, this is worked around via an _explicit_ output parameter, used in
251 // the thunk, which cannot be skipped anymore.
252 //
253 // Note: if the RsTypeKind cannot be parsed / rs_type_kind returns Err, then
254 // bindings generation will fail for this function, so it doesn't really matter
255 // what we do here.
256 if let Ok(return_type) = db.rs_type_kind(func.return_type.rs_type.clone()) {
257 if !return_type.is_unpin() {
258 return false;
259 }
260 }
Devin Jeanpierre11ce2b92022-07-27 07:54:21 -0700261 // ## Nontrivial parameter types.
262 //
263 // If the function accepts a value which is nontrivial for the purpose of calls,
264 // then in the underlying ABI, it is actually passed by pointer.
265 //
266 // Because there's no way to upgrade an lvalue (e.g. pointer) to a prvalue, we
267 // cannot implement guaranteed copy/move elision for inline functions for
268 // now: any thunk we generate would need to invoke the correct function as
269 // if by magic.
270 //
271 // And so for now, we always use C++11 semantics, via an intermediate thunk.
272 //
273 // (As a side effect, this, like return values, means that support is
274 // ABI-agnostic.)
275 for param in &func.params {
276 if let Ok(param_type) = db.rs_type_kind(param.type_.rs_type.clone()) {
277 if !param_type.is_unpin() {
278 return false;
279 }
280 }
281 }
Devin Jeanpierreb8ce2c12022-07-13 10:34:01 -0700282
Devin Jeanpierre96839c12021-12-14 00:27:38 +0000283 true
Marcel Hlopko3164eee2021-08-24 20:09:22 +0000284}
285
Googlerd03d05b2022-01-07 10:10:57 +0000286/// Uniquely identifies a generated Rust function.
Lukasz Anforowiczeb19ac62022-02-05 00:10:16 +0000287#[derive(Clone, Debug, PartialEq, Eq, Hash)]
Googlerd03d05b2022-01-07 10:10:57 +0000288struct FunctionId {
289 // If the function is on a trait impl, contains the name of the Self type for
290 // which the trait is being implemented.
291 self_type: Option<syn::Path>,
292 // Fully qualified path of the function. For functions in impl blocks, this
293 // includes the name of the type or trait on which the function is being
294 // implemented, e.g. `Default::default`.
295 function_path: syn::Path,
296}
297
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +0000298/// Returns the name of `func` in C++ syntax.
Googlerd03d05b2022-01-07 10:10:57 +0000299fn cxx_function_name(func: &Func, ir: &IR) -> Result<String> {
Devin Jeanpierrebdfb4d92022-06-17 01:17:01 -0700300 let record: Option<&str> = ir.record_for_member_func(func)?.map(|r| &*r.cc_name);
Googlerd03d05b2022-01-07 10:10:57 +0000301
302 let func_name = match &func.name {
303 UnqualifiedIdentifier::Identifier(id) => id.identifier.clone(),
Lukasz Anforowicz9c663ca2022-02-09 01:33:31 +0000304 UnqualifiedIdentifier::Operator(op) => op.cc_name(),
Googlerd03d05b2022-01-07 10:10:57 +0000305 UnqualifiedIdentifier::Destructor => {
306 format!("~{}", record.expect("destructor must be associated with a record"))
307 }
308 UnqualifiedIdentifier::Constructor => {
Lukasz Anforowiczeb19ac62022-02-05 00:10:16 +0000309 record.expect("constructor must be associated with a record").to_string()
Googlerd03d05b2022-01-07 10:10:57 +0000310 }
311 };
312
313 if let Some(record_name) = record {
314 Ok(format!("{}::{}", record_name, func_name))
315 } else {
316 Ok(func_name)
317 }
318}
319
Lukasz Anforowiczeb19ac62022-02-05 00:10:16 +0000320fn make_unsupported_fn(func: &Func, ir: &IR, message: impl ToString) -> Result<UnsupportedItem> {
321 Ok(UnsupportedItem {
322 name: cxx_function_name(func, ir)?,
323 message: message.to_string(),
324 source_loc: func.source_loc.clone(),
Rosica Dejanovskad638cf52022-03-23 15:45:01 +0000325 id: func.id,
Lukasz Anforowiczeb19ac62022-02-05 00:10:16 +0000326 })
327}
328
Devin Jeanpierre215ee8e2022-05-16 16:09:41 -0700329/// The name of a one-function trait, with extra entries for
330/// specially-understood traits and families of traits.
Devin Jeanpierre8cd8ae72022-06-29 18:59:40 -0700331enum TraitName {
Devin Jeanpierread125742022-04-11 13:50:28 -0700332 /// The constructor trait for !Unpin types, with a list of parameter types.
333 /// For example, `CtorNew(vec![])` is the default constructor.
Devin Jeanpierre8cd8ae72022-06-29 18:59:40 -0700334 CtorNew(Vec<RsTypeKind>),
Devin Jeanpierree77fa7e2022-06-02 14:05:58 -0700335 /// An Unpin constructor trait, e.g. From or Clone, with a list of parameter
336 /// types.
Devin Jeanpierre8cd8ae72022-06-29 18:59:40 -0700337 UnpinConstructor { name: TokenStream, params: Vec<RsTypeKind> },
Devin Jeanpierre9c34da62022-03-31 04:57:16 -0700338 /// Any other trait, e.g. Eq.
Devin Jeanpierre8cd8ae72022-06-29 18:59:40 -0700339 Other { name: TokenStream, params: Vec<RsTypeKind>, is_unsafe_fn: bool },
Devin Jeanpierre9c34da62022-03-31 04:57:16 -0700340}
Devin Jeanpierree77fa7e2022-06-02 14:05:58 -0700341
Devin Jeanpierre8cd8ae72022-06-29 18:59:40 -0700342impl TraitName {
Devin Jeanpierree77fa7e2022-06-02 14:05:58 -0700343 /// Returns the generic parameters in this trait name.
Devin Jeanpierre8cd8ae72022-06-29 18:59:40 -0700344 fn params(&self) -> impl Iterator<Item = &RsTypeKind> {
Devin Jeanpierree77fa7e2022-06-02 14:05:58 -0700345 match self {
346 Self::CtorNew(params)
347 | Self::UnpinConstructor { params, .. }
348 | Self::Other { params, .. } => params.iter(),
349 }
350 }
351
352 /// Returns the lifetimes used in this trait name.
Devin Jeanpierre4e94a082022-08-10 01:58:35 -0700353 pub fn lifetimes(&self) -> impl Iterator<Item = Lifetime> + '_ {
Devin Jeanpierree77fa7e2022-06-02 14:05:58 -0700354 self.params().flat_map(|p| p.lifetimes())
355 }
356}
357
Devin Jeanpierre8cd8ae72022-06-29 18:59:40 -0700358impl ToTokens for TraitName {
Devin Jeanpierre9c34da62022-03-31 04:57:16 -0700359 fn to_tokens(&self, tokens: &mut TokenStream) {
360 match self {
Devin Jeanpierree77fa7e2022-06-02 14:05:58 -0700361 Self::UnpinConstructor { name, params } | Self::Other { name, params, .. } => {
362 let params = format_generic_params(params);
363 quote! {#name #params}.to_tokens(tokens)
364 }
Devin Jeanpierread125742022-04-11 13:50:28 -0700365 Self::CtorNew(arg_types) => {
366 let arg_types = format_tuple_except_singleton(arg_types);
Devin Jeanpierrecd5ebf32022-07-08 05:31:55 -0700367 quote! { ::ctor::CtorNew < #arg_types > }.to_tokens(tokens)
Devin Jeanpierread125742022-04-11 13:50:28 -0700368 }
Devin Jeanpierre9c34da62022-03-31 04:57:16 -0700369 }
370 }
371}
372
373/// The kind of the `impl` block the function needs to be generated in.
Devin Jeanpierre8cd8ae72022-06-29 18:59:40 -0700374enum ImplKind {
Devin Jeanpierre9c34da62022-03-31 04:57:16 -0700375 /// Used for free functions for which we don't want the `impl` block.
376 None { is_unsafe: bool },
377 /// Used for inherent methods for which we need an `impl SomeStruct { ... }`
378 /// block.
379 Struct {
380 /// For example, `SomeStruct`. Retrieved from
381 /// `func.member_func_metadata`.
382 record_name: Ident,
383 is_unsafe: bool,
384 /// Whether to format the first parameter as "self" (e.g. `__this:
385 /// &mut T` -> `&mut self`)
386 format_first_param_as_self: bool,
387 },
388 /// Used for trait methods for which we need an `impl TraitName for
389 /// SomeStruct { ... }` block.
390 Trait {
391 /// For example, `SomeStruct`.
392 /// Note that `record_name` might *not* be from
393 /// `func.member_func_metadata`.
394 record_name: Ident,
Lukasz Anforowicz92be29f2022-09-02 17:12:29 -0700395 record_qualifier: NamespaceQualifier,
Devin Jeanpierre9c34da62022-03-31 04:57:16 -0700396 /// For example, `quote!{ From<i32> }`.
Devin Jeanpierre8cd8ae72022-06-29 18:59:40 -0700397 trait_name: TraitName,
Michael VanBemmel106f66c2022-07-13 09:48:48 -0700398 /// Reference style for the `impl` block and self parameters.
399 impl_for: ImplFor,
Devin Jeanpierre9c34da62022-03-31 04:57:16 -0700400
Devin Jeanpierrea04bce12022-08-01 13:39:48 -0700401 /// The generic params of trait `impl` (e.g. `vec![quote!{'b}]`). These
402 /// start empty and only later are mutated into the correct value.
403 trait_generic_params: Vec<TokenStream>,
404
Devin Jeanpierre9c34da62022-03-31 04:57:16 -0700405 /// Whether to format the first parameter as "self" (e.g. `__this:
406 /// &mut T` -> `&mut self`)
407 format_first_param_as_self: bool,
Michael VanBemmelf6651302022-08-03 15:56:59 -0700408 /// Whether to drop the C++ function's return value and return unit
409 /// instead.
410 drop_return: bool,
Michael VanBemmel106f66c2022-07-13 09:48:48 -0700411
Devin Jeanpierrefd4ff822022-07-15 01:43:55 -0700412 /// If this trait's method returns an associated type, it has this name.
413 /// For example, this is `Output` on
414 /// [`Add`](https://doc.rust-lang.org/std/ops/trait.Add.html).
415 associated_return_type: Option<Ident>,
Devin Jeanpierre9c34da62022-03-31 04:57:16 -0700416 },
417}
Devin Jeanpierre8cd8ae72022-06-29 18:59:40 -0700418impl ImplKind {
Devin Jeanpierre9c34da62022-03-31 04:57:16 -0700419 fn new_trait(
Devin Jeanpierre8cd8ae72022-06-29 18:59:40 -0700420 trait_name: TraitName,
Lukasz Anforowicz92be29f2022-09-02 17:12:29 -0700421 record: &Record,
422 ir: &IR,
Devin Jeanpierre9c34da62022-03-31 04:57:16 -0700423 format_first_param_as_self: bool,
Lukasz Anforowicz92be29f2022-09-02 17:12:29 -0700424 ) -> Result<Self> {
425 Ok(ImplKind::Trait {
Devin Jeanpierre9c34da62022-03-31 04:57:16 -0700426 trait_name,
Lukasz Anforowicz92be29f2022-09-02 17:12:29 -0700427 record_name: make_rs_ident(&record.rs_name),
428 record_qualifier: NamespaceQualifier::new(record.id, ir)?,
Michael VanBemmel106f66c2022-07-13 09:48:48 -0700429 impl_for: ImplFor::T,
Devin Jeanpierrea04bce12022-08-01 13:39:48 -0700430 trait_generic_params: vec![],
Devin Jeanpierre9c34da62022-03-31 04:57:16 -0700431 format_first_param_as_self,
Michael VanBemmelf6651302022-08-03 15:56:59 -0700432 drop_return: false,
Devin Jeanpierrefd4ff822022-07-15 01:43:55 -0700433 associated_return_type: None,
Lukasz Anforowicz92be29f2022-09-02 17:12:29 -0700434 })
Devin Jeanpierre9c34da62022-03-31 04:57:16 -0700435 }
436 fn format_first_param_as_self(&self) -> bool {
437 matches!(
438 self,
439 Self::Trait { format_first_param_as_self: true, .. }
440 | Self::Struct { format_first_param_as_self: true, .. }
441 )
442 }
443 /// Returns whether the function is defined as `unsafe fn ...`.
444 fn is_unsafe(&self) -> bool {
Devin Jeanpierre215ee8e2022-05-16 16:09:41 -0700445 matches!(
446 self,
447 Self::None { is_unsafe: true, .. }
448 | Self::Struct { is_unsafe: true, .. }
449 | Self::Trait { trait_name: TraitName::Other { is_unsafe_fn: true, .. }, .. }
450 )
Devin Jeanpierre9c34da62022-03-31 04:57:16 -0700451 }
452}
453
Michael VanBemmel106f66c2022-07-13 09:48:48 -0700454/// Whether the impl block is for T, and the receivers take self by reference,
455/// or the impl block is for a reference to T, and the method receivers take
456/// self by value.
457enum ImplFor {
458 /// Implement the trait for `T` directly.
459 ///
460 /// ```
461 /// impl Trait for T {
462 /// fn const_method<'a>(&'a self);
463 /// fn mut_method<'a>(&'a mut self);
464 /// fn pin_method<'a>(Pin<&'a mut self>);
465 /// }
466 /// ```
467 T,
468 /// Implement the trait for `&T`, `&mut T`, or `Pin<&mut T>`, depending on
469 /// the Rust type of the self parameter.
470 ///
471 /// ```
472 /// impl<'a> Trait for &'a T {
473 /// fn const_method(self);
474 /// }
475 /// impl<'a> Trait for &'a mut UnpinT {
476 /// fn mut_method(self);
477 /// }
478 /// impl<'a> Trait for Pin<&'a mut NonUnpinT> {
479 /// fn pin_method(self);
480 /// }
481 /// ```
482 RefT,
483}
484
Michael VanBemmel7a4d4c02022-07-27 13:21:47 -0700485/// Returns whether an argument of this type causes ADL to include the `record`.
486fn adl_expands_to(record: &Record, rs_type_kind: &RsTypeKind) -> bool {
487 match rs_type_kind {
488 RsTypeKind::Record { record: nested_record, .. } => ptr::eq(record, &**nested_record),
489 RsTypeKind::Reference { referent, .. } => adl_expands_to(record, &**referent),
490 RsTypeKind::RvalueReference { referent, .. } => adl_expands_to(record, &**referent),
491 _ => false,
492 }
493}
494
495/// Returns whether any type in `param_types` causes ADL to include `record`.
496///
497/// This is an under-approximation. Things not considered include class template
498/// arguments and the parameters and return type of function types.
499///
500/// See https://en.cppreference.com/w/cpp/language/adl
501fn is_visible_by_adl(enclosing_record: &Record, param_types: &[RsTypeKind]) -> bool {
502 param_types.iter().any(|param_type| adl_expands_to(enclosing_record, param_type))
503}
504
Michael VanBemmel32c26df2022-08-03 16:08:58 -0700505#[derive(Debug)]
506struct OperatorMetadata {
Michael VanBemmel5bde56f2022-08-17 12:05:37 -0700507 by_cc_name_and_params: HashMap<(&'static str, usize), OperatorMetadataEntry>,
Michael VanBemmel32c26df2022-08-03 16:08:58 -0700508}
509
510#[derive(Clone, Copy, Debug)]
511struct OperatorMetadataEntry {
Michael VanBemmel5bde56f2022-08-17 12:05:37 -0700512 cc_name: &'static str,
513 cc_params: usize,
Michael VanBemmel32c26df2022-08-03 16:08:58 -0700514 trait_name: &'static str,
515 method_name: &'static str,
Michael VanBemmel5bde56f2022-08-17 12:05:37 -0700516 is_compound_assignment: bool,
517}
518
519impl OperatorMetadataEntry {
520 const fn unary(
521 cc_name: &'static str,
522 trait_name: &'static str,
523 method_name: &'static str,
524 ) -> Self {
525 Self { cc_name, cc_params: 1, trait_name, method_name, is_compound_assignment: false }
526 }
527
528 const fn binary(
529 cc_name: &'static str,
530 trait_name: &'static str,
531 method_name: &'static str,
532 ) -> Self {
533 Self { cc_name, cc_params: 2, trait_name, method_name, is_compound_assignment: false }
534 }
535
536 const fn assign(
537 cc_name: &'static str,
538 trait_name: &'static str,
539 method_name: &'static str,
540 ) -> Self {
541 Self { cc_name, cc_params: 2, trait_name, method_name, is_compound_assignment: true }
542 }
Michael VanBemmel32c26df2022-08-03 16:08:58 -0700543}
544
Michael VanBemmel5014b3e2022-08-03 16:27:34 -0700545static OPERATOR_METADATA: Lazy<OperatorMetadata> = Lazy::new(|| {
Michael VanBemmel5bde56f2022-08-17 12:05:37 -0700546 const ENTRIES: &[OperatorMetadataEntry] = &[
547 OperatorMetadataEntry::unary("-", "Neg", "neg"),
548 // The Rust `Not` trait matches with both the C++ `!` and `~` operators to some extent. The
549 // two operators appear with similar frequency in our target codebase so it's not clear
550 // which is better to map here. Mapping `operator!` to `Not` as chosen here means that a
551 // C++ `!` matches up with a Rust `!`.
552 OperatorMetadataEntry::unary("!", "Not", "not"),
553 OperatorMetadataEntry::binary("+", "Add", "add"),
554 OperatorMetadataEntry::binary("-", "Sub", "sub"),
555 OperatorMetadataEntry::binary("*", "Mul", "mul"),
556 OperatorMetadataEntry::binary("/", "Div", "div"),
557 OperatorMetadataEntry::binary("%", "Rem", "rem"),
558 OperatorMetadataEntry::binary("&", "BitAnd", "bitand"),
559 OperatorMetadataEntry::binary("|", "BitOr", "bitor"),
560 OperatorMetadataEntry::binary("^", "BitXor", "bitxor"),
561 OperatorMetadataEntry::binary("<<", "Shl", "shl"),
562 OperatorMetadataEntry::binary(">>", "Shr", "shr"),
563 OperatorMetadataEntry::assign("+=", "AddAssign", "add_assign"),
564 OperatorMetadataEntry::assign("-=", "SubAssign", "sub_assign"),
565 OperatorMetadataEntry::assign("*=", "MulAssign", "mul_assign"),
566 OperatorMetadataEntry::assign("/=", "DivAssign", "div_assign"),
567 OperatorMetadataEntry::assign("%=", "RemAssign", "rem_assign"),
568 OperatorMetadataEntry::assign("&=", "BitAndAssign", "bitand_assign"),
569 OperatorMetadataEntry::assign("|=", "BitOrAssign", "bitor_assign"),
570 OperatorMetadataEntry::assign("^=", "BitXorAssign", "bitxor_assign"),
571 OperatorMetadataEntry::assign("<<=", "ShlAssign", "shl_assign"),
572 OperatorMetadataEntry::assign(">>=", "ShrAssign", "shr_assign"),
Michael VanBemmel32c26df2022-08-03 16:08:58 -0700573 ];
Michael VanBemmel5014b3e2022-08-03 16:27:34 -0700574 OperatorMetadata {
Michael VanBemmel5bde56f2022-08-17 12:05:37 -0700575 by_cc_name_and_params: ENTRIES.iter().map(|e| ((e.cc_name, e.cc_params), *e)).collect(),
Michael VanBemmel5014b3e2022-08-03 16:27:34 -0700576 }
577});
Michael VanBemmel32c26df2022-08-03 16:08:58 -0700578
Devin Jeanpierre9c34da62022-03-31 04:57:16 -0700579/// Returns the shape of the generated Rust API for a given function definition.
Devin Jeanpierred7b48102022-03-31 04:15:03 -0700580///
Devin Jeanpierre2b1e46d2022-08-01 13:42:12 -0700581/// If the shape is a trait, this also mutates the parameter types to be
582/// trait-compatible. In particular, types which would be `impl Ctor<Output=T>`
583/// become a `RvalueReference<'_, T>`.
584///
Devin Jeanpierre9c34da62022-03-31 04:57:16 -0700585/// Returns:
Devin Jeanpierred7b48102022-03-31 04:15:03 -0700586///
587/// * `Err(_)`: something went wrong importing this function.
588/// * `Ok(None)`: the function imported as "nothing". (For example, a defaulted
589/// destructor might be mapped to no `Drop` impl at all.)
Devin Jeanpierre9c34da62022-03-31 04:57:16 -0700590/// * `Ok((func_name, impl_kind))`: The function name and ImplKind.
Devin Jeanpierre8cd8ae72022-06-29 18:59:40 -0700591fn api_func_shape(
Michael VanBemmel32c26df2022-08-03 16:08:58 -0700592 db: &dyn BindingsGenerator,
Devin Jeanpierre9c34da62022-03-31 04:57:16 -0700593 func: &Func,
Devin Jeanpierre2b1e46d2022-08-01 13:42:12 -0700594 param_types: &mut [RsTypeKind],
Devin Jeanpierre8cd8ae72022-06-29 18:59:40 -0700595) -> Result<Option<(Ident, ImplKind)>> {
Michael VanBemmel32c26df2022-08-03 16:08:58 -0700596 let ir = db.ir();
Michael VanBemmel5014b3e2022-08-03 16:27:34 -0700597 let op_meta = &*OPERATOR_METADATA;
Michael VanBemmel32c26df2022-08-03 16:08:58 -0700598
Devin Jeanpierre8cd8ae72022-06-29 18:59:40 -0700599 let maybe_record: Option<&Rc<Record>> = ir.record_for_member_func(func)?;
Devin Jeanpierre22f91492022-04-06 13:01:23 -0700600 let has_pointer_params = param_types.iter().any(|p| matches!(p, RsTypeKind::Pointer { .. }));
Devin Jeanpierre9c34da62022-03-31 04:57:16 -0700601 let impl_kind: ImplKind;
Lukasz Anforowiczab65e292022-01-14 23:04:21 +0000602 let func_name: syn::Ident;
Michael VanBemmelf6651302022-08-03 15:56:59 -0700603
604 let adl_check_required_and_failed = if let Some(decl_id) = func.adl_enclosing_record {
Lukasz Anforowicz42ab93b2022-08-31 11:17:59 -0700605 let adl_enclosing_record = ir
606 .find_decl::<Rc<Record>>(decl_id)
607 .with_context(|| format!("Failed to look up `adl_enclosing_record` of {:?}", func))?;
Michael VanBemmelf6651302022-08-03 15:56:59 -0700608 !is_visible_by_adl(adl_enclosing_record, param_types)
609 } else {
610 false
611 };
612
Googlerd03d05b2022-01-07 10:10:57 +0000613 match &func.name {
Michael VanBemmelf6651302022-08-03 15:56:59 -0700614 UnqualifiedIdentifier::Operator(_) | UnqualifiedIdentifier::Identifier(_)
615 if adl_check_required_and_failed =>
616 {
617 return Ok(None);
618 }
Lukasz Anforowicz9c663ca2022-02-09 01:33:31 +0000619 UnqualifiedIdentifier::Operator(op) if op.name == "==" => {
Devin Jeanpierre8dd193a2022-06-03 10:57:21 -0700620 assert_eq!(
621 param_types.len(),
622 2,
623 "Unexpected number of parameters in operator==: {func:?}"
624 );
Devin Jeanpierre22f91492022-04-06 13:01:23 -0700625 match (&param_types[0], &param_types[1]) {
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +0000626 (
Lukasz Anforowiczeb19ac62022-02-05 00:10:16 +0000627 RsTypeKind::Reference { referent: lhs, mutability: Mutability::Const, .. },
628 RsTypeKind::Reference { referent: rhs, mutability: Mutability::Const, .. },
Devin Jeanpierre8cd8ae72022-06-29 18:59:40 -0700629 ) => match &**lhs {
Devin Jeanpierrefec5bfe2022-04-05 10:59:10 -0700630 RsTypeKind::Record { record: lhs_record, .. } => {
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +0000631 func_name = make_rs_ident("eq");
Devin Jeanpierred9a6e6c2022-03-29 02:55:41 -0700632 impl_kind = ImplKind::new_trait(
Devin Jeanpierre215ee8e2022-05-16 16:09:41 -0700633 TraitName::Other {
Devin Jeanpierree77fa7e2022-06-02 14:05:58 -0700634 name: quote! {PartialEq},
635 params: vec![(**rhs).clone()],
Devin Jeanpierre215ee8e2022-05-16 16:09:41 -0700636 is_unsafe_fn: false,
637 },
Lukasz Anforowicz92be29f2022-09-02 17:12:29 -0700638 lhs_record, &ir,
Devin Jeanpierred9a6e6c2022-03-29 02:55:41 -0700639 /* format_first_param_as_self= */ true,
Lukasz Anforowicz92be29f2022-09-02 17:12:29 -0700640 )?;
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +0000641 }
Marcel Hlopko14ee3c82022-02-09 09:46:23 +0000642 _ => {
Devin Jeanpierred7b48102022-03-31 04:15:03 -0700643 bail!("operator== where lhs doesn't refer to a record",);
Marcel Hlopko14ee3c82022-02-09 09:46:23 +0000644 }
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +0000645 },
Marcel Hlopko14ee3c82022-02-09 09:46:23 +0000646 _ => {
Devin Jeanpierred7b48102022-03-31 04:15:03 -0700647 bail!("operator== where operands are not const references",);
Marcel Hlopko14ee3c82022-02-09 09:46:23 +0000648 }
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +0000649 };
650 }
Devin Jeanpierre9ced4ef2022-06-08 12:39:10 -0700651 UnqualifiedIdentifier::Operator(op) if op.name == "=" => {
652 assert_eq!(
653 param_types.len(),
654 2,
655 "Unexpected number of parameters in operator=: {func:?}"
656 );
657 let record =
658 maybe_record.ok_or_else(|| anyhow!("operator= must be a member function."))?;
659 if record.is_unpin() {
660 bail!("operator= for Unpin types is not yet supported.");
661 }
Devin Jeanpierre6bb81802022-08-10 02:08:47 -0700662 materialize_ctor_in_caller(func, param_types);
Devin Jeanpierre9ced4ef2022-06-08 12:39:10 -0700663 let rhs = &param_types[1];
Michael VanBemmelf6651302022-08-03 15:56:59 -0700664 impl_kind = {
665 ImplKind::Trait {
666 trait_name: TraitName::Other {
667 name: quote! {::ctor::Assign},
668 params: vec![rhs.clone()],
669 is_unsafe_fn: false,
670 },
671 record_name: make_rs_ident(&record.rs_name),
Lukasz Anforowicz92be29f2022-09-02 17:12:29 -0700672 record_qualifier: NamespaceQualifier::new(record.id, &ir)?,
Michael VanBemmelf6651302022-08-03 15:56:59 -0700673 impl_for: ImplFor::T,
674 trait_generic_params: vec![],
675 format_first_param_as_self: true,
676 drop_return: true,
677 associated_return_type: None,
678 }
679 };
Devin Jeanpierre9ced4ef2022-06-08 12:39:10 -0700680 func_name = make_rs_ident("assign");
681 }
Michael VanBemmel5bde56f2022-08-17 12:05:37 -0700682 UnqualifiedIdentifier::Operator(op) => match op_meta
683 .by_cc_name_and_params
684 .get(&(op.name.as_str(), param_types.len()))
Michael VanBemmel32c26df2022-08-03 16:08:58 -0700685 {
Michael VanBemmel5bde56f2022-08-17 12:05:37 -0700686 Some(OperatorMetadataEntry {
687 trait_name,
688 method_name,
689 is_compound_assignment: false,
690 ..
691 }) => {
692 materialize_ctor_in_caller(func, param_types);
Lukasz Anforowiczcb1f9c22022-09-02 12:40:01 -0700693 let (record, impl_for) = match &param_types[0] {
694 RsTypeKind::Record { record, .. } => (&**record, ImplFor::T),
695 RsTypeKind::Reference { referent, .. } => (
696 match &**referent {
697 RsTypeKind::Record { record, .. } => &**record,
698 _ => bail!("Expected first parameter referent to be a record"),
699 },
700 ImplFor::RefT,
701 ),
702 RsTypeKind::RvalueReference { .. } => {
703 bail!("Not yet supported for rvalue references (b/219826128)")
Michael VanBemmel5bde56f2022-08-17 12:05:37 -0700704 }
Lukasz Anforowiczcb1f9c22022-09-02 12:40:01 -0700705 _ => bail!("Expected first parameter to be a record or reference"),
Michael VanBemmel5bde56f2022-08-17 12:05:37 -0700706 };
707
708 let trait_name = make_rs_ident(trait_name);
709 impl_kind = ImplKind::Trait {
710 record_name: make_rs_ident(&record.rs_name),
Lukasz Anforowicz92be29f2022-09-02 17:12:29 -0700711 record_qualifier: NamespaceQualifier::new(record.id, &ir)?,
Michael VanBemmel5bde56f2022-08-17 12:05:37 -0700712 trait_name: TraitName::Other {
713 name: quote! {::std::ops::#trait_name},
714 params: param_types[1..].to_vec(),
715 is_unsafe_fn: false,
716 },
717 impl_for,
718 trait_generic_params: vec![],
719 format_first_param_as_self: true,
720 drop_return: false,
721 associated_return_type: Some(make_rs_ident("Output")),
722 };
723 func_name = make_rs_ident(method_name);
724 }
725 Some(OperatorMetadataEntry {
726 trait_name,
727 method_name,
728 is_compound_assignment: true,
729 ..
730 }) => {
731 materialize_ctor_in_caller(func, param_types);
732 let record = match &param_types[0] {
733 RsTypeKind::Record { .. } => {
734 bail!("Compound assignment with by-value left-hand side is not supported")
735 }
736 RsTypeKind::Reference { mutability: Mutability::Const, .. } => {
737 bail!("Compound assignment with const left-hand side is not supported")
738 }
739 RsTypeKind::Reference { referent, mutability: Mutability::Mut, .. } => {
Michael VanBemmel83eca6b2022-07-20 10:16:38 -0700740 match &**referent {
Michael VanBemmel5bde56f2022-08-17 12:05:37 -0700741 RsTypeKind::Record { record, .. } => &**maybe_record.unwrap_or(record),
Michael VanBemmel83eca6b2022-07-20 10:16:38 -0700742 _ => bail!("Expected first parameter referent to be a record"),
Michael VanBemmel5bde56f2022-08-17 12:05:37 -0700743 }
744 }
Michael VanBemmel83eca6b2022-07-20 10:16:38 -0700745 RsTypeKind::RvalueReference { .. } => {
746 bail!("Not yet supported for rvalue references (b/219826128)")
747 }
Michael VanBemmel5bde56f2022-08-17 12:05:37 -0700748 RsTypeKind::Pointer { .. } => {
749 bail!("Not yet supported for pointers with unknown lifetime (b/219826128)")
Michael VanBemmelf6651302022-08-03 15:56:59 -0700750 }
Michael VanBemmel5bde56f2022-08-17 12:05:37 -0700751 _ => bail!("Expected first parameter to be a record or reference"),
752 };
Michael VanBemmelf6651302022-08-03 15:56:59 -0700753
Michael VanBemmel5bde56f2022-08-17 12:05:37 -0700754 let trait_name = make_rs_ident(trait_name);
755 impl_kind = ImplKind::Trait {
756 record_name: make_rs_ident(&record.rs_name),
Lukasz Anforowicz92be29f2022-09-02 17:12:29 -0700757 record_qualifier: NamespaceQualifier::new(record.id, &ir)?,
Michael VanBemmel5bde56f2022-08-17 12:05:37 -0700758 trait_name: TraitName::Other {
759 name: quote! {::std::ops::#trait_name},
760 params: param_types[1..].to_vec(),
761 is_unsafe_fn: false,
762 },
763 impl_for: ImplFor::T,
764 trait_generic_params: vec![],
765 format_first_param_as_self: true,
766 drop_return: true,
767 associated_return_type: None,
768 };
769 func_name = make_rs_ident(method_name);
770 }
771 None => {
772 bail!(
773 "Bindings for this kind of operator (operator {op} with {n} parameter(s)) are not supported",
774 op = &op.name,
775 n = param_types.len(),
776 );
777 }
778 },
Devin Jeanpierref2ec8712021-10-13 20:47:16 +0000779 UnqualifiedIdentifier::Identifier(id) => {
Marcel Hlopkoeaae9b72022-01-21 15:54:11 +0000780 func_name = make_rs_ident(&id.identifier);
Lukasz Anforowiczf9564622022-01-28 14:31:04 +0000781 match maybe_record {
782 None => {
Devin Jeanpierre7c3d8ed2022-03-29 03:02:04 -0700783 impl_kind = ImplKind::None { is_unsafe: has_pointer_params };
Lukasz Anforowiczf9564622022-01-28 14:31:04 +0000784 }
785 Some(record) => {
Devin Jeanpierred9a6e6c2022-03-29 02:55:41 -0700786 let format_first_param_as_self = if func.is_instance_method() {
Devin Jeanpierre22f91492022-04-06 13:01:23 -0700787 let first_param = param_types.first().ok_or_else(|| {
Lukasz Anforowiczf9564622022-01-28 14:31:04 +0000788 anyhow!("Missing `__this` parameter in an instance method: {:?}", func)
789 })?;
Devin Jeanpierred9a6e6c2022-03-29 02:55:41 -0700790 first_param.is_ref_to(record)
Lukasz Anforowiczf9564622022-01-28 14:31:04 +0000791 } else {
Devin Jeanpierred9a6e6c2022-03-29 02:55:41 -0700792 false
793 };
Devin Jeanpierre6784e5e2022-03-29 02:59:01 -0700794 impl_kind = ImplKind::Struct {
795 record_name: make_rs_ident(&record.rs_name),
796 format_first_param_as_self,
Devin Jeanpierre7c3d8ed2022-03-29 03:02:04 -0700797 is_unsafe: has_pointer_params,
Devin Jeanpierre6784e5e2022-03-29 02:59:01 -0700798 };
Lukasz Anforowiczf9564622022-01-28 14:31:04 +0000799 }
800 };
Michael Forstered642022021-10-04 09:48:25 +0000801 }
Devin Jeanpierre91de7012021-10-21 12:53:51 +0000802 UnqualifiedIdentifier::Destructor => {
Lukasz Anforowiczab65e292022-01-14 23:04:21 +0000803 // Note: to avoid double-destruction of the fields, they are all wrapped in
804 // ManuallyDrop in this case. See `generate_record`.
805 let record =
806 maybe_record.ok_or_else(|| anyhow!("Destructors must be member functions."))?;
Lukasz Anforowicze57215c2022-01-12 14:54:16 +0000807 if !should_implement_drop(record) {
Devin Jeanpierred7b48102022-03-31 04:15:03 -0700808 return Ok(None);
Devin Jeanpierre91de7012021-10-21 12:53:51 +0000809 }
Devin Jeanpierre215ee8e2022-05-16 16:09:41 -0700810 if record.is_unpin() {
811 impl_kind = ImplKind::new_trait(
Devin Jeanpierree77fa7e2022-06-02 14:05:58 -0700812 TraitName::Other { name: quote! {Drop}, params: vec![], is_unsafe_fn: false },
Lukasz Anforowicz92be29f2022-09-02 17:12:29 -0700813 record, &ir,
Devin Jeanpierre215ee8e2022-05-16 16:09:41 -0700814 /* format_first_param_as_self= */ true,
Lukasz Anforowicz92be29f2022-09-02 17:12:29 -0700815 )?;
Devin Jeanpierre215ee8e2022-05-16 16:09:41 -0700816 func_name = make_rs_ident("drop");
817 } else {
Devin Jeanpierre6bb81802022-08-10 02:08:47 -0700818 materialize_ctor_in_caller(func, param_types);
Devin Jeanpierre215ee8e2022-05-16 16:09:41 -0700819 impl_kind = ImplKind::new_trait(
Devin Jeanpierree77fa7e2022-06-02 14:05:58 -0700820 TraitName::Other {
821 name: quote! {::ctor::PinnedDrop},
822 params: vec![],
823 is_unsafe_fn: true,
824 },
Lukasz Anforowicz92be29f2022-09-02 17:12:29 -0700825 record, &ir,
Devin Jeanpierre215ee8e2022-05-16 16:09:41 -0700826 /* format_first_param_as_self= */ true,
Lukasz Anforowicz92be29f2022-09-02 17:12:29 -0700827 )?;
Devin Jeanpierre215ee8e2022-05-16 16:09:41 -0700828 func_name = make_rs_ident("pinned_drop");
829 }
Devin Jeanpierre91de7012021-10-21 12:53:51 +0000830 }
Lukasz Anforowicz13cf7492021-12-22 15:29:52 +0000831 UnqualifiedIdentifier::Constructor => {
Lukasz Anforowicz71716b72022-01-26 17:05:05 +0000832 let member_func_metadata = func
833 .member_func_metadata
834 .as_ref()
835 .ok_or_else(|| anyhow!("Constructors must be member functions."))?;
836 let record = maybe_record
837 .ok_or_else(|| anyhow!("Constructors must be associated with a record."))?;
838 let instance_method_metadata =
839 member_func_metadata
840 .instance_method_metadata
841 .as_ref()
842 .ok_or_else(|| anyhow!("Constructors must be instance methods."))?;
Devin Jeanpierre7c3d8ed2022-03-29 03:02:04 -0700843 if has_pointer_params {
Lukasz Anforowicz55673c92022-01-27 19:37:26 +0000844 // TODO(b/216648347): Allow this outside of traits (e.g. after supporting
845 // translating C++ constructors into static methods in Rust).
Devin Jeanpierred7b48102022-03-31 04:15:03 -0700846 bail!(
Lukasz Anforowiczeb19ac62022-02-05 00:10:16 +0000847 "Unsafe constructors (e.g. with no elided or explicit lifetimes) \
848 are intentionally not supported",
849 );
Lukasz Anforowicz55673c92022-01-27 19:37:26 +0000850 }
851
Devin Jeanpierrea99e0a32022-08-17 01:36:58 -0700852 check_by_value(record)?;
Devin Jeanpierre6bb81802022-08-10 02:08:47 -0700853 materialize_ctor_in_caller(func, param_types);
Devin Jeanpierre6784e5e2022-03-29 02:59:01 -0700854 let record_name = make_rs_ident(&record.rs_name);
Devin Jeanpierre6f607372022-03-22 21:34:38 +0000855 if !record.is_unpin() {
856 func_name = make_rs_ident("ctor_new");
Devin Jeanpierre6f607372022-03-22 21:34:38 +0000857
Devin Jeanpierread125742022-04-11 13:50:28 -0700858 match param_types {
859 [] => bail!("Missing `__this` parameter in a constructor: {:?}", func),
860 [_this, params @ ..] => {
Devin Jeanpierrefd4ff822022-07-15 01:43:55 -0700861 impl_kind = ImplKind::Trait {
Devin Jeanpierre1b8f4a12022-03-22 21:29:42 +0000862 record_name,
Lukasz Anforowicz92be29f2022-09-02 17:12:29 -0700863 record_qualifier: NamespaceQualifier::new(record.id, &ir)?,
Devin Jeanpierrefd4ff822022-07-15 01:43:55 -0700864 trait_name: TraitName::CtorNew(params.iter().cloned().collect()),
865 impl_for: ImplFor::T,
Devin Jeanpierrea04bce12022-08-01 13:39:48 -0700866 trait_generic_params: vec![],
Devin Jeanpierrefd4ff822022-07-15 01:43:55 -0700867 format_first_param_as_self: false,
Michael VanBemmelf6651302022-08-03 15:56:59 -0700868 drop_return: false,
Devin Jeanpierrefd4ff822022-07-15 01:43:55 -0700869 associated_return_type: Some(make_rs_ident("CtorType")),
870 };
Devin Jeanpierre6f607372022-03-22 21:34:38 +0000871 }
Lukasz Anforowicz73326af2022-01-05 01:13:10 +0000872 }
Devin Jeanpierre6f607372022-03-22 21:34:38 +0000873 } else {
874 match func.params.len() {
875 0 => bail!("Missing `__this` parameter in a constructor: {:?}", func),
876 1 => {
877 impl_kind = ImplKind::new_trait(
Devin Jeanpierree77fa7e2022-06-02 14:05:58 -0700878 TraitName::UnpinConstructor { name: quote! {Default}, params: vec![] },
Lukasz Anforowicz92be29f2022-09-02 17:12:29 -0700879 record, &ir,
Devin Jeanpierred9a6e6c2022-03-29 02:55:41 -0700880 /* format_first_param_as_self= */ false,
Lukasz Anforowicz92be29f2022-09-02 17:12:29 -0700881 )?;
Devin Jeanpierre6f607372022-03-22 21:34:38 +0000882 func_name = make_rs_ident("default");
Devin Jeanpierre6f607372022-03-22 21:34:38 +0000883 }
884 2 => {
Devin Jeanpierre22f91492022-04-06 13:01:23 -0700885 if param_types[1].is_shared_ref_to(record) {
Devin Jeanpierre6f607372022-03-22 21:34:38 +0000886 // Copy constructor
887 if should_derive_clone(record) {
Devin Jeanpierred7b48102022-03-31 04:15:03 -0700888 return Ok(None);
Devin Jeanpierre6f607372022-03-22 21:34:38 +0000889 } else {
890 impl_kind = ImplKind::new_trait(
Devin Jeanpierree77fa7e2022-06-02 14:05:58 -0700891 TraitName::UnpinConstructor {
892 name: quote! {Clone},
893 params: vec![],
894 },
Lukasz Anforowicz92be29f2022-09-02 17:12:29 -0700895 record, &ir,
Devin Jeanpierred9a6e6c2022-03-29 02:55:41 -0700896 /* format_first_param_as_self= */ true,
Lukasz Anforowicz92be29f2022-09-02 17:12:29 -0700897 )?;
Devin Jeanpierre6f607372022-03-22 21:34:38 +0000898 func_name = make_rs_ident("clone");
Devin Jeanpierre6f607372022-03-22 21:34:38 +0000899 }
900 } else if !instance_method_metadata.is_explicit_ctor {
Devin Jeanpierre22f91492022-04-06 13:01:23 -0700901 let param_type = &param_types[1];
Devin Jeanpierree77fa7e2022-06-02 14:05:58 -0700902 impl_kind = ImplKind::new_trait(
903 TraitName::UnpinConstructor {
904 name: quote! {From},
905 params: vec![param_type.clone()],
906 },
Lukasz Anforowicz92be29f2022-09-02 17:12:29 -0700907 record, &ir,
Devin Jeanpierred9a6e6c2022-03-29 02:55:41 -0700908 /* format_first_param_as_self= */ false,
Lukasz Anforowicz92be29f2022-09-02 17:12:29 -0700909 )?;
Devin Jeanpierre6f607372022-03-22 21:34:38 +0000910 func_name = make_rs_ident("from");
Devin Jeanpierre6f607372022-03-22 21:34:38 +0000911 } else {
Devin Jeanpierred7b48102022-03-31 04:15:03 -0700912 bail!("Not yet supported type of constructor parameter",);
Devin Jeanpierre6f607372022-03-22 21:34:38 +0000913 }
914 }
915 _ => {
916 // TODO(b/216648347): Support bindings for other constructors.
Devin Jeanpierred7b48102022-03-31 04:15:03 -0700917 bail!("More than 1 constructor parameter is not supported yet",);
Devin Jeanpierre6f607372022-03-22 21:34:38 +0000918 }
Lukasz Anforowicz73326af2022-01-05 01:13:10 +0000919 }
Lukasz Anforowiczab65e292022-01-14 23:04:21 +0000920 }
921 }
922 }
Devin Jeanpierre9c34da62022-03-31 04:57:16 -0700923 Ok(Some((func_name, impl_kind)))
924}
925
Devin Jeanpierre2b1e46d2022-08-01 13:42:12 -0700926/// Mutates the provided parameters so that nontrivial by-value parameters are,
927/// instead, materialized in the caller and passed by rvalue reference.
Devin Jeanpierre6bb81802022-08-10 02:08:47 -0700928fn materialize_ctor_in_caller(func: &Func, params: &mut [RsTypeKind]) {
929 let mut existing_lifetime_params: HashSet<Rc<str>> =
930 params.iter().flat_map(|param| param.lifetimes().map(|lifetime| lifetime.0)).collect();
931 let mut new_lifetime_param = |mut lifetime_name: String| {
932 let suffix_start = lifetime_name.len();
933 let mut next_suffix = 2;
934 loop {
935 if !existing_lifetime_params.contains(&*lifetime_name) {
936 let lifetime_name = <Rc<str>>::from(lifetime_name);
937 existing_lifetime_params.insert(lifetime_name.clone());
938 return Lifetime(lifetime_name);
939 }
940 lifetime_name.truncate(suffix_start);
941 write!(lifetime_name, "_{next_suffix}").unwrap();
942 next_suffix += 1;
943 }
944 };
945 for (func_param, param) in func.params.iter().zip(params.iter_mut()) {
Devin Jeanpierre2b1e46d2022-08-01 13:42:12 -0700946 if param.is_unpin() {
947 continue;
948 }
949 let value = std::mem::replace(param, RsTypeKind::Unit); // Temporarily swap in a garbage value.
950 *param = RsTypeKind::RvalueReference {
951 referent: Rc::new(value),
952 mutability: Mutability::Mut,
Devin Jeanpierre6bb81802022-08-10 02:08:47 -0700953 lifetime: new_lifetime_param(func_param.identifier.identifier.clone()),
Devin Jeanpierre2b1e46d2022-08-01 13:42:12 -0700954 };
955 }
956}
957
Devin Jeanpierre9c34da62022-03-31 04:57:16 -0700958/// Generates Rust source code for a given `Func`.
959///
960/// Returns:
961///
962/// * `Err(_)`: couldn't import the function, emit an `UnsupportedItem`.
963/// * `Ok(None)`: the function imported as "nothing". (For example, a defaulted
964/// destructor might be mapped to no `Drop` impl at all.)
965/// * `Ok((rs_api, rs_thunk, function_id))`: The Rust function definition,
966/// thunk FFI definition, and function ID.
Devin Jeanpierre52a14c32022-06-29 19:12:11 -0700967fn generate_func(
968 db: &dyn BindingsGenerator,
969 func: Rc<Func>,
Devin Jeanpierrece549202022-07-25 08:54:34 -0700970) -> Result<Option<RcEq<(RsSnippet, RsSnippet, Rc<FunctionId>)>>> {
Lukasz Anforowicz8d064202022-09-01 07:31:06 -0700971 let ir = db.ir();
Devin Jeanpierrefd4ff822022-07-15 01:43:55 -0700972 let mut features = BTreeSet::new();
Devin Jeanpierre2b1e46d2022-08-01 13:42:12 -0700973 let mut param_types = func
Devin Jeanpierre9c34da62022-03-31 04:57:16 -0700974 .params
975 .iter()
976 .map(|p| {
Devin Jeanpierre409f6f62022-07-07 02:00:26 -0700977 db.rs_type_kind(p.type_.rs_type.clone()).with_context(|| {
Devin Jeanpierre9c34da62022-03-31 04:57:16 -0700978 format!("Failed to process type of parameter {:?} on {:?}", p, func)
979 })
980 })
981 .collect::<Result<Vec<_>>>()?;
982
Devin Jeanpierre2b1e46d2022-08-01 13:42:12 -0700983 let (func_name, mut impl_kind) =
Michael VanBemmel32c26df2022-08-03 16:08:58 -0700984 if let Some(values) = api_func_shape(db, &func, &mut param_types)? {
Devin Jeanpierre2b1e46d2022-08-01 13:42:12 -0700985 values
986 } else {
987 return Ok(None);
988 };
Lukasz Anforowicz92be29f2022-09-02 17:12:29 -0700989 let namespace_qualifier = NamespaceQualifier::new(func.id, &ir)?.format_for_rs();
Lukasz Anforowiczab65e292022-01-14 23:04:21 +0000990
Devin Jeanpierrefd4ff822022-07-15 01:43:55 -0700991 let mut return_type = db
Devin Jeanpierre409f6f62022-07-07 02:00:26 -0700992 .rs_type_kind(func.return_type.rs_type.clone())
Devin Jeanpierreb8ce2c12022-07-13 10:34:01 -0700993 .with_context(|| format!("Failed to format return type for {:?}", &func))?;
Devin Jeanpierrea99e0a32022-08-17 01:36:58 -0700994 return_type.check_by_value()?;
Devin Jeanpierred9cecff2022-03-29 02:53:58 -0700995 let param_idents =
996 func.params.iter().map(|p| make_rs_ident(&p.identifier.identifier)).collect_vec();
Devin Jeanpierreb8ce2c12022-07-13 10:34:01 -0700997 let thunk = generate_func_thunk(db, &func, &param_idents, &param_types, &return_type)?;
Devin Jeanpierred9cecff2022-03-29 02:53:58 -0700998
Devin Jeanpierref5013fb2022-08-01 13:56:27 -0700999 let BindingsSignature {
1000 lifetimes,
1001 params: api_params,
1002 return_type_fragment: mut quoted_return_type,
Devin Jeanpierre93927e82022-08-01 14:05:54 -07001003 thunk_prepare,
Devin Jeanpierref5013fb2022-08-01 13:56:27 -07001004 thunk_args,
1005 } = function_signature(
1006 &mut features,
1007 &func,
1008 &impl_kind,
1009 &param_idents,
1010 &mut param_types,
1011 &mut return_type,
1012 )?;
Devin Jeanpierrefd4ff822022-07-15 01:43:55 -07001013
1014 let api_func_def = {
Devin Jeanpierre6f607372022-03-22 21:34:38 +00001015 // TODO(b/200067242): the Pin-wrapping code doesn't know to wrap &mut
1016 // MaybeUninit<T> in Pin if T is !Unpin. It should understand
1017 // 'structural pinning', so that we do not need into_inner_unchecked()
1018 // here.
Devin Jeanpierre52a14c32022-06-29 19:12:11 -07001019 let thunk_ident = thunk_ident(&func);
Devin Jeanpierre7bddfdb2022-03-14 11:04:40 +00001020 let func_body = match &impl_kind {
Devin Jeanpierree77fa7e2022-06-02 14:05:58 -07001021 ImplKind::Trait { trait_name: TraitName::UnpinConstructor { .. }, .. } => {
Lukasz Anforowiczab65e292022-01-14 23:04:21 +00001022 // SAFETY: A user-defined constructor is not guaranteed to
1023 // initialize all the fields. To make the `assume_init()` call
1024 // below safe, the memory is zero-initialized first. This is a
1025 // bit safer, because zero-initialized memory represents a valid
1026 // value for the currently supported field types (this may
1027 // change once the bindings generator starts supporting
1028 // reference fields). TODO(b/213243309): Double-check if
1029 // zero-initialization is desirable here.
1030 quote! {
Devin Jeanpierrecd5ebf32022-07-08 05:31:55 -07001031 let mut tmp = ::std::mem::MaybeUninit::<Self>::zeroed();
Lukasz Anforowiczab65e292022-01-14 23:04:21 +00001032 unsafe {
Rosica Dejanovska68c11ac2022-05-12 03:48:59 -07001033 crate::detail::#thunk_ident( &mut tmp #( , #thunk_args )* );
Lukasz Anforowiczab65e292022-01-14 23:04:21 +00001034 tmp.assume_init()
Lukasz Anforowicz13cf7492021-12-22 15:29:52 +00001035 }
Lukasz Anforowicz13cf7492021-12-22 15:29:52 +00001036 }
Lukasz Anforowiczab65e292022-01-14 23:04:21 +00001037 }
Devin Jeanpierre7bddfdb2022-03-14 11:04:40 +00001038 _ => {
Devin Jeanpierreb8ce2c12022-07-13 10:34:01 -07001039 // Note: for the time being, all !Unpin values are treated as if they were not
1040 // trivially relocatable. We could, in the special case of trivial !Unpin types,
1041 // not generate the thunk at all, but this would be a bit of extra work.
1042 //
1043 // TODO(jeanpierreda): separately handle non-Unpin and non-trivial types.
1044 let mut body = if return_type.is_unpin() {
1045 quote! { crate::detail::#thunk_ident( #( #thunk_args ),* ) }
1046 } else {
1047 quote! {
1048 ::ctor::FnCtor::new(move |dest: ::std::pin::Pin<&mut ::std::mem::MaybeUninit<#return_type>>| {
1049 crate::detail::#thunk_ident(::std::pin::Pin::into_inner_unchecked(dest) #( , #thunk_args )*);
1050 })
1051 }
1052 };
Michael VanBemmelf6651302022-08-03 15:56:59 -07001053 // Discard the return value if requested (for example, when calling a C++
1054 // operator that returns a value from a Rust trait that returns
1055 // unit).
1056 if let ImplKind::Trait { drop_return: true, .. } = impl_kind {
1057 if return_type.is_unpin() {
1058 // If it's unpin, just discard it:
1059 body = quote! { #body; };
1060 } else {
1061 // Otherwise, in order to discard the return value and return void, we
1062 // need to run the constructor.
1063 body = quote! {let _ = ::ctor::emplace!(#body);};
Devin Jeanpierre9ced4ef2022-06-08 12:39:10 -07001064 }
Michael VanBemmelf6651302022-08-03 15:56:59 -07001065
1066 // We would need to do this, but it's no longer used:
1067 // return_type = RsTypeKind::Unit;
1068 let _ = return_type; // proof that we don't need to update it.
1069 quoted_return_type = quote! {};
Devin Jeanpierre9ced4ef2022-06-08 12:39:10 -07001070 }
Devin Jeanpierre7bddfdb2022-03-14 11:04:40 +00001071 // Only need to wrap everything in an `unsafe { ... }` block if
1072 // the *whole* api function is safe.
Devin Jeanpierre7c3d8ed2022-03-29 03:02:04 -07001073 if !impl_kind.is_unsafe() {
Devin Jeanpierre7bddfdb2022-03-14 11:04:40 +00001074 body = quote! { unsafe { #body } };
1075 }
Devin Jeanpierre93927e82022-08-01 14:05:54 -07001076 quote! {
1077 #thunk_prepare
1078 #body
1079 }
Devin Jeanpierre7bddfdb2022-03-14 11:04:40 +00001080 }
Lukasz Anforowiczab65e292022-01-14 23:04:21 +00001081 };
1082
Devin Jeanpierre7c3d8ed2022-03-29 03:02:04 -07001083 let pub_ = match impl_kind {
1084 ImplKind::None { .. } | ImplKind::Struct { .. } => quote! { pub },
1085 ImplKind::Trait { .. } => quote! {},
1086 };
1087 let unsafe_ = if impl_kind.is_unsafe() {
1088 quote! { unsafe }
1089 } else {
1090 quote! {}
Lukasz Anforowiczab65e292022-01-14 23:04:21 +00001091 };
1092
Lukasz Anforowicz5e623782022-03-14 16:52:23 +00001093 let fn_generic_params: TokenStream;
Devin Jeanpierre2b1e46d2022-08-01 13:42:12 -07001094 if let ImplKind::Trait { trait_name, trait_generic_params, impl_for, .. } = &mut impl_kind {
Michael VanBemmel106f66c2022-07-13 09:48:48 -07001095 // When the impl block is for some kind of reference to T, consider the lifetime
Devin Jeanpierreb8ce2c12022-07-13 10:34:01 -07001096 // parameters on the self parameter to be trait lifetimes so they can be
1097 // introduced before they are used.
Michael VanBemmel106f66c2022-07-13 09:48:48 -07001098 let first_param_lifetimes = match (impl_for, param_types.first()) {
1099 (ImplFor::RefT, Some(first_param)) => Some(first_param.lifetimes()),
1100 _ => None,
1101 };
1102
Devin Jeanpierre4e94a082022-08-10 01:58:35 -07001103 let trait_lifetimes: HashSet<Lifetime> =
Michael VanBemmel106f66c2022-07-13 09:48:48 -07001104 trait_name.lifetimes().chain(first_param_lifetimes.into_iter().flatten()).collect();
Devin Jeanpierree77fa7e2022-06-02 14:05:58 -07001105 fn_generic_params = format_generic_params(
Devin Jeanpierre4e94a082022-08-10 01:58:35 -07001106 lifetimes.iter().filter(|lifetime| !trait_lifetimes.contains(lifetime)),
Devin Jeanpierree77fa7e2022-06-02 14:05:58 -07001107 );
Devin Jeanpierrea04bce12022-08-01 13:39:48 -07001108 *trait_generic_params = lifetimes
1109 .iter()
1110 .filter_map(|lifetime| {
Michael VanBemmel5bde56f2022-08-17 12:05:37 -07001111 if trait_lifetimes.contains(lifetime) {
1112 Some(quote! {#lifetime})
1113 } else {
1114 None
1115 }
Devin Jeanpierrea04bce12022-08-01 13:39:48 -07001116 })
1117 .collect();
Lukasz Anforowicz5e623782022-03-14 16:52:23 +00001118 } else {
1119 fn_generic_params = format_generic_params(lifetimes);
1120 }
Lukasz Anforowicz95551272022-01-20 00:02:24 +00001121
Devin Jeanpierrefd4ff822022-07-15 01:43:55 -07001122 let function_return_type = match &impl_kind {
1123 ImplKind::Trait { associated_return_type: Some(ident), .. } => quote! {Self::#ident},
1124 _ => quoted_return_type.clone(),
1125 };
1126 let arrow = if !function_return_type.is_empty() {
1127 quote! {->}
1128 } else {
1129 quote! {}
1130 };
1131
Lukasz Anforowiczab65e292022-01-14 23:04:21 +00001132 quote! {
1133 #[inline(always)]
Lukasz Anforowicz5e623782022-03-14 16:52:23 +00001134 #pub_ #unsafe_ fn #func_name #fn_generic_params(
Devin Jeanpierrefd4ff822022-07-15 01:43:55 -07001135 #( #api_params ),* ) #arrow #function_return_type {
Lukasz Anforowiczab65e292022-01-14 23:04:21 +00001136 #func_body
1137 }
Lukasz Anforowicz13cf7492021-12-22 15:29:52 +00001138 }
Michael Forstered642022021-10-04 09:48:25 +00001139 };
1140
Devin Jeanpierred9cecff2022-03-29 02:53:58 -07001141 let doc_comment = generate_doc_comment(&func.doc_comment);
Lukasz Anforowiczab65e292022-01-14 23:04:21 +00001142 let api_func: TokenStream;
1143 let function_id: FunctionId;
Lukasz Anforowiczab65e292022-01-14 23:04:21 +00001144 match impl_kind {
Devin Jeanpierre7c3d8ed2022-03-29 03:02:04 -07001145 ImplKind::None { .. } => {
Lukasz Anforowiczab65e292022-01-14 23:04:21 +00001146 api_func = quote! { #doc_comment #api_func_def };
Lukasz Anforowicz8d064202022-09-01 07:31:06 -07001147 function_id = FunctionId {
1148 self_type: None,
1149 function_path: syn::parse2(quote! { #namespace_qualifier #func_name }).unwrap(),
1150 };
Lukasz Anforowiczab65e292022-01-14 23:04:21 +00001151 }
Devin Jeanpierre6784e5e2022-03-29 02:59:01 -07001152 ImplKind::Struct { record_name, .. } => {
Lukasz Anforowiczab65e292022-01-14 23:04:21 +00001153 api_func = quote! { impl #record_name { #doc_comment #api_func_def } };
1154 function_id = FunctionId {
1155 self_type: None,
Lukasz Anforowicz8d064202022-09-01 07:31:06 -07001156 function_path: syn::parse2(quote! {
1157 #namespace_qualifier #record_name :: #func_name
1158 }).unwrap(),
Lukasz Anforowiczab65e292022-01-14 23:04:21 +00001159 };
1160 }
Michael VanBemmel106f66c2022-07-13 09:48:48 -07001161 ImplKind::Trait {
1162 trait_name,
1163 record_name,
Lukasz Anforowicz92be29f2022-09-02 17:12:29 -07001164 record_qualifier,
Michael VanBemmel106f66c2022-07-13 09:48:48 -07001165 impl_for,
1166 trait_generic_params,
Devin Jeanpierrefd4ff822022-07-15 01:43:55 -07001167 associated_return_type,
Michael VanBemmel106f66c2022-07-13 09:48:48 -07001168 ..
1169 } => {
Devin Jeanpierrefd4ff822022-07-15 01:43:55 -07001170 let extra_body = if let Some(name) = associated_return_type {
1171 let quoted_return_type = if quoted_return_type.is_empty() {
1172 quote! {()}
1173 } else {
1174 quoted_return_type
1175 };
1176 quote! {
1177 type #name = #quoted_return_type;
1178 }
1179 } else {
1180 quote! {}
1181 };
1182
Devin Jeanpierre6f607372022-03-22 21:34:38 +00001183 let extra_items;
Devin Jeanpierrea04bce12022-08-01 13:39:48 -07001184 let trait_generic_params = format_generic_params(trait_generic_params);
Devin Jeanpierre46d515c2022-05-03 02:36:54 -07001185 match &trait_name {
1186 TraitName::CtorNew(params) => {
Devin Jeanpierre46d515c2022-05-03 02:36:54 -07001187 if let [single_param] = params.as_slice() {
1188 extra_items = quote! {
Devin Jeanpierrecd5ebf32022-07-08 05:31:55 -07001189 impl #trait_generic_params ::ctor::CtorNew<(#single_param,)> for #record_name {
Devin Jeanpierre46d515c2022-05-03 02:36:54 -07001190 #extra_body
1191
1192 #[inline (always)]
1193 fn ctor_new(args: (#single_param,)) -> Self::CtorType {
1194 let (arg,) = args;
Devin Jeanpierrecd5ebf32022-07-08 05:31:55 -07001195 <Self as ::ctor::CtorNew<#single_param>>::ctor_new(arg)
Devin Jeanpierre46d515c2022-05-03 02:36:54 -07001196 }
1197 }
1198 }
1199 } else {
1200 extra_items = quote! {}
1201 }
Devin Jeanpierre6f607372022-03-22 21:34:38 +00001202 }
1203 _ => {
1204 extra_items = quote! {};
1205 }
1206 };
Michael VanBemmel106f66c2022-07-13 09:48:48 -07001207 let impl_for = match impl_for {
1208 ImplFor::T => quote! { #record_name },
1209 ImplFor::RefT => {
1210 let param = &param_types[0];
1211 quote! { #param }
1212 }
1213 };
Lukasz Anforowicz5e623782022-03-14 16:52:23 +00001214 api_func = quote! {
1215 #doc_comment
Michael VanBemmel106f66c2022-07-13 09:48:48 -07001216 impl #trait_generic_params #trait_name for #impl_for {
Devin Jeanpierre46d515c2022-05-03 02:36:54 -07001217 #extra_body
Lukasz Anforowicz5e623782022-03-14 16:52:23 +00001218 #api_func_def
1219 }
Devin Jeanpierre46d515c2022-05-03 02:36:54 -07001220 #extra_items
Lukasz Anforowicz5e623782022-03-14 16:52:23 +00001221 };
Lukasz Anforowicz92be29f2022-09-02 17:12:29 -07001222 let record_qualifier = record_qualifier.format_for_rs();
Lukasz Anforowiczab65e292022-01-14 23:04:21 +00001223 function_id = FunctionId {
Lukasz Anforowicz92be29f2022-09-02 17:12:29 -07001224 self_type: Some(syn::parse2(quote! { #record_qualifier #record_name }).unwrap()),
Devin Jeanpierre56fda1c2022-07-27 07:55:46 -07001225 function_path: syn::parse2(quote! { #trait_name :: #func_name }).unwrap(),
Lukasz Anforowiczab65e292022-01-14 23:04:21 +00001226 };
1227 }
1228 }
1229
Devin Jeanpierrece549202022-07-25 08:54:34 -07001230 Ok(Some(RcEq::new((
Devin Jeanpierre52a14c32022-06-29 19:12:11 -07001231 RsSnippet { features, tokens: api_func },
1232 thunk.into(),
1233 Rc::new(function_id),
Devin Jeanpierrece549202022-07-25 08:54:34 -07001234 ))))
Devin Jeanpierre3eb5bbd2022-04-06 12:56:19 -07001235}
Devin Jeanpierre7e9a1de2021-12-03 08:04:22 +00001236
Devin Jeanpierref5013fb2022-08-01 13:56:27 -07001237/// The function signature for a function's bindings.
1238struct BindingsSignature {
1239 /// The lifetime parameters for the Rust function.
Devin Jeanpierre4e94a082022-08-10 01:58:35 -07001240 lifetimes: Vec<Lifetime>,
Devin Jeanpierref5013fb2022-08-01 13:56:27 -07001241
1242 /// The parameter list for the Rust function.
1243 ///
1244 /// For example, `vec![quote!{self}, quote!{x: &i32}]`.
1245 params: Vec<TokenStream>,
1246
1247 /// The return type fragment of the Rust function, as a token stream.
1248 ///
1249 /// This is the same as the actual return type, except that () is the empty
1250 /// tokens, non-Unpin by-value types are `impl Ctor<Output=#return_type> +
1251 /// ...`, and wherever the type is the type of `Self`, it gets replaced by
1252 /// literal `Self`.
1253 return_type_fragment: TokenStream,
1254
Devin Jeanpierre93927e82022-08-01 14:05:54 -07001255 /// Any preparation code to define the arguments in `thunk_args`.
1256 thunk_prepare: TokenStream,
1257
Devin Jeanpierref5013fb2022-08-01 13:56:27 -07001258 /// The arguments passed to the thunk, expressed in terms of `params`.
1259 thunk_args: Vec<TokenStream>,
1260}
1261
1262/// Reformats API parameters and return values to match Rust conventions and the
1263/// trait requirements.
1264///
1265/// For example:
1266///
1267/// * Use the `self` keyword for the this pointer.
1268/// * Use `Self` for the return value of constructor traits.
1269/// * For C++ constructors, remove `self` from the Rust side (as it becomes the
1270/// return value), retaining it on the C++ side / thunk args.
1271/// * serialize a `()` as the empty string.
1272fn function_signature(
1273 features: &mut BTreeSet<Ident>,
1274 func: &Func,
1275 impl_kind: &ImplKind,
1276 param_idents: &[Ident],
1277 param_types: &mut Vec<RsTypeKind>,
1278 return_type: &mut RsTypeKind,
1279) -> Result<BindingsSignature> {
1280 let mut api_params = Vec::with_capacity(func.params.len());
1281 let mut thunk_args = Vec::with_capacity(func.params.len());
Devin Jeanpierre93927e82022-08-01 14:05:54 -07001282 let mut thunk_prepare = quote! {};
Devin Jeanpierref5013fb2022-08-01 13:56:27 -07001283 for (i, (ident, type_)) in param_idents.iter().zip(param_types.iter()).enumerate() {
Devin Jeanpierrea99e0a32022-08-17 01:36:58 -07001284 type_.check_by_value()?;
Devin Jeanpierref5013fb2022-08-01 13:56:27 -07001285 if !type_.is_unpin() {
1286 // `impl Ctor` will fail to compile in a trait.
1287 // This will only be hit if there was a bug in api_func_shape.
1288 if let ImplKind::Trait { .. } = &impl_kind {
1289 panic!(
1290 "non-Unpin types cannot work by value in traits; this should have instead \
1291 become an rvalue reference to force the caller to materialize the Ctor."
1292 );
1293 }
1294 // The generated bindings require a move constructor.
1295 if !type_.is_move_constructible() {
1296 bail!("Non-movable, non-trivial_abi type '{type}' is not supported by value as parameter #{i}", type=quote!{#type_});
1297 }
1298 api_params.push(quote! {#ident: impl ::ctor::Ctor<Output=#type_>});
1299 thunk_args
1300 .push(quote! {::std::pin::Pin::into_inner_unchecked(::ctor::emplace!(#ident))});
1301 } else {
1302 api_params.push(quote! {#ident: #type_});
1303 thunk_args.push(quote! {#ident});
1304 }
1305 }
Devin Jeanpierre6bb81802022-08-10 02:08:47 -07001306
1307 let mut lifetimes: Vec<Lifetime> = unique_lifetimes(&*param_types).collect();
1308
Devin Jeanpierref5013fb2022-08-01 13:56:27 -07001309 let mut quoted_return_type = None;
1310 if let ImplKind::Trait {
1311 trait_name: trait_name @ (TraitName::UnpinConstructor { .. } | TraitName::CtorNew(..)),
1312 ..
1313 } = &impl_kind
1314 {
Devin Jeanpierre4e94a082022-08-10 01:58:35 -07001315 // For constructors, we move the output parameter to be the return value.
1316 // The return value is "really" void.
1317 ensure!(
1318 func.return_type.rs_type.is_unit_type(),
1319 "Unexpectedly non-void return type of a constructor"
1320 );
1321
Devin Jeanpierref5013fb2022-08-01 13:56:27 -07001322 // Presence of element #0 is indirectly verified by a `Constructor`-related
1323 // `match` branch a little bit above.
1324 *return_type = param_types[0]
1325 .referent()
1326 .ok_or_else(|| anyhow!("Expected pointer/reference for `__this` parameter"))?
1327 .clone();
1328 quoted_return_type = Some(quote! {Self});
1329
Devin Jeanpierre4e94a082022-08-10 01:58:35 -07001330 // Grab the `__this` lifetime to remove it from the lifetime parameters.
1331 let this_lifetime = param_types[0]
1332 .lifetime()
1333 .ok_or_else(|| anyhow!("Missing lifetime for `__this` parameter"))?;
1334
Devin Jeanpierref5013fb2022-08-01 13:56:27 -07001335 // Drop `__this` parameter from the public Rust API.
1336 api_params.remove(0);
1337 thunk_args.remove(0);
1338 param_types.remove(0);
1339
1340 // Remove the lifetime associated with `__this`.
Devin Jeanpierre4e94a082022-08-10 01:58:35 -07001341 lifetimes.retain(|l| l != &this_lifetime);
Devin Jeanpierref5013fb2022-08-01 13:56:27 -07001342 if let Some(type_still_dependent_on_removed_lifetime) = param_types
1343 .iter()
1344 .flat_map(|t| t.lifetimes())
Devin Jeanpierre4e94a082022-08-10 01:58:35 -07001345 .find(|lifetime| lifetime == &this_lifetime)
Devin Jeanpierref5013fb2022-08-01 13:56:27 -07001346 {
1347 bail!(
1348 "The lifetime of `__this` is unexpectedly also used by another \
Devin Jeanpierre4e94a082022-08-10 01:58:35 -07001349 parameter: {type_still_dependent_on_removed_lifetime:?}",
Devin Jeanpierref5013fb2022-08-01 13:56:27 -07001350 );
1351 }
1352
Devin Jeanpierre93927e82022-08-01 14:05:54 -07001353 // CtorNew groups parameters into a tuple.
Devin Jeanpierref5013fb2022-08-01 13:56:27 -07001354 if let TraitName::CtorNew(args_type) = trait_name {
1355 let args_type = format_tuple_except_singleton(args_type);
1356 api_params = vec![quote! {args: #args_type}];
Devin Jeanpierre93927e82022-08-01 14:05:54 -07001357 let thunk_vars = format_tuple_except_singleton(&thunk_args);
1358 thunk_prepare.extend(quote! {let #thunk_vars = args;});
Devin Jeanpierref5013fb2022-08-01 13:56:27 -07001359 }
1360 }
1361
1362 let return_type_fragment = if return_type == &RsTypeKind::Unit {
1363 quote! {}
1364 } else {
1365 let ty = quoted_return_type.unwrap_or_else(|| quote! {#return_type});
1366 if return_type.is_unpin() {
1367 quote! {#ty}
1368 } else {
1369 // This feature seems destined for stabilization, and makes the code
1370 // simpler. We don't need it for simple functions, but if the return type is
1371 // used as an associated type for a trait.
1372 features.insert(make_rs_ident("type_alias_impl_trait"));
1373 // The returned lazy FnCtor depends on all inputs.
1374 let extra_lifetimes = lifetimes.iter().map(|a| quote! {+ ::ctor::Captures<#a>});
1375 quote! {impl ::ctor::Ctor<Output=#ty> #(#extra_lifetimes)* }
1376 }
1377 };
1378
1379 // Change `__this: &'a SomeStruct` into `&'a self` if needed.
1380 if impl_kind.format_first_param_as_self() {
1381 let first_api_param = param_types
1382 .get(0)
1383 .ok_or_else(|| anyhow!("No parameter to format as 'self': {:?}", func))?;
1384 // If param_types[0] exists, so do api_params[0] and thunk_args[0].
1385 match impl_kind {
1386 ImplKind::None { .. } => unreachable!(),
1387 ImplKind::Struct { .. } | ImplKind::Trait { impl_for: ImplFor::T, .. } => {
1388 // In the ImplFor::T reference style (which is implied for ImplKind::Struct) the
1389 // impl block is for `T`. The `self` parameter has a type determined by the
1390 // first parameter (typically a reference of some kind) and can be passed to a
1391 // thunk via the expression `self`.
1392 api_params[0] = first_api_param.format_as_self_param()?;
1393 thunk_args[0] = quote! { self };
1394 }
1395 ImplKind::Trait { impl_for: ImplFor::RefT, .. } => {
1396 // In the ImplFor::RefT reference style the impl block is for a reference type
1397 // referring to T (`&T`, `&mut T`, or `Pin<&mut T>` so a bare `self` parameter
1398 // has that type and can be passed to a thunk via the expression `self`.
1399 api_params[0] = quote! { self };
1400 thunk_args[0] = quote! { self };
1401 }
1402 }
1403 }
1404
Devin Jeanpierre93927e82022-08-01 14:05:54 -07001405 Ok(BindingsSignature {
1406 lifetimes,
1407 params: api_params,
1408 return_type_fragment,
1409 thunk_prepare,
1410 thunk_args,
1411 })
Devin Jeanpierref5013fb2022-08-01 13:56:27 -07001412}
1413
Devin Jeanpierre3eb5bbd2022-04-06 12:56:19 -07001414fn generate_func_thunk(
Devin Jeanpierreb8ce2c12022-07-13 10:34:01 -07001415 db: &dyn BindingsGenerator,
Devin Jeanpierre3eb5bbd2022-04-06 12:56:19 -07001416 func: &Func,
1417 param_idents: &[Ident],
Devin Jeanpierre22f91492022-04-06 13:01:23 -07001418 param_types: &[RsTypeKind],
Devin Jeanpierreb8ce2c12022-07-13 10:34:01 -07001419 return_type: &RsTypeKind,
Devin Jeanpierre3eb5bbd2022-04-06 12:56:19 -07001420) -> Result<TokenStream> {
Devin Jeanpierreb8ce2c12022-07-13 10:34:01 -07001421 let thunk_attr = if can_skip_cc_thunk(db, func) {
Devin Jeanpierre3eb5bbd2022-04-06 12:56:19 -07001422 let mangled_name = &func.mangled_name;
1423 quote! {#[link_name = #mangled_name]}
1424 } else {
1425 quote! {}
Michael Forstered642022021-10-04 09:48:25 +00001426 };
Devin Jeanpierre6bb81802022-08-10 02:08:47 -07001427 let lifetimes: Vec<_> = unique_lifetimes(param_types).collect();
Michael Forstered642022021-10-04 09:48:25 +00001428
Devin Jeanpierreb8ce2c12022-07-13 10:34:01 -07001429 // The first parameter is the output parameter, if any.
Devin Jeanpierre22f91492022-04-06 13:01:23 -07001430 let mut param_types = param_types.into_iter();
Devin Jeanpierreb8ce2c12022-07-13 10:34:01 -07001431 let mut param_idents = param_idents.into_iter();
1432 let mut out_param = None;
1433 let mut out_param_ident = None;
1434 let mut return_type_fragment = return_type.format_as_return_type_fragment();
Devin Jeanpierre3eb5bbd2022-04-06 12:56:19 -07001435 if func.name == UnqualifiedIdentifier::Constructor {
Devin Jeanpierreb8ce2c12022-07-13 10:34:01 -07001436 // For constructors, inject MaybeUninit into the type of `__this_` parameter.
Devin Jeanpierre22f91492022-04-06 13:01:23 -07001437 let first_param = param_types
Devin Jeanpierre3eb5bbd2022-04-06 12:56:19 -07001438 .next()
1439 .ok_or_else(|| anyhow!("Constructors should have at least one parameter (__this)"))?;
Devin Jeanpierreb8ce2c12022-07-13 10:34:01 -07001440 out_param = Some(first_param.format_mut_ref_as_uninitialized().with_context(|| {
Devin Jeanpierre3eb5bbd2022-04-06 12:56:19 -07001441 format!(
1442 "Failed to format `__this` param for a constructor thunk: {:?}",
1443 func.params.get(0)
1444 )
1445 })?);
Devin Jeanpierreb8ce2c12022-07-13 10:34:01 -07001446 out_param_ident = Some(param_idents.next().unwrap().clone());
1447 } else if !return_type.is_unpin() {
1448 // For nontrivial return types, create a new out parameter.
1449 // The lifetime doesn't matter, so we can insert a new anonymous lifetime here.
1450 out_param = Some(quote! {
1451 &mut ::std::mem::MaybeUninit< #return_type >
1452 });
1453 out_param_ident = Some(make_rs_ident("__return"));
1454 return_type_fragment = quote! {};
Devin Jeanpierre3eb5bbd2022-04-06 12:56:19 -07001455 }
1456
Devin Jeanpierre52a14c32022-06-29 19:12:11 -07001457 let thunk_ident = thunk_ident(&func);
Devin Jeanpierre6bb81802022-08-10 02:08:47 -07001458
Devin Jeanpierre3eb5bbd2022-04-06 12:56:19 -07001459 let generic_params = format_generic_params(lifetimes);
Devin Jeanpierreb8ce2c12022-07-13 10:34:01 -07001460 let param_idents = out_param_ident.as_ref().into_iter().chain(param_idents);
Devin Jeanpierre11ce2b92022-07-27 07:54:21 -07001461 let param_types = out_param.into_iter().chain(param_types.map(|t| {
1462 if !t.is_unpin() {
1463 quote! {&mut #t}
1464 } else {
1465 quote! {#t}
1466 }
1467 }));
Devin Jeanpierre3eb5bbd2022-04-06 12:56:19 -07001468
1469 Ok(quote! {
1470 #thunk_attr
1471 pub(crate) fn #thunk_ident #generic_params( #( #param_idents: #param_types ),*
1472 ) #return_type_fragment ;
1473 })
Michael Forstered642022021-10-04 09:48:25 +00001474}
1475
Michael Forstercc5941a2021-10-07 07:12:24 +00001476fn generate_doc_comment(comment: &Option<String>) -> TokenStream {
1477 match comment {
Michael Forster028800b2021-10-05 12:39:59 +00001478 Some(text) => {
Marcel Hlopko89547752021-12-10 09:39:41 +00001479 // token_stream_printer (and rustfmt) don't put a space between /// and the doc
1480 // comment, let's add it here so our comments are pretty.
Lukasz Anforowiczdd9ae0f2022-02-17 15:52:53 +00001481 let doc = format!(" {}", text.replace('\n', "\n "));
Michael Forster028800b2021-10-05 12:39:59 +00001482 quote! {#[doc=#doc]}
1483 }
1484 None => quote! {},
Michael Forstercc5941a2021-10-07 07:12:24 +00001485 }
1486}
Lukasz Anforowiczdaff0402021-12-23 00:37:50 +00001487
Devin Jeanpierre92ca2612022-04-06 11:35:13 -07001488fn format_generic_params<T: ToTokens>(params: impl IntoIterator<Item = T>) -> TokenStream {
Lukasz Anforowiczdaff0402021-12-23 00:37:50 +00001489 let mut params = params.into_iter().peekable();
1490 if params.peek().is_none() {
1491 quote! {}
1492 } else {
1493 quote! { < #( #params ),* > }
1494 }
1495}
1496
Devin Jeanpierread125742022-04-11 13:50:28 -07001497/// Formats singletons as themselves, and collections of n!=1 items as a tuple.
1498///
1499/// In other words, this formats a collection of things as if via `#(#items),*`,
1500/// but without lint warnings.
1501///
1502/// For example:
1503///
1504/// * [] => ()
1505/// * [x] => x // equivalent to (x), but lint-free.
1506/// * [x, y] => (x, y)
1507fn format_tuple_except_singleton<T: ToTokens>(items: &[T]) -> TokenStream {
1508 match items {
1509 [singleton] => quote! {#singleton},
1510 items => quote! {(#(#items),*)},
1511 }
1512}
1513
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00001514fn should_implement_drop(record: &Record) -> bool {
Lukasz Anforowiczff7df4a2022-06-02 14:27:45 -07001515 match record.destructor {
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00001516 // TODO(b/202258760): Only omit destructor if `Copy` is specified.
Lukasz Anforowiczff7df4a2022-06-02 14:27:45 -07001517 SpecialMemberFunc::Trivial => false,
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00001518
1519 // TODO(b/212690698): Avoid calling into the C++ destructor (e.g. let
1520 // Rust drive `drop`-ing) to avoid (somewhat unergonomic) ManuallyDrop
1521 // if we can ask Rust to preserve C++ field destruction order in
1522 // NontrivialMembers case.
Lukasz Anforowiczff7df4a2022-06-02 14:27:45 -07001523 SpecialMemberFunc::NontrivialMembers => true,
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00001524
1525 // The `impl Drop` for NontrivialUserDefined needs to call into the
1526 // user-defined destructor on C++ side.
Lukasz Anforowiczff7df4a2022-06-02 14:27:45 -07001527 SpecialMemberFunc::NontrivialUserDefined => true,
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00001528
1529 // TODO(b/213516512): Today the IR doesn't contain Func entries for
1530 // deleted functions/destructors/etc. But, maybe we should generate
1531 // `impl Drop` in this case? With `unreachable!`? With
1532 // `std::mem::forget`?
Lukasz Anforowiczff7df4a2022-06-02 14:27:45 -07001533 SpecialMemberFunc::Unavailable => false,
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00001534 }
1535}
1536
1537/// Returns whether fields of type `ty` need to be wrapped in `ManuallyDrop<T>`
1538/// to prevent the fields from being destructed twice (once by the C++
1539/// destructor calkled from the `impl Drop` of the struct and once by `drop` on
1540/// the Rust side).
1541///
1542/// A type is safe to destroy twice if it implements `Copy`. Fields of such
1543/// don't need to be wrapped in `ManuallyDrop<T>` even if the struct
1544/// containing the fields provides an `impl Drop` that calles into a C++
1545/// destructor (in addition to dropping the fields on the Rust side).
1546///
1547/// Note that it is not enough to just be `!needs_drop<T>()`: Rust only
1548/// guarantees that it is safe to use-after-destroy for `Copy` types. See
1549/// e.g. the documentation for
1550/// [`drop_in_place`](https://doc.rust-lang.org/std/ptr/fn.drop_in_place.html):
1551///
1552/// > if `T` is not `Copy`, using the pointed-to value after calling
1553/// > `drop_in_place` can cause undefined behavior
Teddy Katzd2cd1422022-04-04 09:41:33 -07001554///
Devin Jeanpierrefec5bfe2022-04-05 10:59:10 -07001555/// For non-Copy union fields, failing to use `ManuallyDrop<T>` would
1556/// additionally cause a compile-time error until https://github.com/rust-lang/rust/issues/55149 is stabilized.
Devin Jeanpierre45b01962022-07-07 06:12:11 -07001557fn needs_manually_drop(db: &Database, ty: ir::RsType) -> Result<bool> {
Devin Jeanpierre409f6f62022-07-07 02:00:26 -07001558 let ty_implements_copy = db.rs_type_kind(ty)?.implements_copy();
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00001559 Ok(!ty_implements_copy)
1560}
1561
Lukasz Anforowicz92be29f2022-09-02 17:12:29 -07001562#[derive(Debug, PartialEq, Eq, Clone, Hash, PartialOrd, Ord)]
1563struct NamespaceQualifier(Vec<String>);
1564
1565impl NamespaceQualifier {
1566 fn new(item_id: ItemId, ir: &IR) -> Result<Self> {
1567 let mut namespaces = vec![];
1568 let item: &Item = ir.find_decl(item_id)?;
1569 let mut enclosing_namespace_id = item.enclosing_namespace_id();
1570 while let Some(parent_id) = enclosing_namespace_id {
1571 let namespace_item = ir.find_decl(parent_id)?;
1572 match namespace_item {
1573 Item::Namespace(ns) => {
1574 namespaces.push(ns.name.identifier.clone());
1575 enclosing_namespace_id = ns.enclosing_namespace_id;
1576 }
1577 _ => {
1578 bail!("Expected namespace");
1579 }
Rosica Dejanovska8283fe62022-05-09 10:04:28 -07001580 }
1581 }
Lukasz Anforowicz92be29f2022-09-02 17:12:29 -07001582 Ok(Self(namespaces.into_iter().rev().collect_vec()))
Rosica Dejanovska8283fe62022-05-09 10:04:28 -07001583 }
Rosica Dejanovska8283fe62022-05-09 10:04:28 -07001584
Lukasz Anforowicz92be29f2022-09-02 17:12:29 -07001585 fn format_for_rs(&self) -> TokenStream {
1586 let namespace_rs_idents = self.0.iter().map(|ns| make_rs_ident(ns));
1587 quote! { #(#namespace_rs_idents::)* }
1588 }
Lukasz Anforowicz4e2e0162022-09-01 07:07:32 -07001589
Lukasz Anforowicz92be29f2022-09-02 17:12:29 -07001590 fn format_for_cc(&self) -> TokenStream {
1591 let namespace_cc_idents = self.0.iter().map(|ns| format_cc_ident(ns));
1592 quote! { #(#namespace_cc_idents::)* }
1593 }
Lukasz Anforowicz4e2e0162022-09-01 07:07:32 -07001594}
1595
Devin Jeanpierrec0543eb2022-04-20 16:00:34 -07001596/// Generates Rust source code for a given incomplete record declaration.
1597fn generate_incomplete_record(incomplete_record: &IncompleteRecord) -> Result<TokenStream> {
Rosica Dejanovskae12d7172022-06-22 12:20:17 -07001598 let ident = make_rs_ident(&incomplete_record.rs_name);
1599 let name = &incomplete_record.rs_name;
Devin Jeanpierrec0543eb2022-04-20 16:00:34 -07001600 Ok(quote! {
1601 forward_declare::forward_declare!(
1602 pub #ident __SPACE__ = __SPACE__ forward_declare::symbol!(#name)
1603 );
1604 })
1605}
1606
Lukasz Anforowicz0dbcf0e2022-05-17 06:46:24 -07001607fn make_rs_field_ident(field: &Field, field_index: usize) -> Ident {
1608 match field.identifier.as_ref() {
1609 None => make_rs_ident(&format!("__unnamed_field{}", field_index)),
1610 Some(Identifier { identifier }) => make_rs_ident(identifier),
1611 }
1612}
1613
Lukasz Anforowiczfea0db92022-05-17 17:28:04 -07001614/// Gets the type of `field` for layout purposes.
1615///
1616/// Note that `get_field_rs_type_for_layout` may return Err (for
1617/// `is_no_unique_address` fields) even if `field.type_` is Ok.
1618fn get_field_rs_type_for_layout(field: &Field) -> Result<&RsType, &str> {
1619 // [[no_unique_address]] fields are replaced by a type-less, unaligned block of
1620 // memory which fills space up to the next field.
Lukasz Anforowicz5765bb82022-05-17 17:21:06 -07001621 // See: docs/struct_layout
Lukasz Anforowiczfea0db92022-05-17 17:28:04 -07001622 if field.is_no_unique_address {
1623 return Err("`[[no_unique_address]]` attribute was present.");
1624 }
1625
1626 field.type_.as_ref().map(|t| &t.rs_type).map_err(String::as_str)
Lukasz Anforowicz5765bb82022-05-17 17:21:06 -07001627}
1628
Michael Forster82c02d32022-05-20 21:47:33 -07001629/// Returns the type of a type-less, unaligned block of memory that can hold a
1630/// specified number of bits, rounded up to the next multiple of 8.
1631fn bit_padding(padding_size_in_bits: usize) -> TokenStream {
1632 let padding_size = Literal::usize_unsuffixed((padding_size_in_bits + 7) / 8);
Devin Jeanpierrecd5ebf32022-07-08 05:31:55 -07001633 quote! { [::std::mem::MaybeUninit<u8>; #padding_size] }
Michael Forster82c02d32022-05-20 21:47:33 -07001634}
1635
Michael Forsterbee84482021-10-13 08:35:38 +00001636/// Generates Rust source code for a given `Record` and associated assertions as
1637/// a tuple.
Devin Jeanpierre45b01962022-07-07 06:12:11 -07001638fn generate_record(db: &Database, record: &Rc<Record>) -> Result<GeneratedItem> {
Devin Jeanpierre52a14c32022-06-29 19:12:11 -07001639 let ir = db.ir();
Lukasz Anforowicz4c3a2cc2022-03-11 00:24:49 +00001640 let ident = make_rs_ident(&record.rs_name);
Lukasz Anforowicz92be29f2022-09-02 17:12:29 -07001641 let namespace_qualifier = NamespaceQualifier::new(record.id, &ir)?.format_for_rs();
Rosica Dejanovska8283fe62022-05-09 10:04:28 -07001642 let qualified_ident = {
Lukasz Anforowicz4e2e0162022-09-01 07:07:32 -07001643 quote! { crate:: #namespace_qualifier #ident }
Rosica Dejanovska8283fe62022-05-09 10:04:28 -07001644 };
Michael Forstercc5941a2021-10-07 07:12:24 +00001645 let doc_comment = generate_doc_comment(&record.doc_comment);
Lukasz Anforowiczfed64e62022-03-22 22:39:04 +00001646
1647 let mut field_copy_trait_assertions: Vec<TokenStream> = vec![];
Michael Forster82c02d32022-05-20 21:47:33 -07001648
1649 let fields_with_bounds = (record.fields.iter())
1650 .map(|field| {
1651 (
1652 // We don't represent bitfields directly in Rust. We drop the field itself here
1653 // and only retain the offset information. Adjacent bitfields then get merged in
1654 // the next step.
1655 if field.is_bitfield { None } else { Some(field) },
1656 field.offset,
1657 // We retain the end offset of fields only if we have a matching Rust type
1658 // to represent them. Otherwise we'll fill up all the space to the next field.
1659 // See: docs/struct_layout
Marcel Hlopkof05621b2022-05-25 00:26:06 -07001660 match get_field_rs_type_for_layout(field) {
1661 // Regular field
1662 Ok(_rs_type) => Some(field.offset + field.size),
1663 // Opaque field
Michael VanBemmel106f66c2022-07-13 09:48:48 -07001664 Err(_error) => {
1665 if record.is_union() {
1666 Some(field.size)
1667 } else {
1668 None
1669 }
1670 }
Marcel Hlopkof05621b2022-05-25 00:26:06 -07001671 },
Michael Forster82c02d32022-05-20 21:47:33 -07001672 vec![format!(
1673 "{} : {} bits",
1674 field.identifier.as_ref().map(|i| i.identifier.clone()).unwrap_or("".into()),
1675 field.size
1676 )],
1677 )
1678 })
1679 // Merge consecutive bitfields. This is necessary, because they may share storage in the
1680 // same byte.
1681 .coalesce(|first, second| match (first, second) {
1682 ((None, offset, _, desc1), (None, _, end, desc2)) => {
1683 Ok((None, offset, end, [desc1, desc2].concat()))
1684 }
1685 pair => Err(pair),
1686 });
1687
1688 // Pair up fields with the preceeding and following fields (if any):
1689 // - the end offset of the previous field determines if we need to insert
1690 // padding.
1691 // - the start offset of the next field may be need to grow the current field to
1692 // there.
1693 // This uses two separate `map` invocations on purpose to limit available state.
1694 let field_definitions = iter::once(None)
1695 .chain(fields_with_bounds.clone().map(Some))
1696 .chain(iter::once(None))
1697 .tuple_windows()
1698 .map(|(prev, cur, next)| {
1699 let (field, offset, end, desc) = cur.unwrap();
1700 let prev_end = prev.as_ref().map(|(_, _, e, _)| *e).flatten().unwrap_or(offset);
1701 let next_offset = next.map(|(_, o, _, _)| o);
Kinuko Yasuda54b75d72022-08-18 10:09:54 -07001702 let end = end.or(next_offset).unwrap_or(record.size * 8);
Michael Forster82c02d32022-05-20 21:47:33 -07001703
1704 if let Some((Some(prev_field), _, Some(prev_end), _)) = prev {
1705 assert!(
Lukasz Anforowiczd4742ff2022-07-11 17:05:02 -07001706 record.is_union() || prev_end <= offset,
Michael Forster82c02d32022-05-20 21:47:33 -07001707 "Unexpected offset+size for field {:?} in record {}",
1708 prev_field,
1709 record.cc_name
1710 );
1711 }
1712
1713 (field, prev_end, offset, end, desc)
1714 })
Devin Jeanpierreb69bcae2022-02-03 09:45:50 +00001715 .enumerate()
Michael Forster82c02d32022-05-20 21:47:33 -07001716 .map(|(field_index, (field, prev_end, offset, end, desc))| {
1717 // `is_opaque_blob` and bitfield representations are always
1718 // unaligned, even though the actual C++ field might be aligned.
1719 // To put the current field at the right offset, we might need to
1720 // insert some extra padding.
1721 //
1722 // No padding should be needed if the type of the current field is
1723 // known (i.e. if the current field is correctly aligned based on
1724 // its original type).
1725 //
1726 // We also don't need padding if we're in a union.
Lukasz Anforowiczd4742ff2022-07-11 17:05:02 -07001727 let padding_size_in_bits = if record.is_union()
Michael Forster82c02d32022-05-20 21:47:33 -07001728 || (field.is_some() && get_field_rs_type_for_layout(field.unwrap()).is_ok())
1729 {
1730 0
1731 } else {
1732 let padding_start = (prev_end + 7) / 8 * 8; // round up to byte boundary
1733 offset - padding_start
1734 };
1735
1736 let padding = if padding_size_in_bits == 0 {
1737 quote! {}
1738 } else {
1739 let padding_name = make_rs_ident(&format!("__padding{}", field_index));
1740 let padding_type = bit_padding(padding_size_in_bits);
1741 quote! { #padding_name: #padding_type, }
1742 };
1743
1744 // Bitfields get represented by private padding to ensure overall
1745 // struct layout is compatible.
1746 if field.is_none() {
1747 let name = make_rs_ident(&format!("__bitfields{}", field_index));
1748 let bitfield_padding = bit_padding(end - offset);
1749 return Ok(quote! {
1750 __NEWLINE__ #( __COMMENT__ #desc )*
1751 #padding #name: #bitfield_padding
1752 });
1753 }
1754 let field = field.unwrap();
1755
Lukasz Anforowicz0dbcf0e2022-05-17 06:46:24 -07001756 let ident = make_rs_field_ident(field, field_index);
Lukasz Anforowiczfea0db92022-05-17 17:28:04 -07001757 let doc_comment = match field.type_.as_ref() {
1758 Ok(_) => generate_doc_comment(&field.doc_comment),
1759 Err(msg) => {
1760 let supplemental_text =
1761 format!("Reason for representing this field as a blob of bytes:\n{}", msg);
1762 let new_text = match field.doc_comment.as_ref() {
1763 None => supplemental_text,
1764 Some(old_text) => format!("{}\n\n{}", old_text, supplemental_text),
1765 };
1766 generate_doc_comment(&Some(new_text))
1767 }
1768 };
1769 let access = if field.access == AccessSpecifier::Public
1770 && get_field_rs_type_for_layout(field).is_ok()
1771 {
Lukasz Anforowicz0dbcf0e2022-05-17 06:46:24 -07001772 quote! { pub }
1773 } else {
Rosica Dejanovska6676ad82022-06-07 14:28:59 -07001774 quote! { pub(crate) }
Lukasz Anforowicz0dbcf0e2022-05-17 06:46:24 -07001775 };
1776
Lukasz Anforowiczfea0db92022-05-17 17:28:04 -07001777 let field_type = match get_field_rs_type_for_layout(field) {
Michael Forster82c02d32022-05-20 21:47:33 -07001778 Err(_) => bit_padding(end - field.offset),
Lukasz Anforowiczfea0db92022-05-17 17:28:04 -07001779 Ok(rs_type) => {
Devin Jeanpierre409f6f62022-07-07 02:00:26 -07001780 let type_kind = db.rs_type_kind(rs_type.clone()).with_context(|| {
Lukasz Anforowicz0dbcf0e2022-05-17 06:46:24 -07001781 format!(
1782 "Failed to format type for field {:?} on record {:?}",
1783 field, record
1784 )
1785 })?;
Devin Jeanpierre409f6f62022-07-07 02:00:26 -07001786 let mut formatted = quote! {#type_kind};
Lukasz Anforowiczd4742ff2022-07-11 17:05:02 -07001787 if should_implement_drop(record) || record.is_union() {
Devin Jeanpierre409f6f62022-07-07 02:00:26 -07001788 if needs_manually_drop(db, rs_type.clone())? {
Lukasz Anforowiczfea0db92022-05-17 17:28:04 -07001789 // TODO(b/212690698): Avoid (somewhat unergonomic) ManuallyDrop
1790 // if we can ask Rust to preserve field destruction order if the
Lukasz Anforowiczff7df4a2022-06-02 14:27:45 -07001791 // destructor is the SpecialMemberFunc::NontrivialMembers
Lukasz Anforowiczfea0db92022-05-17 17:28:04 -07001792 // case.
Devin Jeanpierrecd5ebf32022-07-08 05:31:55 -07001793 formatted = quote! { ::std::mem::ManuallyDrop<#formatted> }
Lukasz Anforowiczfea0db92022-05-17 17:28:04 -07001794 } else {
1795 field_copy_trait_assertions.push(quote! {
1796 const _: () = {
1797 static_assertions::assert_impl_all!(#formatted: Copy);
1798 };
1799 });
1800 }
1801 };
1802 formatted
1803 }
Lukasz Anforowicz6d553632022-01-06 21:36:14 +00001804 };
Lukasz Anforowicz0dbcf0e2022-05-17 06:46:24 -07001805
Lukasz Anforowicz5765bb82022-05-17 17:21:06 -07001806 Ok(quote! { #padding #doc_comment #access #ident: #field_type })
Devin Jeanpierre7e9a1de2021-12-03 08:04:22 +00001807 })
Devin Jeanpierre09c6f452021-09-29 07:34:24 +00001808 .collect::<Result<Vec<_>>>()?;
Lukasz Anforowiczfed64e62022-03-22 22:39:04 +00001809
Kinuko Yasuda54b75d72022-08-18 10:09:54 -07001810 let size = Literal::usize_unsuffixed(record.size);
Lukasz Anforowiczdc37d6d2022-05-17 08:20:13 -07001811 let alignment = Literal::usize_unsuffixed(record.alignment);
Lukasz Anforowiczd4742ff2022-07-11 17:05:02 -07001812 let field_offset_assertions = if record.is_union() {
Marcel Hlopkofa9a3952022-05-10 01:34:13 -07001813 // TODO(https://github.com/Gilnaa/memoffset/issues/66): generate assertions for unions once
1814 // offsetof supports them.
1815 vec![]
1816 } else {
Michael Forster82c02d32022-05-20 21:47:33 -07001817 fields_with_bounds
Devin Jeanpierrea2be2a22022-05-18 18:59:05 -07001818 .enumerate()
Michael Forster82c02d32022-05-20 21:47:33 -07001819 .map(|(field_index, (field, _, _, _))| {
1820 if let Some(field) = field {
1821 let field_ident = make_rs_field_ident(field, field_index);
Lukasz Anforowicz30360ba2022-05-23 12:15:12 -07001822
1823 // The assertion below reinforces that the division by 8 on the next line is
1824 // justified (because the bitfields have been coallesced / filtered out
1825 // earlier).
1826 assert_eq!(field.offset % 8, 0);
1827 let expected_offset = Literal::usize_unsuffixed(field.offset / 8);
1828
Michael Forster82c02d32022-05-20 21:47:33 -07001829 let actual_offset_expr = quote! {
Lukasz Anforowicz30360ba2022-05-23 12:15:12 -07001830 memoffset_unstable_const::offset_of!(#qualified_ident, #field_ident)
Michael Forster82c02d32022-05-20 21:47:33 -07001831 };
1832 quote! {
1833 const _: () = assert!(#actual_offset_expr == #expected_offset);
1834 }
1835 } else {
1836 quote! {}
Devin Jeanpierrea2be2a22022-05-18 18:59:05 -07001837 }
1838 })
1839 .collect_vec()
Marcel Hlopkofa9a3952022-05-10 01:34:13 -07001840 };
Lukasz Anforowiczb4d17782022-07-07 08:09:13 -07001841 // TODO(b/212696226): Generate `assert_impl_all!` or `assert_not_impl_any!`
Lukasz Anforowiczfed64e62022-03-22 22:39:04 +00001842 // assertions about the `Copy` trait - this trait should be implemented
1843 // iff `should_implement_drop(record)` is false.
Michael Forsterdb8101a2021-10-08 06:56:03 +00001844 let mut record_features = BTreeSet::new();
1845 let mut assertion_features = BTreeSet::new();
Devin Jeanpierre273eeae2021-10-06 13:29:35 +00001846
1847 // TODO(mboehme): For the time being, we're using unstable features to
1848 // be able to use offset_of!() in static assertions. This is fine for a
1849 // prototype, but longer-term we want to either get those features
1850 // stabilized or find an alternative. For more details, see
1851 // b/200120034#comment15
Marcel Hlopkoeaae9b72022-01-21 15:54:11 +00001852 assertion_features.insert(make_rs_ident("const_ptr_offset_from"));
Devin Jeanpierre2ed14ec2021-10-06 11:32:19 +00001853
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00001854 let derives = generate_derives(record);
Devin Jeanpierre9227d2c2021-10-06 12:26:05 +00001855 let derives = if derives.is_empty() {
1856 quote! {}
1857 } else {
1858 quote! {#[derive( #(#derives),* )]}
1859 };
Lukasz Anforowiczd4742ff2022-07-11 17:05:02 -07001860 let record_kind = if record.is_union() {
Teddy Katzd2cd1422022-04-04 09:41:33 -07001861 quote! { union }
1862 } else {
1863 quote! { struct }
1864 };
Devin Jeanpierre190b90a2022-05-24 06:00:34 -07001865
Devin Jeanpierre215ee8e2022-05-16 16:09:41 -07001866 let recursively_pinned_attribute = if record.is_unpin() {
Lukasz Anforowiczdd9ae0f2022-02-17 15:52:53 +00001867 quote! {}
Devin Jeanpierreea700d32021-10-06 11:33:56 +00001868 } else {
Michael Forsterbee84482021-10-13 08:35:38 +00001869 // negative_impls are necessary for universal initialization due to Rust's
1870 // coherence rules: PhantomPinned isn't enough to prove to Rust that a
1871 // blanket impl that requires Unpin doesn't apply. See http://<internal link>=h.f6jp8ifzgt3n
Marcel Hlopkoeaae9b72022-01-21 15:54:11 +00001872 record_features.insert(make_rs_ident("negative_impls"));
Devin Jeanpierre215ee8e2022-05-16 16:09:41 -07001873 if should_implement_drop(record) {
Devin Jeanpierrecd5ebf32022-07-08 05:31:55 -07001874 quote! {#[::ctor::recursively_pinned(PinnedDrop)]}
Devin Jeanpierre215ee8e2022-05-16 16:09:41 -07001875 } else {
Devin Jeanpierrecd5ebf32022-07-08 05:31:55 -07001876 quote! {#[::ctor::recursively_pinned]}
Lukasz Anforowiczdd9ae0f2022-02-17 15:52:53 +00001877 }
1878 };
Devin Jeanpierre273eeae2021-10-06 13:29:35 +00001879
Devin Jeanpierrec80e6242022-02-03 01:56:40 +00001880 let mut repr_attributes = vec![quote! {C}];
1881 if record.override_alignment && record.alignment > 1 {
1882 let alignment = Literal::usize_unsuffixed(record.alignment);
1883 repr_attributes.push(quote! {align(#alignment)});
1884 }
1885
Devin Jeanpierre1221c2a2022-05-05 22:36:22 -07001886 // Adjust the struct to also include base class subobjects, vtables, etc.
1887 let head_padding = if let Some(first_field) = record.fields.first() {
1888 first_field.offset / 8
1889 } else {
Kinuko Yasuda54b75d72022-08-18 10:09:54 -07001890 record.size
Devin Jeanpierre1221c2a2022-05-05 22:36:22 -07001891 };
Devin Jeanpierrea2be2a22022-05-18 18:59:05 -07001892 // Prevent direct initialization for non-aggregate structs.
1893 //
1894 // Technically, any implicit-lifetime type is going to be fine to initialize
1895 // using direct initialization of the fields, even if it is not an aggregate,
1896 // because this is "just" setting memory to the appropriate values, and
1897 // implicit-lifetime types can automatically begin their lifetime without
1898 // running a constructor at all.
1899 //
1900 // However, not all types used in interop are implicit-lifetime. For example,
1901 // while any `Unpin` C++ value is, some `!Unpin` structs (e.g. `std::list`)
1902 // will not be. So for consistency, we apply the same rule for both
1903 // implicit-lifetime and non-implicit-lifetime types: the C++ rule, that the
1904 // type must be an *aggregate* type.
1905 //
1906 // TODO(b/232969667): Protect unions from direct initialization, too.
Lukasz Anforowiczd4742ff2022-07-11 17:05:02 -07001907 let allow_direct_init = record.is_aggregate || record.is_union();
Devin Jeanpierrea2be2a22022-05-18 18:59:05 -07001908 let head_padding = if head_padding > 0 || !allow_direct_init {
Devin Jeanpierre1221c2a2022-05-05 22:36:22 -07001909 let n = proc_macro2::Literal::usize_unsuffixed(head_padding);
Devin Jeanpierrec80e6242022-02-03 01:56:40 +00001910 quote! {
Devin Jeanpierrecd5ebf32022-07-08 05:31:55 -07001911 __non_field_data: [::std::mem::MaybeUninit<u8>; #n],
Devin Jeanpierrec80e6242022-02-03 01:56:40 +00001912 }
1913 } else {
1914 quote! {}
1915 };
1916
Devin Jeanpierre27450132022-04-11 13:52:01 -07001917 // TODO(b/227442773): After namespace support is added, use the fully-namespaced
1918 // name.
1919 let incomplete_symbol = &record.cc_name;
1920 let incomplete_definition = quote! {
Rosica Dejanovska8283fe62022-05-09 10:04:28 -07001921 forward_declare::unsafe_define!(forward_declare::symbol!(#incomplete_symbol), #qualified_ident);
Devin Jeanpierre27450132022-04-11 13:52:01 -07001922 };
1923
Devin Jeanpierre409f6f62022-07-07 02:00:26 -07001924 let no_unique_address_accessors = cc_struct_no_unique_address_impl(db, record)?;
Devin Jeanpierre8763a8e2022-04-26 15:45:13 -07001925 let mut record_generated_items = record
Rosica Dejanovskab2bd59e2022-04-11 09:02:03 -07001926 .child_item_ids
1927 .iter()
1928 .map(|id| {
Lukasz Anforowicz42ab93b2022-08-31 11:17:59 -07001929 let item = ir
1930 .find_decl(*id)
1931 .with_context(|| format!("Failed to look up `record.child_item_ids` for {:?}", record))?;
Devin Jeanpierreab85d442022-06-29 19:16:41 -07001932 generate_item(db, item)
Rosica Dejanovskab2bd59e2022-04-11 09:02:03 -07001933 })
1934 .collect::<Result<Vec<_>>>()?;
1935
Devin Jeanpierre52a14c32022-06-29 19:12:11 -07001936 record_generated_items.push(cc_struct_upcast_impl(record, &ir)?);
Devin Jeanpierre8763a8e2022-04-26 15:45:13 -07001937
Rosica Dejanovskab2bd59e2022-04-11 09:02:03 -07001938 let mut items = vec![];
1939 let mut thunks_from_record_items = vec![];
Devin Jeanpierre4f06f832022-04-26 15:51:30 -07001940 let mut thunk_impls_from_record_items = vec![];
Rosica Dejanovskab2bd59e2022-04-11 09:02:03 -07001941 let mut assertions_from_record_items = vec![];
1942
Devin Jeanpierre4f06f832022-04-26 15:51:30 -07001943 for generated in record_generated_items {
1944 items.push(generated.item);
Rosica Dejanovskab2bd59e2022-04-11 09:02:03 -07001945 if !generated.thunks.is_empty() {
Devin Jeanpierre4f06f832022-04-26 15:51:30 -07001946 thunks_from_record_items.push(generated.thunks);
Rosica Dejanovskab2bd59e2022-04-11 09:02:03 -07001947 }
1948 if !generated.assertions.is_empty() {
Devin Jeanpierre4f06f832022-04-26 15:51:30 -07001949 assertions_from_record_items.push(generated.assertions);
1950 }
1951 if !generated.thunk_impls.is_empty() {
1952 thunk_impls_from_record_items.push(generated.thunk_impls);
Rosica Dejanovskab2bd59e2022-04-11 09:02:03 -07001953 }
1954 record_features.extend(generated.features.clone());
1955 }
1956
Michael Forsterdb8101a2021-10-08 06:56:03 +00001957 let record_tokens = quote! {
Michael Forster028800b2021-10-05 12:39:59 +00001958 #doc_comment
Devin Jeanpierre9227d2c2021-10-06 12:26:05 +00001959 #derives
Devin Jeanpierre215ee8e2022-05-16 16:09:41 -07001960 #recursively_pinned_attribute
Devin Jeanpierrec80e6242022-02-03 01:56:40 +00001961 #[repr(#( #repr_attributes ),*)]
Teddy Katzd2cd1422022-04-04 09:41:33 -07001962 pub #record_kind #ident {
Devin Jeanpierre1221c2a2022-05-05 22:36:22 -07001963 #head_padding
Lukasz Anforowicz0dbcf0e2022-05-17 06:46:24 -07001964 #( #field_definitions, )*
Marcel Hlopkob4b28742021-09-15 12:45:20 +00001965 }
Googlerec648ff2021-09-23 07:19:53 +00001966
Devin Jeanpierre27450132022-04-11 13:52:01 -07001967 #incomplete_definition
1968
Devin Jeanpierre58181ac2022-02-14 21:30:05 +00001969 #no_unique_address_accessors
1970
Rosica Dejanovskab2bd59e2022-04-11 09:02:03 -07001971 __NEWLINE__ __NEWLINE__
1972 #( #items __NEWLINE__ __NEWLINE__)*
Devin Jeanpierre273eeae2021-10-06 13:29:35 +00001973 };
1974
Lukasz Anforowicz95938f82022-03-24 13:51:54 +00001975 let record_trait_assertions = {
Devin Jeanpierre52a14c32022-06-29 19:12:11 -07001976 let record_type_name = RsTypeKind::new_record(record.clone(), &ir)?.to_token_stream();
Lukasz Anforowicz95938f82022-03-24 13:51:54 +00001977 let mut assertions: Vec<TokenStream> = vec![];
1978 let mut add_assertion = |assert_impl_macro: TokenStream, trait_name: TokenStream| {
1979 assertions.push(quote! {
Lukasz Anforowicz768bba32022-04-11 14:06:13 -07001980 const _: () = { static_assertions::#assert_impl_macro (#record_type_name: #trait_name); };
Lukasz Anforowicz95938f82022-03-24 13:51:54 +00001981 });
1982 };
1983 if should_derive_clone(record) {
1984 add_assertion(quote! { assert_impl_all! }, quote! { Clone });
1985 } else {
Lukasz Anforowiczb4d17782022-07-07 08:09:13 -07001986 // Can't `assert_not_impl_any!` here, because `Clone` may be
Lukasz Anforowicz95938f82022-03-24 13:51:54 +00001987 // implemented rather than derived.
1988 }
1989 let mut add_conditional_assertion = |should_impl_trait: bool, trait_name: TokenStream| {
1990 let assert_impl_macro = if should_impl_trait {
1991 quote! { assert_impl_all! }
1992 } else {
Lukasz Anforowiczb4d17782022-07-07 08:09:13 -07001993 quote! { assert_not_impl_any! }
Lukasz Anforowicz95938f82022-03-24 13:51:54 +00001994 };
1995 add_assertion(assert_impl_macro, trait_name);
1996 };
1997 add_conditional_assertion(should_derive_copy(record), quote! { Copy });
1998 add_conditional_assertion(should_implement_drop(record), quote! { Drop });
1999 assertions
2000 };
Michael Forsterdb8101a2021-10-08 06:56:03 +00002001 let assertion_tokens = quote! {
Devin Jeanpierrecd5ebf32022-07-08 05:31:55 -07002002 const _: () = assert!(::std::mem::size_of::<#qualified_ident>() == #size);
2003 const _: () = assert!(::std::mem::align_of::<#qualified_ident>() == #alignment);
Lukasz Anforowicz95938f82022-03-24 13:51:54 +00002004 #( #record_trait_assertions )*
Lukasz Anforowiczfed64e62022-03-22 22:39:04 +00002005 #( #field_offset_assertions )*
2006 #( #field_copy_trait_assertions )*
Rosica Dejanovskab2bd59e2022-04-11 09:02:03 -07002007 #( #assertions_from_record_items )*
Michael Forsterdb8101a2021-10-08 06:56:03 +00002008 };
2009
Rosica Dejanovskab2bd59e2022-04-11 09:02:03 -07002010 let thunk_tokens = quote! {
2011 #( #thunks_from_record_items )*
2012 };
2013
2014 Ok(GeneratedItem {
2015 item: record_tokens,
2016 features: record_features.union(&assertion_features).cloned().collect(),
2017 assertions: assertion_tokens,
2018 thunks: thunk_tokens,
Devin Jeanpierre4f06f832022-04-26 15:51:30 -07002019 thunk_impls: quote! {#(#thunk_impls_from_record_items __NEWLINE__ __NEWLINE__)*},
Rosica Dejanovskab2bd59e2022-04-11 09:02:03 -07002020 has_record: true,
2021 })
Marcel Hlopkob4b28742021-09-15 12:45:20 +00002022}
2023
Devin Jeanpierrea99e0a32022-08-17 01:36:58 -07002024fn check_by_value(record: &Record) -> Result<()> {
2025 if record.destructor == SpecialMemberFunc::Unavailable {
2026 bail!(
2027 "Can't directly construct values of type `{}` as it has a non-public or deleted destructor",
2028 record.cc_name
2029 )
2030 }
Devin Jeanpierreccb67672022-08-17 10:05:47 -07002031 if record.is_abstract {
2032 bail!("Can't directly construct values of type `{}`: it is abstract", record.cc_name);
2033 }
Devin Jeanpierrea99e0a32022-08-17 01:36:58 -07002034 Ok(())
2035}
2036
Lukasz Anforowicz2e41bb62022-01-11 18:23:07 +00002037fn should_derive_clone(record: &Record) -> bool {
Lukasz Anforowiczd4742ff2022-07-11 17:05:02 -07002038 if record.is_union() {
Lukasz Anforowicz2469a5e2022-06-02 09:44:51 -07002039 // `union`s (unlike `struct`s) should only derive `Clone` if they are `Copy`.
2040 should_derive_copy(record)
2041 } else {
Devin Jeanpierrea99e0a32022-08-17 01:36:58 -07002042 record.is_unpin()
2043 && record.copy_constructor == SpecialMemberFunc::Trivial
2044 && check_by_value(record).is_ok()
Lukasz Anforowicz2469a5e2022-06-02 09:44:51 -07002045 }
Lukasz Anforowicz2e41bb62022-01-11 18:23:07 +00002046}
2047
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00002048fn should_derive_copy(record: &Record) -> bool {
2049 // TODO(b/202258760): Make `Copy` inclusion configurable.
Lukasz Anforowicz2469a5e2022-06-02 09:44:51 -07002050 record.is_unpin()
Lukasz Anforowiczff7df4a2022-06-02 14:27:45 -07002051 && record.copy_constructor == SpecialMemberFunc::Trivial
2052 && record.destructor == ir::SpecialMemberFunc::Trivial
Devin Jeanpierrea99e0a32022-08-17 01:36:58 -07002053 && check_by_value(record).is_ok()
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00002054}
2055
2056fn generate_derives(record: &Record) -> Vec<Ident> {
2057 let mut derives = vec![];
Lukasz Anforowicz2e41bb62022-01-11 18:23:07 +00002058 if should_derive_clone(record) {
Marcel Hlopkoeaae9b72022-01-21 15:54:11 +00002059 derives.push(make_rs_ident("Clone"));
Devin Jeanpierre2ed14ec2021-10-06 11:32:19 +00002060 }
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00002061 if should_derive_copy(record) {
Marcel Hlopkoeaae9b72022-01-21 15:54:11 +00002062 derives.push(make_rs_ident("Copy"));
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00002063 }
2064 derives
Devin Jeanpierre2ed14ec2021-10-06 11:32:19 +00002065}
2066
Devin Jeanpierre45b01962022-07-07 06:12:11 -07002067fn generate_enum(db: &Database, enum_: &Enum) -> Result<TokenStream> {
Teddy Katz76fa42b2022-02-23 01:22:56 +00002068 let name = make_rs_ident(&enum_.identifier.identifier);
Devin Jeanpierre409f6f62022-07-07 02:00:26 -07002069 let underlying_type = db.rs_type_kind(enum_.underlying_type.rs_type.clone())?;
Teddy Katz76fa42b2022-02-23 01:22:56 +00002070 let enumerator_names =
2071 enum_.enumerators.iter().map(|enumerator| make_rs_ident(&enumerator.identifier.identifier));
2072 let enumerator_values = enum_.enumerators.iter().map(|enumerator| enumerator.value);
2073 Ok(quote! {
2074 #[repr(transparent)]
2075 #[derive(Debug, PartialEq, Eq, Copy, Clone, Hash, PartialOrd, Ord)]
2076 pub struct #name(#underlying_type);
2077 impl #name {
2078 #(pub const #enumerator_names: #name = #name(#enumerator_values);)*
2079 }
2080 impl From<#underlying_type> for #name {
2081 fn from(value: #underlying_type) -> #name {
Marcel Hlopko872b41a2022-05-10 01:49:33 -07002082 #name(value)
Teddy Katz76fa42b2022-02-23 01:22:56 +00002083 }
2084 }
2085 impl From<#name> for #underlying_type {
2086 fn from(value: #name) -> #underlying_type {
Marcel Hlopko872b41a2022-05-10 01:49:33 -07002087 value.0
Teddy Katz76fa42b2022-02-23 01:22:56 +00002088 }
2089 }
2090 })
2091}
2092
Devin Jeanpierre45b01962022-07-07 06:12:11 -07002093fn generate_type_alias(db: &Database, type_alias: &TypeAlias) -> Result<TokenStream> {
Marcel Hlopkoeaae9b72022-01-21 15:54:11 +00002094 let ident = make_rs_ident(&type_alias.identifier.identifier);
Lukasz Anforowiczc503af42022-03-04 23:18:15 +00002095 let doc_comment = generate_doc_comment(&type_alias.doc_comment);
Devin Jeanpierre409f6f62022-07-07 02:00:26 -07002096 let underlying_type = db
2097 .rs_type_kind(type_alias.underlying_type.rs_type.clone())
Googler6a0a5252022-01-11 14:08:09 +00002098 .with_context(|| format!("Failed to format underlying type for {:?}", type_alias))?;
Lukasz Anforowiczc503af42022-03-04 23:18:15 +00002099 Ok(quote! {
2100 #doc_comment
2101 pub type #ident = #underlying_type;
2102 })
Googler6a0a5252022-01-11 14:08:09 +00002103}
2104
Michael Forster523dbd42021-10-12 11:05:44 +00002105/// Generates Rust source code for a given `UnsupportedItem`.
2106fn generate_unsupported(item: &UnsupportedItem) -> Result<TokenStream> {
Googler48a74dd2021-10-25 07:31:53 +00002107 let location = if item.source_loc.filename.is_empty() {
2108 "<unknown location>".to_string()
2109 } else {
2110 // TODO(forster): The "google3" prefix should probably come from a command line
2111 // argument.
2112 // TODO(forster): Consider linking to the symbol instead of to the line number
2113 // to avoid wrong links while generated files have not caught up.
2114 format!("google3/{};l={}", &item.source_loc.filename, &item.source_loc.line)
2115 };
Michael Forster6a184ad2021-10-12 13:04:05 +00002116 let message = format!(
Googler48a74dd2021-10-25 07:31:53 +00002117 "{}\nError while generating bindings for item '{}':\n{}",
2118 &location, &item.name, &item.message
Michael Forster6a184ad2021-10-12 13:04:05 +00002119 );
Michael Forster523dbd42021-10-12 11:05:44 +00002120 Ok(quote! { __COMMENT__ #message })
2121}
2122
Michael Forsterf1dce422021-10-13 09:50:16 +00002123/// Generates Rust source code for a given `Comment`.
2124fn generate_comment(comment: &Comment) -> Result<TokenStream> {
2125 let text = &comment.text;
2126 Ok(quote! { __COMMENT__ #text })
2127}
2128
Devin Jeanpierre45b01962022-07-07 06:12:11 -07002129fn generate_namespace(db: &Database, namespace: &Namespace) -> Result<GeneratedItem> {
Devin Jeanpierre52a14c32022-06-29 19:12:11 -07002130 let ir = db.ir();
Rosica Dejanovskadd9a9032022-04-12 07:34:41 -07002131 let mut items = vec![];
2132 let mut thunks = vec![];
Lukasz Anforowicz90bb7462022-09-01 08:13:28 -07002133 let mut thunk_impls = vec![];
Rosica Dejanovskadd9a9032022-04-12 07:34:41 -07002134 let mut assertions = vec![];
2135 let mut has_record = false;
2136 let mut features = BTreeSet::new();
2137
2138 for item_id in namespace.child_item_ids.iter() {
Lukasz Anforowicz42ab93b2022-08-31 11:17:59 -07002139 let item = ir
2140 .find_decl(*item_id)
2141 .with_context(|| format!("Failed to look up namespace.child_item_ids for {:?}", namespace))?;
Devin Jeanpierreab85d442022-06-29 19:16:41 -07002142 let generated = generate_item(db, item)?;
Rosica Dejanovskadd9a9032022-04-12 07:34:41 -07002143 items.push(generated.item);
2144 if !generated.thunks.is_empty() {
2145 thunks.push(generated.thunks);
2146 }
Lukasz Anforowicz90bb7462022-09-01 08:13:28 -07002147 if !generated.thunk_impls.is_empty() {
2148 thunk_impls.push(generated.thunk_impls);
2149 }
Rosica Dejanovskadd9a9032022-04-12 07:34:41 -07002150 if !generated.assertions.is_empty() {
2151 assertions.push(generated.assertions);
2152 }
2153 features.extend(generated.features);
2154 has_record = has_record || generated.has_record;
2155 }
2156
Rosica Dejanovska93aeafb2022-06-01 07:05:31 -07002157 let reopened_namespace_idx = ir.get_reopened_namespace_idx(namespace.id)?;
2158 let should_skip_index =
2159 ir.is_last_reopened_namespace(namespace.id, namespace.canonical_namespace_id)?;
2160
2161 let name = if should_skip_index {
2162 make_rs_ident(&namespace.name.identifier)
2163 } else {
2164 make_rs_ident(&format!("{}_{}", &namespace.name.identifier, reopened_namespace_idx))
2165 };
2166
2167 let use_stmt_for_previous_namespace = if reopened_namespace_idx == 0 {
2168 quote! {}
2169 } else {
2170 let previous_namespace_ident = make_rs_ident(&format!(
2171 "{}_{}",
2172 &namespace.name.identifier,
2173 reopened_namespace_idx - 1
2174 ));
2175 quote! { pub use super::#previous_namespace_ident::*; __NEWLINE__ __NEWLINE__ }
2176 };
Rosica Dejanovskadd9a9032022-04-12 07:34:41 -07002177
Rosica Dejanovskadd9a9032022-04-12 07:34:41 -07002178 let namespace_tokens = quote! {
2179 pub mod #name {
Rosica Dejanovska93aeafb2022-06-01 07:05:31 -07002180 #use_stmt_for_previous_namespace
2181
Rosica Dejanovskadd9a9032022-04-12 07:34:41 -07002182 #( #items __NEWLINE__ __NEWLINE__ )*
Rosica Dejanovskadd9a9032022-04-12 07:34:41 -07002183 }
2184 };
2185
2186 Ok(GeneratedItem {
2187 item: namespace_tokens,
2188 features: features,
2189 has_record: has_record,
Lukasz Anforowicz90bb7462022-09-01 08:13:28 -07002190 thunks: quote! { #( #thunks )* },
2191 thunk_impls: quote! { #( #thunk_impls )* },
2192 assertions: quote! { #( #assertions )* },
Rosica Dejanovskadd9a9032022-04-12 07:34:41 -07002193 ..Default::default()
2194 })
2195}
2196
Rosica Dejanovska8da8f792022-03-29 02:02:22 -07002197#[derive(Clone, Debug, Default)]
2198struct GeneratedItem {
2199 item: TokenStream,
2200 thunks: TokenStream,
Devin Jeanpierre4f06f832022-04-26 15:51:30 -07002201 // C++ source code for helper functions.
2202 thunk_impls: TokenStream,
Rosica Dejanovska8da8f792022-03-29 02:02:22 -07002203 assertions: TokenStream,
2204 features: BTreeSet<Ident>,
2205 has_record: bool,
2206}
2207
Devin Jeanpierre45b01962022-07-07 06:12:11 -07002208fn generate_item(db: &Database, item: &Item) -> Result<GeneratedItem> {
Devin Jeanpierre52a14c32022-06-29 19:12:11 -07002209 let ir = db.ir();
Devin Jeanpierreab85d442022-06-29 19:16:41 -07002210 let overloaded_funcs = db.overloaded_funcs();
Rosica Dejanovska8da8f792022-03-29 02:02:22 -07002211 let generated_item = match item {
Devin Jeanpierre52a14c32022-06-29 19:12:11 -07002212 Item::Func(func) => match db.generate_func(func.clone()) {
Devin Jeanpierred7b48102022-03-31 04:15:03 -07002213 Err(e) => GeneratedItem {
Devin Jeanpierre52a14c32022-06-29 19:12:11 -07002214 item: generate_unsupported(&make_unsupported_fn(func, &ir, format!("{e}"))?)?,
Devin Jeanpierred7b48102022-03-31 04:15:03 -07002215 ..Default::default()
2216 },
2217 Ok(None) => GeneratedItem::default(),
Devin Jeanpierre52a14c32022-06-29 19:12:11 -07002218 Ok(Some(f)) => {
Devin Jeanpierrece549202022-07-25 08:54:34 -07002219 let (api_func, thunk, function_id) = &*f;
Devin Jeanpierre52a14c32022-06-29 19:12:11 -07002220 if overloaded_funcs.contains(function_id) {
Rosica Dejanovska8da8f792022-03-29 02:02:22 -07002221 GeneratedItem {
2222 item: generate_unsupported(&make_unsupported_fn(
2223 func,
Devin Jeanpierre52a14c32022-06-29 19:12:11 -07002224 &ir,
Rosica Dejanovska8da8f792022-03-29 02:02:22 -07002225 "Cannot generate bindings for overloaded function",
2226 )?)?,
2227 ..Default::default()
2228 }
2229 } else {
Devin Jeanpierre52a14c32022-06-29 19:12:11 -07002230 // TODO(b/236687702): Use Rc for these, or else split this into a non-query
2231 // and only use the query for Function IDs.
Rosica Dejanovska8da8f792022-03-29 02:02:22 -07002232 GeneratedItem {
Devin Jeanpierre52a14c32022-06-29 19:12:11 -07002233 item: api_func.tokens.clone(),
2234 thunks: thunk.tokens.clone(),
Rosica Dejanovska8da8f792022-03-29 02:02:22 -07002235 features: api_func.features.union(&thunk.features).cloned().collect(),
2236 ..Default::default()
2237 }
2238 }
2239 }
2240 },
Devin Jeanpierrec0543eb2022-04-20 16:00:34 -07002241 Item::IncompleteRecord(incomplete_record) => {
2242 if !ir.is_current_target(&incomplete_record.owning_target)
2243 && !ir.is_stdlib_target(&incomplete_record.owning_target)
2244 {
Devin Jeanpierre090a9872022-04-26 15:19:46 -07002245 GeneratedItem::default()
Devin Jeanpierrec0543eb2022-04-20 16:00:34 -07002246 } else {
2247 GeneratedItem {
2248 item: generate_incomplete_record(incomplete_record)?,
2249 ..Default::default()
2250 }
2251 }
2252 }
Rosica Dejanovska8da8f792022-03-29 02:02:22 -07002253 Item::Record(record) => {
2254 if !ir.is_current_target(&record.owning_target)
2255 && !ir.is_stdlib_target(&record.owning_target)
2256 {
Devin Jeanpierre090a9872022-04-26 15:19:46 -07002257 GeneratedItem::default()
Rosica Dejanovska8da8f792022-03-29 02:02:22 -07002258 } else {
Devin Jeanpierreab85d442022-06-29 19:16:41 -07002259 generate_record(db, record)?
Rosica Dejanovska8da8f792022-03-29 02:02:22 -07002260 }
2261 }
2262 Item::Enum(enum_) => {
2263 if !ir.is_current_target(&enum_.owning_target)
2264 && !ir.is_stdlib_target(&enum_.owning_target)
2265 {
Devin Jeanpierre090a9872022-04-26 15:19:46 -07002266 GeneratedItem::default()
Rosica Dejanovska8da8f792022-03-29 02:02:22 -07002267 } else {
Devin Jeanpierre409f6f62022-07-07 02:00:26 -07002268 GeneratedItem { item: generate_enum(db, enum_)?, ..Default::default() }
Rosica Dejanovska8da8f792022-03-29 02:02:22 -07002269 }
2270 }
2271 Item::TypeAlias(type_alias) => {
2272 if !ir.is_current_target(&type_alias.owning_target)
2273 && !ir.is_stdlib_target(&type_alias.owning_target)
2274 {
Devin Jeanpierre090a9872022-04-26 15:19:46 -07002275 GeneratedItem::default()
Rosica Dejanovska8da8f792022-03-29 02:02:22 -07002276 } else {
Devin Jeanpierre409f6f62022-07-07 02:00:26 -07002277 GeneratedItem { item: generate_type_alias(db, type_alias)?, ..Default::default() }
Rosica Dejanovska8da8f792022-03-29 02:02:22 -07002278 }
2279 }
2280 Item::UnsupportedItem(unsupported) => {
2281 GeneratedItem { item: generate_unsupported(unsupported)?, ..Default::default() }
2282 }
2283 Item::Comment(comment) => {
2284 GeneratedItem { item: generate_comment(comment)?, ..Default::default() }
2285 }
Devin Jeanpierreab85d442022-06-29 19:16:41 -07002286 Item::Namespace(namespace) => generate_namespace(db, namespace)?,
Rosica Dejanovska8da8f792022-03-29 02:02:22 -07002287 };
2288
2289 Ok(generated_item)
2290}
2291
Devin Jeanpierreab85d442022-06-29 19:16:41 -07002292/// Identifies all functions having overloads that we can't import (yet).
2293///
2294/// TODO(b/213280424): Implement support for overloaded functions.
2295fn overloaded_funcs(db: &dyn BindingsGenerator) -> Rc<HashSet<Rc<FunctionId>>> {
2296 let mut seen_funcs = HashSet::new();
2297 let mut overloaded_funcs = HashSet::new();
2298 for func in db.ir().functions() {
2299 if let Ok(Some(f)) = db.generate_func(func.clone()) {
Devin Jeanpierrece549202022-07-25 08:54:34 -07002300 let (.., function_id) = &*f;
Devin Jeanpierreab85d442022-06-29 19:16:41 -07002301 if !seen_funcs.insert(function_id.clone()) {
2302 overloaded_funcs.insert(function_id.clone());
2303 }
2304 }
2305 }
2306 Rc::new(overloaded_funcs)
2307}
2308
Devin Jeanpierre4f06f832022-04-26 15:51:30 -07002309// Returns the Rust code implementing bindings, plus any auxiliary C++ code
2310// needed to support it.
Devin Jeanpierree9850a72022-06-29 19:04:48 -07002311fn generate_bindings_tokens(ir: Rc<IR>, crubit_support_path: &str) -> Result<BindingsTokens> {
Devin Jeanpierre52a14c32022-06-29 19:12:11 -07002312 let mut db = Database::default();
2313 db.set_ir(ir.clone());
2314
Michael Forstered642022021-10-04 09:48:25 +00002315 let mut items = vec![];
Marcel Hlopko42abfc82021-08-09 07:03:17 +00002316 let mut thunks = vec![];
Devin Jeanpierreb8ce2c12022-07-13 10:34:01 -07002317 let mut thunk_impls = vec![generate_rs_api_impl(&mut db, crubit_support_path)?];
Michael Forsterdb8101a2021-10-08 06:56:03 +00002318 let mut assertions = vec![];
Marcel Hlopko42abfc82021-08-09 07:03:17 +00002319
Googler454f2652021-12-06 12:53:12 +00002320 // We import nullable pointers as an Option<&T> and assume that at the ABI
2321 // level, None is represented as a zero pointer value whereas Some is
2322 // represented as as non-zero pointer value. This seems like a pretty safe
2323 // assumption to make, but to provide some safeguard, assert that
2324 // `Option<&i32>` and `&i32` have the same size.
2325 assertions.push(quote! {
Devin Jeanpierrecd5ebf32022-07-08 05:31:55 -07002326 const _: () = assert!(::std::mem::size_of::<Option<&i32>>() == ::std::mem::size_of::<&i32>());
Googler454f2652021-12-06 12:53:12 +00002327 });
2328
Michael Forsterbee84482021-10-13 08:35:38 +00002329 // TODO(jeanpierreda): Delete has_record, either in favor of using RsSnippet, or not
2330 // having uses. See https://chat.google.com/room/AAAAnQmj8Qs/6QbkSvWcfhA
Devin Jeanpierreea700d32021-10-06 11:33:56 +00002331 let mut has_record = false;
Devin Jeanpierre273eeae2021-10-06 13:29:35 +00002332 let mut features = BTreeSet::new();
Marcel Hlopko42abfc82021-08-09 07:03:17 +00002333
Lukasz Anforowicz72c4d222022-02-18 19:07:28 +00002334 // For #![rustfmt::skip].
2335 features.insert(make_rs_ident("custom_inner_attributes"));
2336
Rosica Dejanovskab2bd59e2022-04-11 09:02:03 -07002337 for top_level_item_id in ir.top_level_item_ids() {
Lukasz Anforowicz42ab93b2022-08-31 11:17:59 -07002338 let item = ir
2339 .find_decl(*top_level_item_id)
2340 .context("Failed to look up ir.top_level_item_ids")?;
Devin Jeanpierre45b01962022-07-07 06:12:11 -07002341 let generated = generate_item(&db, item)?;
Rosica Dejanovska8da8f792022-03-29 02:02:22 -07002342 items.push(generated.item);
Devin Jeanpierreb5ba1402022-03-29 07:29:24 -07002343 if !generated.thunks.is_empty() {
Rosica Dejanovska8da8f792022-03-29 02:02:22 -07002344 thunks.push(generated.thunks);
Michael Forstered642022021-10-04 09:48:25 +00002345 }
Devin Jeanpierreb5ba1402022-03-29 07:29:24 -07002346 if !generated.assertions.is_empty() {
Rosica Dejanovska8da8f792022-03-29 02:02:22 -07002347 assertions.push(generated.assertions);
2348 }
Devin Jeanpierre4f06f832022-04-26 15:51:30 -07002349 if !generated.thunk_impls.is_empty() {
2350 thunk_impls.push(generated.thunk_impls);
2351 }
Rosica Dejanovska8da8f792022-03-29 02:02:22 -07002352 features.extend(generated.features);
2353 has_record = has_record || generated.has_record;
Marcel Hlopko42abfc82021-08-09 07:03:17 +00002354 }
2355
Marcel Hlopkob4b28742021-09-15 12:45:20 +00002356 let mod_detail = if thunks.is_empty() {
2357 quote! {}
2358 } else {
2359 quote! {
2360 mod detail {
Googler55647142022-01-11 12:37:39 +00002361 #[allow(unused_imports)]
Devin Jeanpierred4dde0e2021-10-13 20:48:25 +00002362 use super::*;
Marcel Hlopkob4b28742021-09-15 12:45:20 +00002363 extern "C" {
2364 #( #thunks )*
2365 }
Marcel Hlopko42abfc82021-08-09 07:03:17 +00002366 }
2367 }
2368 };
2369
Devin Jeanpierre273eeae2021-10-06 13:29:35 +00002370 let features = if features.is_empty() {
2371 quote! {}
2372 } else {
2373 quote! {
2374 #![feature( #(#features),* )]
2375 }
2376 };
2377
Devin Jeanpierre4f06f832022-04-26 15:51:30 -07002378 Ok(BindingsTokens {
2379 rs_api: quote! {
2380 #features __NEWLINE__
2381 #![allow(non_camel_case_types)] __NEWLINE__
Lukasz Anforowicz945919c2022-05-12 13:08:47 -07002382 #![allow(non_snake_case)] __NEWLINE__
Lukasz Anforowiczcc262632022-05-12 15:07:43 -07002383 #![allow(non_upper_case_globals)] __NEWLINE__
2384 #![deny(warnings)] __NEWLINE__ __NEWLINE__
Googler55647142022-01-11 12:37:39 +00002385
Devin Jeanpierre4f06f832022-04-26 15:51:30 -07002386 #( #items __NEWLINE__ __NEWLINE__ )*
Marcel Hlopkob4b28742021-09-15 12:45:20 +00002387
Devin Jeanpierre4f06f832022-04-26 15:51:30 -07002388 #mod_detail __NEWLINE__ __NEWLINE__
Michael Forsterdb8101a2021-10-08 06:56:03 +00002389
Devin Jeanpierre4f06f832022-04-26 15:51:30 -07002390 #( #assertions __NEWLINE__ __NEWLINE__ )*
2391 },
2392 rs_api_impl: quote! {#(#thunk_impls __NEWLINE__ __NEWLINE__ )*},
Marcel Hlopko89547752021-12-10 09:39:41 +00002393 })
Marcel Hlopko42abfc82021-08-09 07:03:17 +00002394}
2395
Marcel Hlopkoeaae9b72022-01-21 15:54:11 +00002396/// Makes an 'Ident' to be used in the Rust source code. Escapes Rust keywords.
2397fn make_rs_ident(ident: &str) -> Ident {
2398 // TODO(https://github.com/dtolnay/syn/pull/1098): Remove the hardcoded list once syn recognizes
2399 // 2018 and 2021 keywords.
2400 if ["async", "await", "try", "dyn"].contains(&ident) {
2401 return format_ident!("r#{}", ident);
2402 }
2403 match syn::parse_str::<syn::Ident>(ident) {
2404 Ok(_) => format_ident!("{}", ident),
2405 Err(_) => format_ident!("r#{}", ident),
2406 }
2407}
2408
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +00002409/// Formats a C++ identifier. Does not escape C++ keywords.
2410fn format_cc_ident(ident: &str) -> TokenStream {
2411 ident.parse().unwrap()
Marcel Hlopko42abfc82021-08-09 07:03:17 +00002412}
2413
Devin Jeanpierrefec5bfe2022-04-05 10:59:10 -07002414/// Returns Some(crate_ident) if this is an imported crate.
2415fn rs_imported_crate_name(owning_target: &BazelLabel, ir: &IR) -> Option<Ident> {
Googler6a0a5252022-01-11 14:08:09 +00002416 if ir.is_current_target(owning_target) || ir.is_stdlib_target(owning_target) {
Devin Jeanpierrefec5bfe2022-04-05 10:59:10 -07002417 None
Googler6a0a5252022-01-11 14:08:09 +00002418 } else {
Devin Jeanpierre084ebbd2022-04-01 04:32:46 -07002419 let owning_crate_name = owning_target.target_name();
Marcel Hlopkod906b892022-01-27 08:52:36 +00002420 // TODO(b/216587072): Remove this hacky escaping and use the import! macro once
2421 // available
Lukasz Anforowiczdd9ae0f2022-02-17 15:52:53 +00002422 let escaped_owning_crate_name = owning_crate_name.replace('-', "_");
Marcel Hlopkod906b892022-01-27 08:52:36 +00002423 let owning_crate = make_rs_ident(&escaped_owning_crate_name);
Devin Jeanpierrefec5bfe2022-04-05 10:59:10 -07002424 Some(owning_crate)
Googler6a0a5252022-01-11 14:08:09 +00002425 }
2426}
2427
Devin Jeanpierre103cbb52022-04-06 12:55:16 -07002428#[derive(Copy, Clone, Debug, Eq, PartialEq)]
Lukasz Anforowicz40c2eb82022-01-11 18:22:31 +00002429enum Mutability {
2430 Const,
2431 Mut,
2432}
2433
2434impl Mutability {
2435 fn format_for_pointer(&self) -> TokenStream {
2436 match self {
2437 Mutability::Mut => quote! {mut},
2438 Mutability::Const => quote! {const},
2439 }
2440 }
2441
2442 fn format_for_reference(&self) -> TokenStream {
2443 match self {
2444 Mutability::Mut => quote! {mut},
2445 Mutability::Const => quote! {},
2446 }
2447 }
2448}
2449
Devin Jeanpierre4e94a082022-08-10 01:58:35 -07002450/// Either a named lifetime, or the magic `'_` elided lifetime.
Devin Jeanpierre7ed8c6f2022-08-01 13:41:02 -07002451///
2452/// Warning: elided lifetimes are not always valid, and sometimes named
2453/// lifetimes are required. In particular, this should never be used for
2454/// output lifetimes.
2455///
2456/// However, because output lifetimes are never elided, a lifetime that only
2457/// occurs in a single input position can always be elided.
2458#[derive(Debug, PartialEq, Eq, Hash, Clone)]
Devin Jeanpierre4e94a082022-08-10 01:58:35 -07002459pub struct Lifetime(pub Rc<str>);
2460
2461impl From<&ir::LifetimeName> for Lifetime {
2462 fn from(lifetime_name: &ir::LifetimeName) -> Self {
2463 Lifetime(lifetime_name.name.clone())
2464 }
Devin Jeanpierre7ed8c6f2022-08-01 13:41:02 -07002465}
2466
2467impl Lifetime {
Devin Jeanpierre4e94a082022-08-10 01:58:35 -07002468 pub fn new(name: &str) -> Self {
2469 Lifetime(Rc::from(name))
2470 }
Devin Jeanpierre7ed8c6f2022-08-01 13:41:02 -07002471 /// Formats a lifetime for use as a reference lifetime parameter.
2472 ///
2473 /// In this case, elided lifetimes are empty.
2474 pub fn format_for_reference(&self) -> TokenStream {
Devin Jeanpierre4e94a082022-08-10 01:58:35 -07002475 match &*self.0 {
2476 "_" => quote! {},
2477 _ => quote! {#self},
Devin Jeanpierre7ed8c6f2022-08-01 13:41:02 -07002478 }
2479 }
2480}
2481
2482/// Formats a lifetime for use anywhere.
2483///
2484/// For the specific context of references, prefer `format_for_reference`, as it
2485/// gives a more idiomatic formatting for elided lifetimes.
2486impl ToTokens for Lifetime {
2487 fn to_tokens(&self, tokens: &mut TokenStream) {
Devin Jeanpierre4e94a082022-08-10 01:58:35 -07002488 let Self(name) = self;
2489 let lifetime = syn::Lifetime::new(&format!("'{name}"), proc_macro2::Span::call_site());
2490 lifetime.to_tokens(tokens);
Devin Jeanpierre7ed8c6f2022-08-01 13:41:02 -07002491 }
2492}
2493
Devin Jeanpierre409f6f62022-07-07 02:00:26 -07002494#[derive(Clone, Debug, PartialEq, Eq)]
Devin Jeanpierre8cd8ae72022-06-29 18:59:40 -07002495enum RsTypeKind {
Devin Jeanpierrefec5bfe2022-04-05 10:59:10 -07002496 Pointer {
Devin Jeanpierreb2b6cf82022-07-07 01:49:27 -07002497 pointee: Rc<RsTypeKind>,
Devin Jeanpierrefec5bfe2022-04-05 10:59:10 -07002498 mutability: Mutability,
2499 },
2500 Reference {
Devin Jeanpierreb2b6cf82022-07-07 01:49:27 -07002501 referent: Rc<RsTypeKind>,
Devin Jeanpierrefec5bfe2022-04-05 10:59:10 -07002502 mutability: Mutability,
Devin Jeanpierre7ed8c6f2022-08-01 13:41:02 -07002503 lifetime: Lifetime,
Devin Jeanpierrefec5bfe2022-04-05 10:59:10 -07002504 },
2505 RvalueReference {
Devin Jeanpierreb2b6cf82022-07-07 01:49:27 -07002506 referent: Rc<RsTypeKind>,
Devin Jeanpierrefec5bfe2022-04-05 10:59:10 -07002507 mutability: Mutability,
Devin Jeanpierre7ed8c6f2022-08-01 13:41:02 -07002508 lifetime: Lifetime,
Devin Jeanpierrefec5bfe2022-04-05 10:59:10 -07002509 },
2510 FuncPtr {
Devin Jeanpierre8cd8ae72022-06-29 18:59:40 -07002511 abi: Rc<str>,
Devin Jeanpierreb2b6cf82022-07-07 01:49:27 -07002512 return_type: Rc<RsTypeKind>,
2513 param_types: Rc<[RsTypeKind]>,
Devin Jeanpierrefec5bfe2022-04-05 10:59:10 -07002514 },
Devin Jeanpierrec0543eb2022-04-20 16:00:34 -07002515 /// An incomplete record type.
2516 IncompleteRecord {
Devin Jeanpierre8cd8ae72022-06-29 18:59:40 -07002517 incomplete_record: Rc<IncompleteRecord>,
Devin Jeanpierrec0543eb2022-04-20 16:00:34 -07002518
2519 /// The imported crate this comes from, or None if the current crate.
2520 crate_ident: Option<Ident>,
Rosica Dejanovska8283fe62022-05-09 10:04:28 -07002521 /// The namespace qualifier for this record.
Lukasz Anforowicz92be29f2022-09-02 17:12:29 -07002522 namespace_qualifier: Rc<NamespaceQualifier>,
Devin Jeanpierrec0543eb2022-04-20 16:00:34 -07002523 },
2524 /// A complete record type.
Devin Jeanpierrefec5bfe2022-04-05 10:59:10 -07002525 Record {
Devin Jeanpierre8cd8ae72022-06-29 18:59:40 -07002526 record: Rc<Record>,
Rosica Dejanovska8283fe62022-05-09 10:04:28 -07002527 /// The namespace qualifier for this record.
Lukasz Anforowicz92be29f2022-09-02 17:12:29 -07002528 namespace_qualifier: Rc<NamespaceQualifier>,
Devin Jeanpierrefec5bfe2022-04-05 10:59:10 -07002529 /// The imported crate this comes from, or None if the current crate.
2530 crate_ident: Option<Ident>,
2531 },
2532 TypeAlias {
Devin Jeanpierre8cd8ae72022-06-29 18:59:40 -07002533 type_alias: Rc<TypeAlias>,
Devin Jeanpierreb2b6cf82022-07-07 01:49:27 -07002534 underlying_type: Rc<RsTypeKind>,
Rosica Dejanovska8283fe62022-05-09 10:04:28 -07002535 /// The namespace qualifier for this alias.
Lukasz Anforowicz92be29f2022-09-02 17:12:29 -07002536 namespace_qualifier: Rc<NamespaceQualifier>,
Devin Jeanpierrefec5bfe2022-04-05 10:59:10 -07002537 /// The imported crate this comes from, or None if the current crate.
2538 crate_ident: Option<Ident>,
2539 },
Lukasz Anforowicz40c2eb82022-01-11 18:22:31 +00002540 Unit,
Devin Jeanpierrefec5bfe2022-04-05 10:59:10 -07002541 Other {
Devin Jeanpierre8cd8ae72022-06-29 18:59:40 -07002542 name: Rc<str>,
Devin Jeanpierreb2b6cf82022-07-07 01:49:27 -07002543 type_args: Rc<[RsTypeKind]>,
Devin Jeanpierrefec5bfe2022-04-05 10:59:10 -07002544 },
Lukasz Anforowicz40c2eb82022-01-11 18:22:31 +00002545}
2546
Devin Jeanpierre8cd8ae72022-06-29 18:59:40 -07002547impl RsTypeKind {
Devin Jeanpierre8cd8ae72022-06-29 18:59:40 -07002548 pub fn new_record(record: Rc<Record>, ir: &IR) -> Result<Self> {
Lukasz Anforowicz92be29f2022-09-02 17:12:29 -07002549 let namespace_qualifier = Rc::new(NamespaceQualifier::new(record.id, ir)?);
Devin Jeanpierre8cd8ae72022-06-29 18:59:40 -07002550 let crate_ident = rs_imported_crate_name(&record.owning_target, ir);
2551 Ok(RsTypeKind::Record { record, namespace_qualifier, crate_ident })
Devin Jeanpierrefec5bfe2022-04-05 10:59:10 -07002552 }
2553
Devin Jeanpierre149950d2022-02-22 21:02:02 +00002554 /// Returns true if the type is known to be `Unpin`, false otherwise.
Devin Jeanpierref85ae592022-04-01 04:33:57 -07002555 pub fn is_unpin(&self) -> bool {
Devin Jeanpierre149950d2022-02-22 21:02:02 +00002556 match self {
Devin Jeanpierrec0543eb2022-04-20 16:00:34 -07002557 RsTypeKind::IncompleteRecord { .. } => false,
Devin Jeanpierrefec5bfe2022-04-05 10:59:10 -07002558 RsTypeKind::Record { record, .. } => record.is_unpin(),
Devin Jeanpierref85ae592022-04-01 04:33:57 -07002559 RsTypeKind::TypeAlias { underlying_type, .. } => underlying_type.is_unpin(),
Devin Jeanpierre149950d2022-02-22 21:02:02 +00002560 _ => true,
2561 }
2562 }
2563
Devin Jeanpierre11ce2b92022-07-27 07:54:21 -07002564 /// Returns true if the type is known to be move-constructible, false
2565 /// otherwise.
2566 ///
2567 /// For the purposes of this method, references are considered
2568 /// move-constructible (as if they were pointers).
2569 pub fn is_move_constructible(&self) -> bool {
2570 match self {
2571 RsTypeKind::IncompleteRecord { .. } => false,
2572 RsTypeKind::Record { record, .. } => {
2573 record.move_constructor != ir::SpecialMemberFunc::Unavailable
2574 }
2575 RsTypeKind::TypeAlias { underlying_type, .. } => {
2576 underlying_type.is_move_constructible()
2577 }
2578 _ => true,
2579 }
2580 }
2581
Devin Jeanpierrea99e0a32022-08-17 01:36:58 -07002582 /// Returns Ok if the type can be used by value, or an error describing why
2583 /// it can't.
2584 pub fn check_by_value(&self) -> Result<()> {
2585 match self {
2586 RsTypeKind::Record { record, .. } => check_by_value(record),
2587 RsTypeKind::TypeAlias { underlying_type, .. } => underlying_type.check_by_value(),
2588 _ => Ok(()),
2589 }
2590 }
2591
Devin Jeanpierre82e8e362022-04-05 11:04:55 -07002592 pub fn format_as_return_type_fragment(&self) -> TokenStream {
Lukasz Anforowiczcf230fd2022-02-18 19:20:39 +00002593 match self {
Devin Jeanpierred2b7a8f2022-04-01 04:37:16 -07002594 RsTypeKind::Unit => quote! {},
Lukasz Anforowiczcf230fd2022-02-18 19:20:39 +00002595 other_type => {
Devin Jeanpierre92ca2612022-04-06 11:35:13 -07002596 quote! { -> #other_type }
Lukasz Anforowiczcf230fd2022-02-18 19:20:39 +00002597 }
2598 }
2599 }
2600
Lukasz Anforowiczcde4b1b2022-02-03 21:20:55 +00002601 /// Formats this RsTypeKind as `&'a mut MaybeUninit<SomeStruct>`. This is
2602 /// used to format `__this` parameter in a constructor thunk.
Devin Jeanpierre82e8e362022-04-05 11:04:55 -07002603 pub fn format_mut_ref_as_uninitialized(&self) -> Result<TokenStream> {
Lukasz Anforowiczcde4b1b2022-02-03 21:20:55 +00002604 match self {
Devin Jeanpierred2b7a8f2022-04-01 04:37:16 -07002605 RsTypeKind::Reference { referent, lifetime, mutability: Mutability::Mut } => {
Devin Jeanpierre7ed8c6f2022-08-01 13:41:02 -07002606 let lifetime = lifetime.format_for_reference();
Devin Jeanpierrecd5ebf32022-07-08 05:31:55 -07002607 Ok(quote! { & #lifetime mut ::std::mem::MaybeUninit< #referent > })
Lukasz Anforowicz40c2eb82022-01-11 18:22:31 +00002608 }
Devin Jeanpierre149950d2022-02-22 21:02:02 +00002609 _ => bail!("Expected reference to format as MaybeUninit, got: {:?}", self),
Lukasz Anforowiczcde4b1b2022-02-03 21:20:55 +00002610 }
Lukasz Anforowicz40c2eb82022-01-11 18:22:31 +00002611 }
2612
Devin Jeanpierre149950d2022-02-22 21:02:02 +00002613 /// Formats this RsTypeKind as the `self` parameter: usually, `&'a self` or
2614 /// `&'a mut self`.
2615 ///
2616 /// If this is !Unpin, however, it uses `self: Pin<&mut Self>` instead.
Devin Jeanpierre108e9c02022-06-02 07:10:09 -07002617 pub fn format_as_self_param(&self) -> Result<TokenStream> {
Devin Jeanpierre215ee8e2022-05-16 16:09:41 -07002618 let referent;
2619 let mutability;
2620 let lifetime;
2621 match self {
Devin Jeanpierre108e9c02022-06-02 07:10:09 -07002622 RsTypeKind::Pointer { .. } => {
Devin Jeanpierre8a4fa202022-06-08 12:33:11 -07002623 // TODO(jeanpierreda): provide end-user-facing docs, and insert a link to e.g.
2624 // something like <internal link>
2625 bail!(
2626 "`self` has no lifetime. Use lifetime annotations or `#pragma clang lifetime_elision` to create bindings for this function."
2627 )
Lukasz Anforowicz732ca642022-02-03 20:58:38 +00002628 }
Devin Jeanpierre215ee8e2022-05-16 16:09:41 -07002629 RsTypeKind::Reference {
2630 referent: reference_pointee,
2631 lifetime: reference_lifetime,
2632 mutability: reference_mutability,
2633 } => {
2634 referent = reference_pointee;
2635 mutability = reference_mutability;
Devin Jeanpierre7ed8c6f2022-08-01 13:41:02 -07002636 lifetime = reference_lifetime;
Lukasz Anforowicz231a3bb2022-01-12 14:05:59 +00002637 }
Michael VanBemmel83eca6b2022-07-20 10:16:38 -07002638 RsTypeKind::Record { .. } => {
Devin Jeanpierree9be70a2022-07-25 08:58:54 -07002639 // This case doesn't happen for methods, but is needed for free functions mapped
2640 // to a trait impl that take the first argument by value.
Michael VanBemmel83eca6b2022-07-20 10:16:38 -07002641 return Ok(quote! { self });
2642 }
Lukasz Anforowicz732ca642022-02-03 20:58:38 +00002643 _ => bail!("Unexpected type of `self` parameter: {:?}", self),
Lukasz Anforowicz231a3bb2022-01-12 14:05:59 +00002644 }
Devin Jeanpierre215ee8e2022-05-16 16:09:41 -07002645 let mut_ = mutability.format_for_reference();
Devin Jeanpierre7ed8c6f2022-08-01 13:41:02 -07002646 let lifetime = lifetime.format_for_reference();
Devin Jeanpierre215ee8e2022-05-16 16:09:41 -07002647 if mutability == &Mutability::Mut && !referent.is_unpin() {
Devin Jeanpierre5d5ca7a2022-07-20 02:27:00 -07002648 // TODO(b/239661934): Add a `use ::std::pin::Pin` to the crate, and use
Devin Jeanpierre215ee8e2022-05-16 16:09:41 -07002649 // `Pin`.
Devin Jeanpierrecd5ebf32022-07-08 05:31:55 -07002650 Ok(quote! {self: ::std::pin::Pin< & #lifetime #mut_ Self>})
Devin Jeanpierre215ee8e2022-05-16 16:09:41 -07002651 } else {
2652 Ok(quote! { & #lifetime #mut_ self })
2653 }
Lukasz Anforowicz231a3bb2022-01-12 14:05:59 +00002654 }
2655
Lukasz Anforowicz90bdb962022-02-14 21:07:45 +00002656 /// Returns whether the type represented by `self` implements the `Copy`
2657 /// trait.
Lukasz Anforowicza94ab702022-01-14 22:40:25 +00002658 pub fn implements_copy(&self) -> bool {
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00002659 // TODO(b/212696226): Verify results of `implements_copy` via static
2660 // assertions in the generated Rust code (because incorrect results
2661 // can silently lead to unsafe behavior).
2662 match self {
2663 RsTypeKind::Unit => true,
2664 RsTypeKind::Pointer { .. } => true,
Lukasz Anforowiczcf230fd2022-02-18 19:20:39 +00002665 RsTypeKind::FuncPtr { .. } => true,
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00002666 RsTypeKind::Reference { mutability: Mutability::Const, .. } => true,
2667 RsTypeKind::Reference { mutability: Mutability::Mut, .. } => false,
Devin Jeanpierredeea7892022-03-29 02:13:31 -07002668 RsTypeKind::RvalueReference { .. } => false,
Devin Jeanpierrec0543eb2022-04-20 16:00:34 -07002669 RsTypeKind::IncompleteRecord { .. } => false,
Devin Jeanpierrefec5bfe2022-04-05 10:59:10 -07002670 RsTypeKind::Record { record, .. } => should_derive_copy(record),
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00002671 RsTypeKind::TypeAlias { underlying_type, .. } => underlying_type.implements_copy(),
Lukasz Anforowiczd81bea92022-02-11 08:57:58 +00002672 RsTypeKind::Other { type_args, .. } => {
Lukasz Anforowicz20651e32022-02-10 14:52:15 +00002673 // All types that may appear here without `type_args` (e.g.
2674 // primitive types like `i32`) implement `Copy`. Generic types
2675 // that may be present here (e.g. Option<...>) are `Copy` if all
2676 // of their `type_args` are `Copy`.
2677 type_args.iter().all(|t| t.implements_copy())
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00002678 }
2679 }
2680 }
Lukasz Anforowicza94ab702022-01-14 22:40:25 +00002681
Lukasz Anforowiczf9564622022-01-28 14:31:04 +00002682 pub fn is_ref_to(&self, expected_record: &Record) -> bool {
2683 match self {
2684 RsTypeKind::Reference { referent, .. } => referent.is_record(expected_record),
2685 _ => false,
2686 }
2687 }
2688
Lukasz Anforowicza94ab702022-01-14 22:40:25 +00002689 pub fn is_shared_ref_to(&self, expected_record: &Record) -> bool {
2690 match self {
2691 RsTypeKind::Reference { referent, mutability: Mutability::Const, .. } => {
Lukasz Anforowiczf9564622022-01-28 14:31:04 +00002692 referent.is_record(expected_record)
Lukasz Anforowicza94ab702022-01-14 22:40:25 +00002693 }
2694 _ => false,
2695 }
2696 }
Lukasz Anforowiczf9564622022-01-28 14:31:04 +00002697
2698 pub fn is_record(&self, expected_record: &Record) -> bool {
2699 match self {
Devin Jeanpierrefec5bfe2022-04-05 10:59:10 -07002700 RsTypeKind::Record { record: actual_record, .. } => {
2701 actual_record.id == expected_record.id
2702 }
Lukasz Anforowiczf9564622022-01-28 14:31:04 +00002703 _ => false,
2704 }
2705 }
Lukasz Anforowicz90bdb962022-02-14 21:07:45 +00002706
2707 /// Iterates over `self` and all the nested types (e.g. pointees, generic
2708 /// type args, etc.) in DFS order.
Devin Jeanpierre8cd8ae72022-06-29 18:59:40 -07002709 pub fn dfs_iter<'ty>(&'ty self) -> impl Iterator<Item = &'ty RsTypeKind> + '_ {
Lukasz Anforowicz90bdb962022-02-14 21:07:45 +00002710 RsTypeKindIter::new(self)
2711 }
2712
2713 /// Iterates over all `LifetimeId`s in `self` and in all the nested types.
2714 /// Note that the results might contain duplicate LifetimeId values (e.g.
2715 /// if the same LifetimeId is used in two `type_args`).
Devin Jeanpierre4e94a082022-08-10 01:58:35 -07002716 pub fn lifetimes(&self) -> impl Iterator<Item = Lifetime> + '_ {
2717 self.dfs_iter().filter_map(Self::lifetime)
Lukasz Anforowicz90bdb962022-02-14 21:07:45 +00002718 }
Devin Jeanpierrefd4ff822022-07-15 01:43:55 -07002719
2720 /// Returns the pointer or reference target.
2721 pub fn referent(&self) -> Option<&RsTypeKind> {
2722 match self {
2723 Self::Pointer { pointee: p, .. }
2724 | Self::Reference { referent: p, .. }
2725 | Self::RvalueReference { referent: p, .. } => Some(&**p),
2726 _ => None,
2727 }
2728 }
Devin Jeanpierre4e94a082022-08-10 01:58:35 -07002729
2730 /// Returns the reference lifetime, or None if this is not a reference.
2731 pub fn lifetime(&self) -> Option<Lifetime> {
2732 match self {
2733 Self::Reference { lifetime, .. } | Self::RvalueReference { lifetime, .. } => {
2734 Some(lifetime.clone())
2735 }
2736 _ => None,
2737 }
2738 }
Lukasz Anforowicz90bdb962022-02-14 21:07:45 +00002739}
2740
Devin Jeanpierre8cd8ae72022-06-29 18:59:40 -07002741impl ToTokens for RsTypeKind {
Devin Jeanpierre92ca2612022-04-06 11:35:13 -07002742 fn to_tokens(&self, tokens: &mut TokenStream) {
2743 self.to_token_stream().to_tokens(tokens)
2744 }
2745
2746 fn to_token_stream(&self) -> TokenStream {
2747 match self {
2748 RsTypeKind::Pointer { pointee, mutability } => {
2749 let mutability = mutability.format_for_pointer();
2750 quote! {* #mutability #pointee}
2751 }
2752 RsTypeKind::Reference { referent, mutability, lifetime } => {
2753 let mut_ = mutability.format_for_reference();
Devin Jeanpierre7ed8c6f2022-08-01 13:41:02 -07002754 let lifetime = lifetime.format_for_reference();
Devin Jeanpierre92ca2612022-04-06 11:35:13 -07002755 let reference = quote! {& #lifetime #mut_ #referent};
2756 if mutability == &Mutability::Mut && !referent.is_unpin() {
Devin Jeanpierre5d5ca7a2022-07-20 02:27:00 -07002757 // TODO(b/239661934): Add a `use ::std::pin::Pin` to the crate, and use
Devin Jeanpierre92ca2612022-04-06 11:35:13 -07002758 // `Pin`. This either requires deciding how to qualify pin at
2759 // RsTypeKind-creation time, or returning an RsSnippet from here (and not
2760 // implementing ToTokens, but instead some other interface.)
Devin Jeanpierrecd5ebf32022-07-08 05:31:55 -07002761 quote! {::std::pin::Pin< #reference >}
Devin Jeanpierre92ca2612022-04-06 11:35:13 -07002762 } else {
2763 reference
2764 }
2765 }
2766 RsTypeKind::RvalueReference { referent, mutability, lifetime } => {
Devin Jeanpierre5d5ca7a2022-07-20 02:27:00 -07002767 // TODO(b/239661934): Add a `use ::ctor::RvalueReference` (etc.) to the crate.
Devin Jeanpierre92ca2612022-04-06 11:35:13 -07002768 if mutability == &Mutability::Mut {
Devin Jeanpierrecd5ebf32022-07-08 05:31:55 -07002769 quote! {::ctor::RvalueReference<#lifetime, #referent>}
Devin Jeanpierre92ca2612022-04-06 11:35:13 -07002770 } else {
Devin Jeanpierrecd5ebf32022-07-08 05:31:55 -07002771 quote! {::ctor::ConstRvalueReference<#lifetime, #referent>}
Devin Jeanpierre92ca2612022-04-06 11:35:13 -07002772 }
2773 }
2774 RsTypeKind::FuncPtr { abi, return_type, param_types } => {
2775 let return_frag = return_type.format_as_return_type_fragment();
2776 quote! { extern #abi fn( #( #param_types ),* ) #return_frag }
2777 }
Marcel Hlopko36234892022-05-10 00:39:54 -07002778 RsTypeKind::IncompleteRecord {
2779 incomplete_record,
2780 namespace_qualifier,
2781 crate_ident,
2782 } => {
Rosica Dejanovskae12d7172022-06-22 12:20:17 -07002783 let record_ident = make_rs_ident(&incomplete_record.rs_name);
Lukasz Anforowicz92be29f2022-09-02 17:12:29 -07002784 let namespace_qualifier = namespace_qualifier.format_for_rs();
Rosica Dejanovska8283fe62022-05-09 10:04:28 -07002785 match crate_ident {
2786 Some(ci) => {
Lukasz Anforowicz4e2e0162022-09-01 07:07:32 -07002787 quote! { #ci #namespace_qualifier #record_ident }
Marcel Hlopko36234892022-05-10 00:39:54 -07002788 }
Rosica Dejanovska8283fe62022-05-09 10:04:28 -07002789 None => {
Lukasz Anforowicz4e2e0162022-09-01 07:07:32 -07002790 quote! { crate:: #namespace_qualifier #record_ident }
Rosica Dejanovska8283fe62022-05-09 10:04:28 -07002791 }
2792 }
Devin Jeanpierrec0543eb2022-04-20 16:00:34 -07002793 }
Rosica Dejanovska8283fe62022-05-09 10:04:28 -07002794 RsTypeKind::Record { record, namespace_qualifier, crate_ident } => {
2795 let ident = make_rs_ident(&record.rs_name);
Lukasz Anforowicz92be29f2022-09-02 17:12:29 -07002796 let namespace_qualifier = namespace_qualifier.format_for_rs();
Rosica Dejanovska8283fe62022-05-09 10:04:28 -07002797 match crate_ident {
2798 Some(ci) => {
Lukasz Anforowicz4e2e0162022-09-01 07:07:32 -07002799 quote! { #ci:: #namespace_qualifier #ident }
Marcel Hlopko36234892022-05-10 00:39:54 -07002800 }
Rosica Dejanovska8283fe62022-05-09 10:04:28 -07002801 None => {
Lukasz Anforowicz4e2e0162022-09-01 07:07:32 -07002802 quote! { crate:: #namespace_qualifier #ident }
Rosica Dejanovska8283fe62022-05-09 10:04:28 -07002803 }
2804 }
Devin Jeanpierre92ca2612022-04-06 11:35:13 -07002805 }
Rosica Dejanovska8283fe62022-05-09 10:04:28 -07002806 RsTypeKind::TypeAlias { type_alias, namespace_qualifier, crate_ident, .. } => {
2807 let ident = make_rs_ident(&type_alias.identifier.identifier);
Lukasz Anforowicz92be29f2022-09-02 17:12:29 -07002808 let namespace_qualifier = namespace_qualifier.format_for_rs();
Rosica Dejanovska8283fe62022-05-09 10:04:28 -07002809 match crate_ident {
2810 Some(ci) => {
Lukasz Anforowicz4e2e0162022-09-01 07:07:32 -07002811 quote! { #ci:: #namespace_qualifier #ident }
Marcel Hlopko36234892022-05-10 00:39:54 -07002812 }
Rosica Dejanovska8283fe62022-05-09 10:04:28 -07002813 None => {
Lukasz Anforowicz4e2e0162022-09-01 07:07:32 -07002814 quote! { crate:: #namespace_qualifier #ident }
Rosica Dejanovska8283fe62022-05-09 10:04:28 -07002815 }
2816 }
Devin Jeanpierre92ca2612022-04-06 11:35:13 -07002817 }
2818 RsTypeKind::Unit => quote! {()},
2819 RsTypeKind::Other { name, type_args } => {
2820 let ident = make_rs_ident(name);
Devin Jeanpierreb2b6cf82022-07-07 01:49:27 -07002821 let generic_params = format_generic_params(type_args.iter());
Devin Jeanpierre92ca2612022-04-06 11:35:13 -07002822 quote! {#ident #generic_params}
2823 }
2824 }
2825 }
2826}
2827
Devin Jeanpierre8cd8ae72022-06-29 18:59:40 -07002828struct RsTypeKindIter<'ty> {
2829 todo: Vec<&'ty RsTypeKind>,
Lukasz Anforowicz90bdb962022-02-14 21:07:45 +00002830}
2831
Devin Jeanpierre8cd8ae72022-06-29 18:59:40 -07002832impl<'ty> RsTypeKindIter<'ty> {
2833 pub fn new(ty: &'ty RsTypeKind) -> Self {
Lukasz Anforowicz90bdb962022-02-14 21:07:45 +00002834 Self { todo: vec![ty] }
2835 }
2836}
2837
Devin Jeanpierre8cd8ae72022-06-29 18:59:40 -07002838impl<'ty> Iterator for RsTypeKindIter<'ty> {
2839 type Item = &'ty RsTypeKind;
Lukasz Anforowicz90bdb962022-02-14 21:07:45 +00002840
2841 fn next(&mut self) -> Option<Self::Item> {
2842 match self.todo.pop() {
2843 None => None,
2844 Some(curr) => {
2845 match curr {
Devin Jeanpierrec0543eb2022-04-20 16:00:34 -07002846 RsTypeKind::Unit
2847 | RsTypeKind::IncompleteRecord { .. }
2848 | RsTypeKind::Record { .. } => {}
Lukasz Anforowicz90bdb962022-02-14 21:07:45 +00002849 RsTypeKind::Pointer { pointee, .. } => self.todo.push(pointee),
2850 RsTypeKind::Reference { referent, .. } => self.todo.push(referent),
Devin Jeanpierredeea7892022-03-29 02:13:31 -07002851 RsTypeKind::RvalueReference { referent, .. } => self.todo.push(referent),
Lukasz Anforowicz90bdb962022-02-14 21:07:45 +00002852 RsTypeKind::TypeAlias { underlying_type: t, .. } => self.todo.push(t),
Lukasz Anforowiczcf230fd2022-02-18 19:20:39 +00002853 RsTypeKind::FuncPtr { return_type, param_types, .. } => {
2854 self.todo.push(return_type);
2855 self.todo.extend(param_types.iter().rev());
Devin Jeanpierre1b8f4a12022-03-22 21:29:42 +00002856 }
Lukasz Anforowicz90bdb962022-02-14 21:07:45 +00002857 RsTypeKind::Other { type_args, .. } => self.todo.extend(type_args.iter().rev()),
2858 };
2859 Some(curr)
2860 }
2861 }
2862 }
Lukasz Anforowicz40c2eb82022-01-11 18:22:31 +00002863}
2864
Devin Jeanpierre6bb81802022-08-10 02:08:47 -07002865fn unique_lifetimes<'a>(
2866 types: impl IntoIterator<Item = &'a RsTypeKind> + 'a,
2867) -> impl Iterator<Item = Lifetime> + 'a {
2868 let mut unordered_lifetimes = HashSet::new();
2869 types
2870 .into_iter()
2871 .flat_map(|ty| ty.lifetimes())
2872 .filter(move |lifetime| unordered_lifetimes.insert(lifetime.clone()))
2873}
2874
Devin Jeanpierre3a0cc5a2022-07-12 09:36:34 -07002875fn rs_type_kind(db: &dyn BindingsGenerator, ty: ir::RsType) -> Result<RsTypeKind> {
2876 let ir = db.ir();
2877 // The lambdas deduplicate code needed by multiple `match` branches.
2878 let get_type_args = || -> Result<Vec<RsTypeKind>> {
2879 ty.type_args.iter().map(|type_arg| db.rs_type_kind(type_arg.clone())).collect()
2880 };
2881 let get_pointee = || -> Result<Rc<RsTypeKind>> {
2882 if ty.type_args.len() != 1 {
2883 bail!("Missing pointee/referent type (need exactly 1 type argument): {:?}", ty);
2884 }
2885 Ok(Rc::new(get_type_args()?.remove(0)))
2886 };
Devin Jeanpierre7ed8c6f2022-08-01 13:41:02 -07002887 let get_lifetime = || -> Result<Lifetime> {
Devin Jeanpierre3a0cc5a2022-07-12 09:36:34 -07002888 if ty.lifetime_args.len() != 1 {
2889 bail!("Missing reference lifetime (need exactly 1 lifetime argument): {:?}", ty);
2890 }
2891 let lifetime_id = ty.lifetime_args[0];
2892 ir.get_lifetime(lifetime_id)
2893 .ok_or_else(|| anyhow!("no known lifetime with id {lifetime_id:?}"))
Devin Jeanpierre4e94a082022-08-10 01:58:35 -07002894 .map(Lifetime::from)
Devin Jeanpierre3a0cc5a2022-07-12 09:36:34 -07002895 };
2896
2897 let result = match ty.name.as_deref() {
2898 None => {
2899 ensure!(
2900 ty.type_args.is_empty(),
2901 "Type arguments on records nor type aliases are not yet supported: {:?}",
2902 ty
2903 );
2904 match ir.item_for_type(&ty)? {
2905 Item::IncompleteRecord(incomplete_record) => RsTypeKind::IncompleteRecord {
2906 incomplete_record: incomplete_record.clone(),
Lukasz Anforowicz92be29f2022-09-02 17:12:29 -07002907 namespace_qualifier: Rc::new(NamespaceQualifier::new(incomplete_record.id, &ir)?),
Devin Jeanpierre3a0cc5a2022-07-12 09:36:34 -07002908 crate_ident: rs_imported_crate_name(&incomplete_record.owning_target, &ir),
2909 },
2910 Item::Record(record) => RsTypeKind::new_record(record.clone(), &ir)?,
2911 Item::TypeAlias(type_alias) => RsTypeKind::TypeAlias {
2912 type_alias: type_alias.clone(),
Lukasz Anforowicz92be29f2022-09-02 17:12:29 -07002913 namespace_qualifier: Rc::new(NamespaceQualifier::new(type_alias.id, &ir)?),
Devin Jeanpierre3a0cc5a2022-07-12 09:36:34 -07002914 crate_ident: rs_imported_crate_name(&type_alias.owning_target, &ir),
2915 underlying_type: Rc::new(
2916 db.rs_type_kind(type_alias.underlying_type.rs_type.clone())?,
2917 ),
2918 },
2919 other_item => bail!("Item does not define a type: {:?}", other_item),
2920 }
2921 }
2922 Some(name) => match name {
2923 "()" => {
2924 if !ty.type_args.is_empty() {
2925 bail!("Unit type must not have type arguments: {:?}", ty);
2926 }
2927 RsTypeKind::Unit
2928 }
2929 "*mut" => RsTypeKind::Pointer { pointee: get_pointee()?, mutability: Mutability::Mut },
2930 "*const" => {
2931 RsTypeKind::Pointer { pointee: get_pointee()?, mutability: Mutability::Const }
2932 }
2933 "&mut" => RsTypeKind::Reference {
2934 referent: get_pointee()?,
2935 mutability: Mutability::Mut,
2936 lifetime: get_lifetime()?,
2937 },
2938 "&" => RsTypeKind::Reference {
2939 referent: get_pointee()?,
2940 mutability: Mutability::Const,
2941 lifetime: get_lifetime()?,
2942 },
2943 "#RvalueReference mut" => RsTypeKind::RvalueReference {
2944 referent: get_pointee()?,
2945 mutability: Mutability::Mut,
2946 lifetime: get_lifetime()?,
2947 },
2948 "#RvalueReference const" => RsTypeKind::RvalueReference {
2949 referent: get_pointee()?,
2950 mutability: Mutability::Const,
2951 lifetime: get_lifetime()?,
2952 },
2953 name => {
2954 let mut type_args = get_type_args()?;
2955 match name.strip_prefix("#funcPtr ") {
2956 None => RsTypeKind::Other { name: name.into(), type_args: Rc::from(type_args) },
2957 Some(abi) => {
2958 // TODO(b/217419782): Consider enforcing `'static` lifetime.
2959 ensure!(!type_args.is_empty(), "No return type in fn type: {:?}", ty);
2960 RsTypeKind::FuncPtr {
2961 abi: abi.into(),
2962 return_type: Rc::new(type_args.remove(type_args.len() - 1)),
2963 param_types: Rc::from(type_args),
2964 }
2965 }
2966 }
2967 }
2968 },
2969 };
2970 Ok(result)
Devin Jeanpierre7a7328e2021-09-17 07:10:08 +00002971}
2972
Lukasz Anforowicz90bb7462022-09-01 08:13:28 -07002973fn cc_type_name_for_record(record: &Record, ir: &IR) -> Result<TokenStream> {
2974 let ident = format_cc_ident(&record.cc_name);
Lukasz Anforowicz92be29f2022-09-02 17:12:29 -07002975 let namespace_qualifier = NamespaceQualifier::new(record.id, ir)?.format_for_cc();
Lukasz Anforowicz90bb7462022-09-01 08:13:28 -07002976 let tag_kind = cc_tag_kind(record);
2977 Ok(quote! { #tag_kind #namespace_qualifier #ident })
2978}
2979
Rosica Dejanovska8283fe62022-05-09 10:04:28 -07002980fn cc_type_name_for_item(item: &ir::Item, ir: &IR) -> Result<TokenStream> {
Lukasz Anforowicz90bb7462022-09-01 08:13:28 -07002981 match item {
Lukasz Anforowicz8dd51792022-08-31 10:11:17 -07002982 Item::IncompleteRecord(incomplete_record) => {
2983 let ident = format_cc_ident(&incomplete_record.cc_name);
Lukasz Anforowicz92be29f2022-09-02 17:12:29 -07002984 let namespace_qualifier = NamespaceQualifier::new(incomplete_record.id, ir)?.format_for_cc();
Lukasz Anforowicz8dd51792022-08-31 10:11:17 -07002985 let tag_kind = incomplete_record.record_type;
Lukasz Anforowicz90bb7462022-09-01 08:13:28 -07002986 Ok(quote! { #tag_kind #namespace_qualifier #ident })
Lukasz Anforowicz8dd51792022-08-31 10:11:17 -07002987 }
Lukasz Anforowicz90bb7462022-09-01 08:13:28 -07002988 Item::Record(record) => cc_type_name_for_record(record, ir),
Lukasz Anforowicz4c3a2cc2022-03-11 00:24:49 +00002989 Item::TypeAlias(type_alias) => {
2990 let ident = format_cc_ident(&type_alias.identifier.identifier);
Lukasz Anforowicz92be29f2022-09-02 17:12:29 -07002991 let namespace_qualifier = NamespaceQualifier::new(type_alias.id, ir)?.format_for_cc();
Lukasz Anforowicz90bb7462022-09-01 08:13:28 -07002992 Ok(quote! { #namespace_qualifier #ident })
Devin Jeanpierre1b8f4a12022-03-22 21:29:42 +00002993 }
Googler6a0a5252022-01-11 14:08:09 +00002994 _ => bail!("Item does not define a type: {:?}", item),
Lukasz Anforowicz90bb7462022-09-01 08:13:28 -07002995 }
Googler6a0a5252022-01-11 14:08:09 +00002996}
2997
Lukasz Anforowiczd4742ff2022-07-11 17:05:02 -07002998fn cc_tag_kind(record: &ir::Record) -> TokenStream {
Kinuko Yasuda8dd84642022-08-17 09:19:47 -07002999 if record.is_anon_record_with_typedef {
3000 quote! {}
3001 } else {
Lukasz Anforowicz8dd51792022-08-31 10:11:17 -07003002 record.record_type.into_token_stream()
Marcel Hlopko4c29c6f2022-05-04 00:49:14 -07003003 }
3004}
3005
Lukasz Anforowicz71e9b592022-03-04 19:03:02 +00003006// Maps a Rust ABI [1] into a Clang attribute. See also
3007// `ConvertCcCallConvIntoRsApi` in importer.cc.
3008// [1]
3009// https://doc.rust-lang.org/reference/items/functions.html#extern-function-qualifier
3010fn format_cc_call_conv_as_clang_attribute(rs_abi: &str) -> Result<TokenStream> {
3011 match rs_abi {
3012 "cdecl" => Ok(quote! {}),
3013 "fastcall" => Ok(quote! { __attribute__((fastcall)) }),
3014 "stdcall" => Ok(quote! { __attribute__((stdcall)) }),
3015 "thiscall" => Ok(quote! { __attribute__((thiscall)) }),
3016 "vectorcall" => Ok(quote! { __attribute__((vectorcall)) }),
3017 _ => bail!("Unsupported ABI: {}", rs_abi),
3018 }
3019}
3020
Marcel Hlopkoc0956cf2021-11-29 08:31:28 +00003021fn format_cc_type(ty: &ir::CcType, ir: &IR) -> Result<TokenStream> {
Devin Jeanpierre09c6f452021-09-29 07:34:24 +00003022 let const_fragment = if ty.is_const {
Devin Jeanpierre184f9ac2021-09-17 13:47:03 +00003023 quote! {const}
3024 } else {
3025 quote! {}
3026 };
Marcel Hlopkoc0956cf2021-11-29 08:31:28 +00003027 if let Some(ref name) = ty.name {
3028 match name.as_str() {
Lukasz Anforowicz07b33902022-07-13 15:27:03 -07003029 // Formatting *both* pointers *and* references as pointers, because:
3030 // - Pointers and references have the same representation in the ABI.
3031 // - Clang's `-Wreturn-type-c-linkage` warns when using references in C++ function
3032 // thunks declared as `extern "C"` (see b/238681766).
3033 "*" | "&" | "&&" => {
Googlerff7fc232021-12-02 09:43:00 +00003034 if ty.type_args.len() != 1 {
3035 bail!("Invalid pointer type (need exactly 1 type argument): {:?}", ty);
Marcel Hlopkoc0956cf2021-11-29 08:31:28 +00003036 }
Googlerff7fc232021-12-02 09:43:00 +00003037 let nested_type = format_cc_type(&ty.type_args[0], ir)?;
Marcel Hlopkoc0956cf2021-11-29 08:31:28 +00003038 Ok(quote! {#nested_type * #const_fragment})
Devin Jeanpierre7a7328e2021-09-17 07:10:08 +00003039 }
Lukasz Anforowicz71e9b592022-03-04 19:03:02 +00003040 cc_type_name => match cc_type_name.strip_prefix("#funcValue ") {
3041 None => {
3042 if !ty.type_args.is_empty() {
3043 bail!("Type not yet supported: {:?}", ty);
3044 }
3045 let idents = cc_type_name.split_whitespace().map(format_cc_ident);
3046 Ok(quote! {#( #idents )* #const_fragment})
Devin Jeanpierre1b8f4a12022-03-22 21:29:42 +00003047 }
Lukasz Anforowicz71e9b592022-03-04 19:03:02 +00003048 Some(abi) => match ty.type_args.split_last() {
3049 None => bail!("funcValue type without a return type: {:?}", ty),
3050 Some((ret_type, param_types)) => {
3051 let ret_type = format_cc_type(ret_type, ir)?;
3052 let param_types = param_types
3053 .iter()
3054 .map(|t| format_cc_type(t, ir))
3055 .collect::<Result<Vec<_>>>()?;
3056 let attr = format_cc_call_conv_as_clang_attribute(abi)?;
3057 // `type_identity_t` is used below to avoid having to
3058 // emit spiral-like syntax where some syntax elements of
3059 // an inner type (e.g. function type as below) can
3060 // surround syntax elements of an outer type (e.g. a
3061 // pointer type). Compare: `int (*foo)(int, int)` VS
3062 // `type_identity_t<int(int, int)>* foo`.
Lukasz Anforowicz23171542022-04-11 15:26:17 -07003063 Ok(quote! { crubit::type_identity_t<
Devin Jeanpierre1b8f4a12022-03-22 21:29:42 +00003064 #ret_type ( #( #param_types ),* ) #attr
3065 > })
3066 }
3067 },
3068 },
Devin Jeanpierre7a7328e2021-09-17 07:10:08 +00003069 }
Marcel Hlopkoc0956cf2021-11-29 08:31:28 +00003070 } else {
Googler6a0a5252022-01-11 14:08:09 +00003071 let item = ir.item_for_type(ty)?;
Rosica Dejanovska8283fe62022-05-09 10:04:28 -07003072 let type_name = cc_type_name_for_item(item, ir)?;
Googler6a0a5252022-01-11 14:08:09 +00003073 Ok(quote! {#const_fragment #type_name})
Devin Jeanpierre7a7328e2021-09-17 07:10:08 +00003074 }
3075}
3076
Rosica Dejanovska8283fe62022-05-09 10:04:28 -07003077fn cc_struct_layout_assertion(record: &Record, ir: &IR) -> Result<TokenStream> {
Googler6a0a5252022-01-11 14:08:09 +00003078 if !ir.is_current_target(&record.owning_target) && !ir.is_stdlib_target(&record.owning_target) {
Rosica Dejanovska8283fe62022-05-09 10:04:28 -07003079 return Ok(quote! {});
Marcel Hlopkoa0f38662021-12-03 08:45:26 +00003080 }
Lukasz Anforowicz4c3a2cc2022-03-11 00:24:49 +00003081 let record_ident = format_cc_ident(&record.cc_name);
Lukasz Anforowicz92be29f2022-09-02 17:12:29 -07003082 let namespace_qualifier = NamespaceQualifier::new(record.id, ir)?.format_for_cc();
Kinuko Yasuda54b75d72022-08-18 10:09:54 -07003083 let cc_size = Literal::usize_unsuffixed(record.original_cc_size);
Googler5ea88642021-09-29 08:05:59 +00003084 let alignment = Literal::usize_unsuffixed(record.alignment);
Lukasz Anforowiczd4742ff2022-07-11 17:05:02 -07003085 let tag_kind = cc_tag_kind(record);
Devin Jeanpierre190b90a2022-05-24 06:00:34 -07003086 let field_assertions = record
3087 .fields
3088 .iter()
3089 .filter(|f| f.access == AccessSpecifier::Public && f.identifier.is_some())
3090 // https://en.cppreference.com/w/cpp/types/offsetof points out that "if member is [...]
3091 // a bit-field [...] the behavior [of `offsetof` macro] is undefined.". In such
3092 // scenario clang reports an error: cannot compute offset of bit-field 'field_name'.
3093 .filter(|f| !f.is_bitfield)
3094 .map(|field| {
3095 // The IR contains the offset in bits, while `CRUBIT_OFFSET_OF` returns the
3096 // offset in bytes, so we need to convert. We can assert that
3097 // `field.offset` is always at field boundaries, because the
3098 // bitfields have been filtered out earlier.
3099 assert_eq!(field.offset % 8, 0);
3100 let expected_offset = Literal::usize_unsuffixed(field.offset / 8);
Lukasz Anforowicz30360ba2022-05-23 12:15:12 -07003101
Devin Jeanpierre190b90a2022-05-24 06:00:34 -07003102 let field_ident = format_cc_ident(&field.identifier.as_ref().unwrap().identifier);
3103 let actual_offset = quote! {
3104 CRUBIT_OFFSET_OF(#field_ident, #tag_kind #namespace_qualifier #record_ident)
3105 };
Lukasz Anforowicz30360ba2022-05-23 12:15:12 -07003106
Devin Jeanpierre190b90a2022-05-24 06:00:34 -07003107 quote! { static_assert( #actual_offset == #expected_offset); }
3108 });
Rosica Dejanovska8283fe62022-05-09 10:04:28 -07003109 Ok(quote! {
Kinuko Yasuda54b75d72022-08-18 10:09:54 -07003110 static_assert(sizeof(#tag_kind #namespace_qualifier #record_ident) == #cc_size);
Rosica Dejanovska8283fe62022-05-09 10:04:28 -07003111 static_assert(alignof(#tag_kind #namespace_qualifier #record_ident) == #alignment);
Googler5ea88642021-09-29 08:05:59 +00003112 #( #field_assertions )*
Rosica Dejanovska8283fe62022-05-09 10:04:28 -07003113 })
Googler5ea88642021-09-29 08:05:59 +00003114}
3115
Devin Jeanpierre58181ac2022-02-14 21:30:05 +00003116// Returns the accessor functions for no_unique_address member variables.
Devin Jeanpierre45b01962022-07-07 06:12:11 -07003117fn cc_struct_no_unique_address_impl(db: &Database, record: &Record) -> Result<TokenStream> {
Devin Jeanpierre58181ac2022-02-14 21:30:05 +00003118 let mut fields = vec![];
3119 let mut types = vec![];
3120 for field in &record.fields {
3121 if field.access != AccessSpecifier::Public || !field.is_no_unique_address {
3122 continue;
3123 }
Lukasz Anforowiczfea0db92022-05-17 17:28:04 -07003124 // Can't use `get_field_rs_type_for_layout` here, because we want to dig into
3125 // no_unique_address fields, despite laying them out as opaque blobs of bytes.
Devin Jeanpierre409f6f62022-07-07 02:00:26 -07003126 if let Ok(rs_type) = field.type_.as_ref().map(|t| t.rs_type.clone()) {
Lukasz Anforowiczfea0db92022-05-17 17:28:04 -07003127 fields.push(make_rs_ident(
3128 &field
3129 .identifier
3130 .as_ref()
3131 .expect("Unnamed fields can't be annotated with [[no_unique_address]]")
3132 .identifier,
3133 ));
Devin Jeanpierre409f6f62022-07-07 02:00:26 -07003134 types.push(db.rs_type_kind(rs_type).with_context(|| {
Lukasz Anforowiczfea0db92022-05-17 17:28:04 -07003135 format!("Failed to format type for field {:?} on record {:?}", field, record)
Devin Jeanpierre409f6f62022-07-07 02:00:26 -07003136 })?);
Lukasz Anforowiczfea0db92022-05-17 17:28:04 -07003137 }
Devin Jeanpierre58181ac2022-02-14 21:30:05 +00003138 }
3139
3140 if fields.is_empty() {
3141 return Ok(quote! {});
3142 }
3143
Lukasz Anforowicz4c3a2cc2022-03-11 00:24:49 +00003144 let ident = make_rs_ident(&record.rs_name);
Devin Jeanpierre58181ac2022-02-14 21:30:05 +00003145 Ok(quote! {
3146 impl #ident {
3147 #(
3148 pub fn #fields(&self) -> &#types {
3149 unsafe {&* (&self.#fields as *const _ as *const #types)}
3150 }
3151 )*
3152 }
3153 })
3154}
3155
Devin Jeanpierre56777022022-02-03 01:57:15 +00003156/// Returns the implementation of base class conversions, for converting a type
3157/// to its unambiguous public base classes.
Lukasz Anforowicz90bb7462022-09-01 08:13:28 -07003158fn cc_struct_upcast_impl(record: &Rc<Record>, ir: &IR) -> Result<GeneratedItem> {
Devin Jeanpierre56777022022-02-03 01:57:15 +00003159 let mut impls = Vec::with_capacity(record.unambiguous_public_bases.len());
Devin Jeanpierref99db3e2022-04-27 23:10:33 -07003160 let mut thunks = vec![];
3161 let mut cc_impls = vec![];
Devin Jeanpierre56777022022-02-03 01:57:15 +00003162 for base in &record.unambiguous_public_bases {
Devin Jeanpierre8cd8ae72022-06-29 18:59:40 -07003163 let base_record: &Rc<Record> = ir
Lukasz Anforowicz44047252022-03-23 13:04:48 +00003164 .find_decl(base.base_record_id)
3165 .with_context(|| format!("Can't find a base record of {:?}", record))?;
Devin Jeanpierre8cd8ae72022-06-29 18:59:40 -07003166 let base_name = RsTypeKind::new_record(base_record.clone(), ir)?.into_token_stream();
Lukasz Anforowicz90bb7462022-09-01 08:13:28 -07003167 let derived_name = RsTypeKind::new_record(record.clone(), ir)?.into_token_stream();
Devin Jeanpierref99db3e2022-04-27 23:10:33 -07003168 let body;
Devin Jeanpierre56777022022-02-03 01:57:15 +00003169 if let Some(offset) = base.offset {
3170 let offset = Literal::i64_unsuffixed(offset);
Devin Jeanpierreb368e682022-05-03 02:23:44 -07003171 body = quote! {(derived as *const _ as *const u8).offset(#offset) as *const #base_name};
Devin Jeanpierref99db3e2022-04-27 23:10:33 -07003172 } else {
Devin Jeanpierref99db3e2022-04-27 23:10:33 -07003173 let cast_fn_name = make_rs_ident(&format!(
3174 "__crubit_dynamic_upcast__{}__to__{}",
Lukasz Anforowicz3f133972022-09-01 09:01:02 -07003175 record.mangled_cc_name, base_record.mangled_cc_name
Devin Jeanpierref99db3e2022-04-27 23:10:33 -07003176 ));
Lukasz Anforowicz90bb7462022-09-01 08:13:28 -07003177 let base_cc_name = cc_type_name_for_record(base_record.as_ref(), ir)?;
3178 let derived_cc_name = cc_type_name_for_record(record.as_ref(), ir)?;
Devin Jeanpierref99db3e2022-04-27 23:10:33 -07003179 cc_impls.push(quote! {
3180 extern "C" const #base_cc_name& #cast_fn_name(const #derived_cc_name& from) {
3181 return from;
Devin Jeanpierre56777022022-02-03 01:57:15 +00003182 }
3183 });
Devin Jeanpierref99db3e2022-04-27 23:10:33 -07003184 thunks.push(quote! {
3185 pub fn #cast_fn_name (from: *const #derived_name) -> *const #base_name;
3186 });
3187 body = quote! {
Rosica Dejanovska68c11ac2022-05-12 03:48:59 -07003188 crate::detail::#cast_fn_name(derived)
Devin Jeanpierref99db3e2022-04-27 23:10:33 -07003189 };
Devin Jeanpierre56777022022-02-03 01:57:15 +00003190 }
Devin Jeanpierref99db3e2022-04-27 23:10:33 -07003191 impls.push(quote! {
Devin Jeanpierreb368e682022-05-03 02:23:44 -07003192 unsafe impl oops::Inherits<#base_name> for #derived_name {
3193 unsafe fn upcast_ptr(derived: *const Self) -> *const #base_name {
3194 #body
Devin Jeanpierref99db3e2022-04-27 23:10:33 -07003195 }
3196 }
3197 });
Devin Jeanpierre56777022022-02-03 01:57:15 +00003198 }
3199
Devin Jeanpierre8763a8e2022-04-26 15:45:13 -07003200 Ok(GeneratedItem {
Devin Jeanpierref99db3e2022-04-27 23:10:33 -07003201 item: quote! {#(#impls)*},
3202 thunks: quote! {#(#thunks)*},
3203 thunk_impls: quote! {#(#cc_impls)*},
Devin Jeanpierre8763a8e2022-04-26 15:45:13 -07003204 ..Default::default()
Devin Jeanpierre56777022022-02-03 01:57:15 +00003205 })
3206}
3207
Googlera675ae02021-12-07 08:04:59 +00003208fn thunk_ident(func: &Func) -> Ident {
3209 format_ident!("__rust_thunk__{}", func.mangled_name)
Devin Jeanpierref2ec8712021-10-13 20:47:16 +00003210}
3211
Devin Jeanpierreb8ce2c12022-07-13 10:34:01 -07003212fn generate_rs_api_impl(db: &mut Database, crubit_support_path: &str) -> Result<TokenStream> {
Michael Forsterbee84482021-10-13 08:35:38 +00003213 // This function uses quote! to generate C++ source code out of convenience.
3214 // This is a bold idea so we have to continously evaluate if it still makes
3215 // sense or the cost of working around differences in Rust and C++ tokens is
3216 // greather than the value added.
Marcel Hlopko3164eee2021-08-24 20:09:22 +00003217 //
Michael Forsterbee84482021-10-13 08:35:38 +00003218 // See rs_bindings_from_cc/
3219 // token_stream_printer.rs for a list of supported placeholders.
Marcel Hlopko3164eee2021-08-24 20:09:22 +00003220 let mut thunks = vec![];
Devin Jeanpierreb8ce2c12022-07-13 10:34:01 -07003221 let ir = db.ir();
Michael Forster7ef80732021-10-01 18:12:19 +00003222 for func in ir.functions() {
Devin Jeanpierreb8ce2c12022-07-13 10:34:01 -07003223 if can_skip_cc_thunk(db, func) {
Marcel Hlopko3164eee2021-08-24 20:09:22 +00003224 continue;
3225 }
Devin Jeanpierree9be70a2022-07-25 08:58:54 -07003226 if db.generate_func(func.clone()).unwrap_or_default().is_none() {
3227 // No function was generated that will call this thunk.
3228 continue;
3229 }
Marcel Hlopko3164eee2021-08-24 20:09:22 +00003230
Googlera675ae02021-12-07 08:04:59 +00003231 let thunk_ident = thunk_ident(func);
Devin Jeanpierref2ec8712021-10-13 20:47:16 +00003232 let implementation_function = match &func.name {
Lukasz Anforowicz9c663ca2022-02-09 01:33:31 +00003233 UnqualifiedIdentifier::Operator(op) => {
3234 let name = syn::parse_str::<TokenStream>(&op.name)?;
3235 quote! { operator #name }
3236 }
Devin Jeanpierref2ec8712021-10-13 20:47:16 +00003237 UnqualifiedIdentifier::Identifier(id) => {
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +00003238 let fn_ident = format_cc_ident(&id.identifier);
Rosica Dejanovskaa08b3862022-06-02 08:22:49 -07003239 match func.member_func_metadata.as_ref() {
Lukasz Anforowiczaab8ad22021-12-19 20:29:26 +00003240 Some(meta) => {
Rosica Dejanovskaa08b3862022-06-02 08:22:49 -07003241 if let Some(_) = meta.instance_method_metadata {
3242 quote! { #fn_ident }
3243 } else {
Devin Jeanpierreab85d442022-06-29 19:16:41 -07003244 let record: &Rc<Record> = ir.find_decl(meta.record_id)?;
Rosica Dejanovskaa08b3862022-06-02 08:22:49 -07003245 let record_ident = format_cc_ident(&record.cc_name);
Lukasz Anforowicz92be29f2022-09-02 17:12:29 -07003246 let namespace_qualifier = NamespaceQualifier::new(record.id, &ir)?.format_for_cc();
Lukasz Anforowicz4e2e0162022-09-01 07:07:32 -07003247 quote! { #namespace_qualifier #record_ident :: #fn_ident }
Rosica Dejanovskaa08b3862022-06-02 08:22:49 -07003248 }
3249 }
3250 None => {
Lukasz Anforowicz92be29f2022-09-02 17:12:29 -07003251 let namespace_qualifier = NamespaceQualifier::new(func.id, &ir)?.format_for_cc();
Lukasz Anforowicz4e2e0162022-09-01 07:07:32 -07003252 quote! { #namespace_qualifier #fn_ident }
Lukasz Anforowiczaab8ad22021-12-19 20:29:26 +00003253 }
3254 }
Devin Jeanpierref2ec8712021-10-13 20:47:16 +00003255 }
Lukasz Anforowicz7b0042d2022-01-06 23:00:19 +00003256 // Use `destroy_at` to avoid needing to spell out the class name. Destructor identiifers
Devin Jeanpierrecc6cf092021-12-16 04:31:14 +00003257 // use the name of the type itself, without namespace qualification, template
3258 // parameters, or aliases. We do not need to use that naming scheme anywhere else in
3259 // the bindings, and it can be difficult (impossible?) to spell in the general case. By
3260 // using destroy_at, we avoid needing to determine or remember what the correct spelling
Lukasz Anforowicz7b0042d2022-01-06 23:00:19 +00003261 // is. Similar arguments apply to `construct_at`.
Lukasz Anforowicze643ec92021-12-22 15:45:15 +00003262 UnqualifiedIdentifier::Constructor => {
Lukasz Anforowicz23171542022-04-11 15:26:17 -07003263 quote! { crubit::construct_at }
Lukasz Anforowicze643ec92021-12-22 15:45:15 +00003264 }
Devin Jeanpierref2ec8712021-10-13 20:47:16 +00003265 UnqualifiedIdentifier::Destructor => quote! {std::destroy_at},
Devin Jeanpierref2ec8712021-10-13 20:47:16 +00003266 };
Marcel Hlopko3164eee2021-08-24 20:09:22 +00003267
Devin Jeanpierreb8ce2c12022-07-13 10:34:01 -07003268 let mut param_idents =
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +00003269 func.params.iter().map(|p| format_cc_ident(&p.identifier.identifier)).collect_vec();
Marcel Hlopko3164eee2021-08-24 20:09:22 +00003270
Devin Jeanpierreb8ce2c12022-07-13 10:34:01 -07003271 let mut param_types = func
Devin Jeanpierre09c6f452021-09-29 07:34:24 +00003272 .params
3273 .iter()
Devin Jeanpierre11ce2b92022-07-27 07:54:21 -07003274 .map(|p| {
3275 let formatted = format_cc_type(&p.type_.cc_type, &ir)?;
3276 if !db.rs_type_kind(p.type_.rs_type.clone())?.is_unpin() {
3277 // non-Unpin types are wrapped by a pointer in the thunk.
3278 Ok(quote! {#formatted *})
3279 } else {
3280 Ok(formatted)
3281 }
3282 })
Devin Jeanpierre09c6f452021-09-29 07:34:24 +00003283 .collect::<Result<Vec<_>>>()?;
Marcel Hlopko3164eee2021-08-24 20:09:22 +00003284
Devin Jeanpierre11ce2b92022-07-27 07:54:21 -07003285 let arg_expressions = func
Devin Jeanpierrefd4ff822022-07-15 01:43:55 -07003286 .params
Devin Jeanpierreb8ce2c12022-07-13 10:34:01 -07003287 .iter()
Lukasz Anforowicz07b33902022-07-13 15:27:03 -07003288 .map(|p| {
3289 let ident = format_cc_ident(&p.identifier.identifier);
3290 match p.type_.cc_type.name.as_deref() {
Devin Jeanpierre11ce2b92022-07-27 07:54:21 -07003291 Some("&") => Ok(quote! { * #ident }),
3292 Some("&&") => Ok(quote! { std::move(* #ident) }),
3293 _ => {
3294 // non-Unpin types are wrapped by a pointer in the thunk.
3295 if !db.rs_type_kind(p.type_.rs_type.clone())?.is_unpin() {
3296 Ok(quote! { std::move(* #ident) })
3297 } else {
3298 Ok(quote! { #ident })
3299 }
3300 }
Lukasz Anforowicz07b33902022-07-13 15:27:03 -07003301 }
3302 })
Devin Jeanpierre11ce2b92022-07-27 07:54:21 -07003303 .collect::<Result<Vec<_>>>()?;
Devin Jeanpierreb8ce2c12022-07-13 10:34:01 -07003304
3305 // Here, we add a __return parameter if the return type is not trivially
3306 // relocatable. (We do this after the arg_expressions computation, so
3307 // that it's only in the parameter list, not the argument list.)
3308 //
3309 // RsTypeKind is where, as much as anywhere, where the information about trivial
3310 // relocatability is stored.
3311 let is_trivial_return = db.rs_type_kind(func.return_type.rs_type.clone())?.is_unpin();
3312 let mut return_type_name = format_cc_type(&func.return_type.cc_type, &ir)?;
3313 if !is_trivial_return {
3314 param_idents.insert(0, format_cc_ident("__return"));
3315 param_types.insert(0, quote! {#return_type_name *});
3316 return_type_name = quote! {void};
3317 }
3318
Lukasz Anforowiczb3d89aa2022-01-12 14:35:52 +00003319 let needs_this_deref = match &func.member_func_metadata {
3320 None => false,
3321 Some(meta) => match &func.name {
3322 UnqualifiedIdentifier::Constructor | UnqualifiedIdentifier::Destructor => false,
Marcel Hlopko14ee3c82022-02-09 09:46:23 +00003323 UnqualifiedIdentifier::Identifier(_) | UnqualifiedIdentifier::Operator(_) => {
3324 meta.instance_method_metadata.is_some()
3325 }
Lukasz Anforowiczb3d89aa2022-01-12 14:35:52 +00003326 },
3327 };
3328 let (implementation_function, arg_expressions) = if !needs_this_deref {
Devin Jeanpierredeea7892022-03-29 02:13:31 -07003329 (implementation_function, arg_expressions.clone())
Lukasz Anforowiczb3d89aa2022-01-12 14:35:52 +00003330 } else {
3331 let this_param = func
3332 .params
3333 .first()
3334 .ok_or_else(|| anyhow!("Instance methods must have `__this` param."))?;
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +00003335 let this_arg = format_cc_ident(&this_param.identifier.identifier);
Lukasz Anforowiczb3d89aa2022-01-12 14:35:52 +00003336 (
3337 quote! { #this_arg -> #implementation_function},
Devin Jeanpierredeea7892022-03-29 02:13:31 -07003338 arg_expressions.iter().skip(1).cloned().collect_vec(),
Lukasz Anforowiczb3d89aa2022-01-12 14:35:52 +00003339 )
3340 };
3341
Devin Jeanpierreb8ce2c12022-07-13 10:34:01 -07003342 let return_expr = quote! {#implementation_function( #( #arg_expressions ),* )};
3343 let return_stmt = if !is_trivial_return {
Devin Jeanpierre521b4ee2022-07-27 07:19:50 -07003344 // Explicitly use placement new so that we get guaranteed copy elision in C++17.
Devin Jeanpierreb8ce2c12022-07-13 10:34:01 -07003345 let out_param = &param_idents[0];
Devin Jeanpierre521b4ee2022-07-27 07:19:50 -07003346 quote! {new(#out_param) auto(#return_expr)}
Devin Jeanpierreb8ce2c12022-07-13 10:34:01 -07003347 } else {
Lukasz Anforowicz07b33902022-07-13 15:27:03 -07003348 match func.return_type.cc_type.name.as_deref() {
3349 Some("void") => return_expr,
3350 Some("&") => quote! { return & #return_expr },
3351 Some("&&") => {
3352 // The code below replicates bits of `format_cc_type`, but formats an rvalue
3353 // reference (which `format_cc_type` would format as a pointer).
3354 // `const_fragment` from `format_cc_type` is ignored - it is not applicable for
3355 // references.
3356 let ty = &func.return_type.cc_type;
3357 if ty.type_args.len() != 1 {
3358 bail!("Invalid reference type (need exactly 1 type argument): {:?}", ty);
3359 }
3360 let nested_type = format_cc_type(&ty.type_args[0], &ir)?;
3361 quote! {
3362 #nested_type && lvalue = #return_expr;
3363 return &lvalue
3364 }
Devin Jeanpierrefd4ff822022-07-15 01:43:55 -07003365 }
Lukasz Anforowicz07b33902022-07-13 15:27:03 -07003366 _ => quote! { return #return_expr },
3367 }
Devin Jeanpierreb8ce2c12022-07-13 10:34:01 -07003368 };
3369
Marcel Hlopko3164eee2021-08-24 20:09:22 +00003370 thunks.push(quote! {
3371 extern "C" #return_type_name #thunk_ident( #( #param_types #param_idents ),* ) {
Devin Jeanpierreb8ce2c12022-07-13 10:34:01 -07003372 #return_stmt;
Marcel Hlopko3164eee2021-08-24 20:09:22 +00003373 }
3374 });
3375 }
3376
Rosica Dejanovska8283fe62022-05-09 10:04:28 -07003377 let layout_assertions = ir
3378 .records()
Devin Jeanpierreb8ce2c12022-07-13 10:34:01 -07003379 .map(|record| cc_struct_layout_assertion(record, &ir))
Rosica Dejanovska8283fe62022-05-09 10:04:28 -07003380 .collect::<Result<Vec<_>>>()?;
Googler5ea88642021-09-29 08:05:59 +00003381
Devin Jeanpierre231ef8d2021-10-27 10:50:44 +00003382 let mut standard_headers = <BTreeSet<Ident>>::new();
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +00003383 standard_headers.insert(format_ident!("memory")); // ubiquitous.
Devin Jeanpierre231ef8d2021-10-27 10:50:44 +00003384 if ir.records().next().is_some() {
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +00003385 standard_headers.insert(format_ident!("cstddef"));
Devin Jeanpierre231ef8d2021-10-27 10:50:44 +00003386 };
Googler5ea88642021-09-29 08:05:59 +00003387
Lukasz Anforowiczdd907702022-05-06 09:24:07 -07003388 let mut includes = vec!["cxx20_backports.h", "offsetof.h"]
3389 .into_iter()
3390 .map(|hdr| format!("{}/{}", crubit_support_path, hdr))
3391 .collect_vec();
Lukasz Anforowicz4457baf2021-12-23 17:24:04 +00003392
Michael Forsterbee84482021-10-13 08:35:38 +00003393 // In order to generate C++ thunk in all the cases Clang needs to be able to
3394 // access declarations from public headers of the C++ library.
Lukasz Anforowiczdd907702022-05-06 09:24:07 -07003395 includes.extend(ir.used_headers().map(|hdr| hdr.name.clone()));
Marcel Hlopko3164eee2021-08-24 20:09:22 +00003396
Marcel Hlopko89547752021-12-10 09:39:41 +00003397 Ok(quote! {
Googler5ea88642021-09-29 08:05:59 +00003398 #( __HASH_TOKEN__ include <#standard_headers> __NEWLINE__)*
Devin Jeanpierre7c74f842022-02-03 07:08:06 +00003399 __NEWLINE__
Michael Forsterdb8101a2021-10-08 06:56:03 +00003400 #( __HASH_TOKEN__ include #includes __NEWLINE__)* __NEWLINE__
Marcel Hlopkob8069ae2022-02-19 09:31:00 +00003401 __HASH_TOKEN__ pragma clang diagnostic push __NEWLINE__
3402 // Disable Clang thread-safety-analysis warnings that would otherwise
3403 // complain about thunks that call mutex locking functions in an unpaired way.
3404 __HASH_TOKEN__ pragma clang diagnostic ignored "-Wthread-safety-analysis" __NEWLINE__
Marcel Hlopko3164eee2021-08-24 20:09:22 +00003405
Michael Forsterdb8101a2021-10-08 06:56:03 +00003406 #( #thunks )* __NEWLINE__ __NEWLINE__
Googler5ea88642021-09-29 08:05:59 +00003407
Michael Forsterdb8101a2021-10-08 06:56:03 +00003408 #( #layout_assertions __NEWLINE__ __NEWLINE__ )*
Marcel Hlopkoc6b726c2021-10-07 06:53:09 +00003409
Marcel Hlopkob8069ae2022-02-19 09:31:00 +00003410 __NEWLINE__
3411 __HASH_TOKEN__ pragma clang diagnostic pop __NEWLINE__
Marcel Hlopkoc6b726c2021-10-07 06:53:09 +00003412 // To satisfy http://cs/symbol:devtools.metadata.Presubmit.CheckTerminatingNewline check.
3413 __NEWLINE__
Marcel Hlopko89547752021-12-10 09:39:41 +00003414 })
Marcel Hlopko3164eee2021-08-24 20:09:22 +00003415}
3416
Marcel Hlopko42abfc82021-08-09 07:03:17 +00003417#[cfg(test)]
3418mod tests {
Devin Jeanpierre45cb1162021-10-27 10:54:28 +00003419 use super::*;
Lukasz Anforowiczedabf002022-04-28 06:45:09 -07003420 use ir_testing::{
3421 ir_from_cc, ir_from_cc_dependency, ir_record, make_ir_from_items, retrieve_func,
Martin Brænnee5ba6b62022-06-23 07:38:40 -07003422 with_lifetime_macros,
Lukasz Anforowiczedabf002022-04-28 06:45:09 -07003423 };
Lukasz Anforowiczb4d17782022-07-07 08:09:13 -07003424 use static_assertions::{assert_impl_all, assert_not_impl_any};
Marcel Hlopko89547752021-12-10 09:39:41 +00003425 use token_stream_matchers::{
Lukasz Anforowicz71e9b592022-03-04 19:03:02 +00003426 assert_cc_matches, assert_cc_not_matches, assert_ir_matches, assert_rs_matches,
3427 assert_rs_not_matches,
Devin Jeanpierre7e9a1de2021-12-03 08:04:22 +00003428 };
Lukasz Anforowicz54ff3182022-05-06 07:17:58 -07003429 use token_stream_printer::{rs_tokens_to_formatted_string_for_tests, tokens_to_string};
Marcel Hlopko42abfc82021-08-09 07:03:17 +00003430
Devin Jeanpierree9850a72022-06-29 19:04:48 -07003431 fn generate_bindings_tokens(ir: Rc<IR>) -> Result<BindingsTokens> {
Lukasz Anforowiczdd907702022-05-06 09:24:07 -07003432 super::generate_bindings_tokens(ir, "crubit/rs_bindings_support")
3433 }
3434
Devin Jeanpierre409f6f62022-07-07 02:00:26 -07003435 fn db_from_cc(cc_src: &str) -> Result<Database> {
3436 let mut db = Database::default();
3437 db.set_ir(ir_from_cc(cc_src)?);
3438 Ok(db)
3439 }
3440
Marcel Hlopko42abfc82021-08-09 07:03:17 +00003441 #[test]
Marcel Hlopkob8069ae2022-02-19 09:31:00 +00003442 fn test_disable_thread_safety_warnings() -> Result<()> {
3443 let ir = ir_from_cc("inline void foo() {}")?;
Devin Jeanpierree9850a72022-06-29 19:04:48 -07003444 let rs_api_impl = generate_bindings_tokens(ir)?.rs_api_impl;
Marcel Hlopkob8069ae2022-02-19 09:31:00 +00003445 assert_cc_matches!(
3446 rs_api_impl,
3447 quote! {
3448 ...
3449 __HASH_TOKEN__ pragma clang diagnostic push
3450 __HASH_TOKEN__ pragma clang diagnostic ignored "-Wthread-safety-analysis"
3451 ...
3452
3453 __HASH_TOKEN__ pragma clang diagnostic pop
3454 ...
3455 }
3456 );
3457 Ok(())
3458 }
3459
3460 #[test]
Marcel Hlopko3b9bf9e2021-11-29 08:25:14 +00003461 // TODO(hlopko): Move this test to a more principled place where it can access
3462 // `ir_testing`.
3463 fn test_duplicate_decl_ids_err() {
3464 let mut r1 = ir_record("R1");
Lukasz Anforowicz14732b22022-06-02 09:11:08 -07003465 r1.id = ItemId::new_for_testing(42);
Marcel Hlopko3b9bf9e2021-11-29 08:25:14 +00003466 let mut r2 = ir_record("R2");
Lukasz Anforowicz14732b22022-06-02 09:11:08 -07003467 r2.id = ItemId::new_for_testing(42);
Marcel Hlopko3b9bf9e2021-11-29 08:25:14 +00003468 let result = make_ir_from_items([r1.into(), r2.into()]);
3469 assert!(result.is_err());
3470 assert!(result.unwrap_err().to_string().contains("Duplicate decl_id found in"));
3471 }
3472
3473 #[test]
Marcel Hlopko45fba972021-08-23 19:52:20 +00003474 fn test_simple_function() -> Result<()> {
Marcel Hlopko89547752021-12-10 09:39:41 +00003475 let ir = ir_from_cc("int Add(int a, int b);")?;
Devin Jeanpierree9850a72022-06-29 19:04:48 -07003476 let BindingsTokens { rs_api, rs_api_impl } = generate_bindings_tokens(ir)?;
Marcel Hlopko89547752021-12-10 09:39:41 +00003477 assert_rs_matches!(
3478 rs_api,
3479 quote! {
Michael Forsterdb8101a2021-10-08 06:56:03 +00003480 #[inline(always)]
Marcel Hlopko89547752021-12-10 09:39:41 +00003481 pub fn Add(a: i32, b: i32) -> i32 {
Rosica Dejanovska68c11ac2022-05-12 03:48:59 -07003482 unsafe { crate::detail::__rust_thunk___Z3Addii(a, b) }
Marcel Hlopko89547752021-12-10 09:39:41 +00003483 }
3484 }
3485 );
3486 assert_rs_matches!(
3487 rs_api,
3488 quote! {
Michael Forsterdb8101a2021-10-08 06:56:03 +00003489 mod detail {
Googler55647142022-01-11 12:37:39 +00003490 #[allow(unused_imports)]
Devin Jeanpierred4dde0e2021-10-13 20:48:25 +00003491 use super::*;
Michael Forsterdb8101a2021-10-08 06:56:03 +00003492 extern "C" {
3493 #[link_name = "_Z3Addii"]
Googlera675ae02021-12-07 08:04:59 +00003494 pub(crate) fn __rust_thunk___Z3Addii(a: i32, b: i32) -> i32;
Marcel Hlopko89547752021-12-10 09:39:41 +00003495 }
3496 }
3497 }
Marcel Hlopko42abfc82021-08-09 07:03:17 +00003498 );
Michael Forsterdb8101a2021-10-08 06:56:03 +00003499
Devin Jeanpierre4f06f832022-04-26 15:51:30 -07003500 assert_cc_not_matches!(rs_api_impl, quote! {__rust_thunk___Z3Addii});
Michael Forsterdb8101a2021-10-08 06:56:03 +00003501
Marcel Hlopko3164eee2021-08-24 20:09:22 +00003502 Ok(())
3503 }
3504
3505 #[test]
3506 fn test_inline_function() -> Result<()> {
Marcel Hlopko89547752021-12-10 09:39:41 +00003507 let ir = ir_from_cc("inline int Add(int a, int b);")?;
Devin Jeanpierree9850a72022-06-29 19:04:48 -07003508 let BindingsTokens { rs_api, rs_api_impl } = generate_bindings_tokens(ir)?;
Marcel Hlopko89547752021-12-10 09:39:41 +00003509 assert_rs_matches!(
3510 rs_api,
3511 quote! {
Michael Forsterdb8101a2021-10-08 06:56:03 +00003512 #[inline(always)]
Marcel Hlopko89547752021-12-10 09:39:41 +00003513 pub fn Add(a: i32, b: i32) -> i32 {
Rosica Dejanovska68c11ac2022-05-12 03:48:59 -07003514 unsafe { crate::detail::__rust_thunk___Z3Addii(a, b) }
Marcel Hlopko89547752021-12-10 09:39:41 +00003515 }
3516 }
3517 );
3518 assert_rs_matches!(
3519 rs_api,
3520 quote! {
Michael Forsterdb8101a2021-10-08 06:56:03 +00003521 mod detail {
Googler55647142022-01-11 12:37:39 +00003522 #[allow(unused_imports)]
Devin Jeanpierred4dde0e2021-10-13 20:48:25 +00003523 use super::*;
Michael Forsterdb8101a2021-10-08 06:56:03 +00003524 extern "C" {
Googlera675ae02021-12-07 08:04:59 +00003525 pub(crate) fn __rust_thunk___Z3Addii(a: i32, b: i32) -> i32;
Marcel Hlopko89547752021-12-10 09:39:41 +00003526 }
3527 }
3528 }
Marcel Hlopko3164eee2021-08-24 20:09:22 +00003529 );
3530
Marcel Hlopko89547752021-12-10 09:39:41 +00003531 assert_cc_matches!(
Devin Jeanpierre4f06f832022-04-26 15:51:30 -07003532 rs_api_impl,
Marcel Hlopko89547752021-12-10 09:39:41 +00003533 quote! {
Googlera675ae02021-12-07 08:04:59 +00003534 extern "C" int __rust_thunk___Z3Addii(int a, int b) {
Lukasz Anforowicz07b33902022-07-13 15:27:03 -07003535 return Add(a, b);
Marcel Hlopko3164eee2021-08-24 20:09:22 +00003536 }
Marcel Hlopko89547752021-12-10 09:39:41 +00003537 }
Marcel Hlopko3164eee2021-08-24 20:09:22 +00003538 );
Marcel Hlopko42abfc82021-08-09 07:03:17 +00003539 Ok(())
3540 }
Marcel Hlopkob4b28742021-09-15 12:45:20 +00003541
3542 #[test]
Marcel Hlopkoa0f38662021-12-03 08:45:26 +00003543 fn test_simple_function_with_types_from_other_target() -> Result<()> {
3544 let ir = ir_from_cc_dependency(
3545 "inline ReturnStruct DoSomething(ParamStruct param);",
Devin Jeanpierreb8ce2c12022-07-13 10:34:01 -07003546 "struct ReturnStruct final {}; struct ParamStruct final {};",
Marcel Hlopkoa0f38662021-12-03 08:45:26 +00003547 )?;
3548
Devin Jeanpierree9850a72022-06-29 19:04:48 -07003549 let BindingsTokens { rs_api, rs_api_impl } = generate_bindings_tokens(ir)?;
Marcel Hlopko89547752021-12-10 09:39:41 +00003550 assert_rs_matches!(
3551 rs_api,
3552 quote! {
Marcel Hlopkoa0f38662021-12-03 08:45:26 +00003553 #[inline(always)]
3554 pub fn DoSomething(param: dependency::ParamStruct)
Marcel Hlopko89547752021-12-10 09:39:41 +00003555 -> dependency::ReturnStruct {
Rosica Dejanovska68c11ac2022-05-12 03:48:59 -07003556 unsafe { crate::detail::__rust_thunk___Z11DoSomething11ParamStruct(param) }
Marcel Hlopko89547752021-12-10 09:39:41 +00003557 }
3558 }
3559 );
3560 assert_rs_matches!(
3561 rs_api,
3562 quote! {
3563 mod detail {
Googler55647142022-01-11 12:37:39 +00003564 #[allow(unused_imports)]
Marcel Hlopko89547752021-12-10 09:39:41 +00003565 use super::*;
3566 extern "C" {
Lukasz Anforowicz07b33902022-07-13 15:27:03 -07003567 pub(crate) fn __rust_thunk___Z11DoSomething11ParamStruct(
3568 param: dependency::ParamStruct) -> dependency::ReturnStruct;
Marcel Hlopko89547752021-12-10 09:39:41 +00003569 }
3570 }}
Marcel Hlopkoa0f38662021-12-03 08:45:26 +00003571 );
3572
Marcel Hlopko89547752021-12-10 09:39:41 +00003573 assert_cc_matches!(
Devin Jeanpierre4f06f832022-04-26 15:51:30 -07003574 rs_api_impl,
Marcel Hlopko89547752021-12-10 09:39:41 +00003575 quote! {
Lukasz Anforowicz07b33902022-07-13 15:27:03 -07003576 extern "C" struct ReturnStruct __rust_thunk___Z11DoSomething11ParamStruct(
3577 struct ParamStruct param) {
3578 return DoSomething(param);
Marcel Hlopkoa0f38662021-12-03 08:45:26 +00003579 }
Marcel Hlopko89547752021-12-10 09:39:41 +00003580 }
Marcel Hlopkoa0f38662021-12-03 08:45:26 +00003581 );
3582 Ok(())
3583 }
3584
3585 #[test]
Lukasz Anforowiczb1ff2e52022-05-16 10:54:23 -07003586 fn test_template_in_dependency_and_alias_in_current_target() -> Result<()> {
3587 // See also the test with the same name in `ir_from_cc_test.rs`.
3588 let ir = {
3589 let dependency_src = r#" #pragma clang lifetime_elision
3590 template <typename T>
3591 struct MyTemplate {
3592 T GetValue() { return field; }
3593 T field;
3594 }; "#;
3595 let current_target_src = r#" #pragma clang lifetime_elision
3596 using MyAliasOfTemplate = MyTemplate<int>; "#;
3597 ir_from_cc_dependency(current_target_src, dependency_src)?
3598 };
3599
Devin Jeanpierree9850a72022-06-29 19:04:48 -07003600 let BindingsTokens { rs_api, rs_api_impl } = generate_bindings_tokens(ir)?;
Lukasz Anforowiczb1ff2e52022-05-16 10:54:23 -07003601 assert_rs_matches!(
3602 rs_api,
3603 quote! {
3604 #[repr(C)]
3605 pub struct __CcTemplateInst10MyTemplateIiE {
3606 pub field: i32,
3607 }
3608 }
3609 );
3610 assert_rs_matches!(
3611 rs_api,
3612 quote! {
3613 impl __CcTemplateInst10MyTemplateIiE {
3614 #[inline(always)]
3615 pub fn GetValue<'a>(self: ... Pin<&'a mut Self>) -> i32 { unsafe {
Lukasz Anforowicz8d1e4322022-06-08 07:56:06 -07003616 crate::detail::__rust_thunk___ZN10MyTemplateIiE8GetValueEv__2f_2ftest_3atesting_5ftarget(
Lukasz Anforowiczb1ff2e52022-05-16 10:54:23 -07003617 self)
3618 }}
3619 }
3620 }
3621 );
3622 assert_rs_matches!(
3623 rs_api,
3624 quote! {
3625 pub type MyAliasOfTemplate = crate::__CcTemplateInst10MyTemplateIiE;
3626 }
3627 );
3628 assert_rs_matches!(
3629 rs_api,
3630 quote! {
Lukasz Anforowicz8d1e4322022-06-08 07:56:06 -07003631 mod detail { ... extern "C" {
Lukasz Anforowiczb1ff2e52022-05-16 10:54:23 -07003632 ...
Lukasz Anforowicz8d1e4322022-06-08 07:56:06 -07003633 pub(crate) fn
3634 __rust_thunk___ZN10MyTemplateIiE8GetValueEv__2f_2ftest_3atesting_5ftarget<'a>(
3635 __this: ... Pin<&'a mut crate::__CcTemplateInst10MyTemplateIiE>
3636 ) -> i32;
3637 ...
3638 } }
Lukasz Anforowiczb1ff2e52022-05-16 10:54:23 -07003639 }
3640 );
3641 assert_cc_matches!(
3642 rs_api_impl,
3643 quote! {
Lukasz Anforowicz8d1e4322022-06-08 07:56:06 -07003644 extern "C"
3645 int __rust_thunk___ZN10MyTemplateIiE8GetValueEv__2f_2ftest_3atesting_5ftarget(
Lukasz Anforowiczd4742ff2022-07-11 17:05:02 -07003646 struct MyTemplate<int>* __this) {
Lukasz Anforowiczb1ff2e52022-05-16 10:54:23 -07003647 return __this->GetValue();
3648 }
3649 }
3650 );
3651
3652 Ok(())
3653 }
3654
3655 #[test]
3656 fn test_template_with_out_of_line_definition() -> Result<()> {
3657 // See also an end-to-end test in the `test/templates/out_of_line_definition`
3658 // directory.
3659 let ir = ir_from_cc(
3660 r#" #pragma clang lifetime_elision
3661 template <typename T>
3662 class MyTemplate final {
3663 public:
3664 static MyTemplate Create(T value);
3665 const T& value() const;
3666
3667 private:
3668 T value_;
3669 };
3670
3671 using MyTypeAlias = MyTemplate<int>; "#,
3672 )?;
3673
Devin Jeanpierree9850a72022-06-29 19:04:48 -07003674 let BindingsTokens { rs_api_impl, .. } = generate_bindings_tokens(ir)?;
Lukasz Anforowiczb1ff2e52022-05-16 10:54:23 -07003675
3676 // Even though the member functions above are *not* defined inline (e.g.
3677 // IR::Func::is_inline is false), they still need to have thunks generated for
3678 // them (to force/guarantee that the class template and its members get
3679 // instantiated). This is also covered in the following end-to-end
3680 // tests:
3681 // - test/templates/out_of_line_definition/ - without a thunk, the template
3682 // won't be instantiated and Rust bindings won't be able to call the member
3683 // function (there will be no instantiation of the member function in the C++
3684 // object files)
3685 // - test/templates/definition_in_cc/ - the instantiation happens in the .cc
3686 // file and therefore the thunk is not *required* (but it doesn't hurt to have
3687 // the thunk)
3688 assert_cc_matches!(
3689 rs_api_impl,
3690 quote! {
3691 extern "C" class MyTemplate<int>
Lukasz Anforowicz8d1e4322022-06-08 07:56:06 -07003692 __rust_thunk___ZN10MyTemplateIiE6CreateEi__2f_2ftest_3atesting_5ftarget(
3693 int value) {
Lukasz Anforowicz07b33902022-07-13 15:27:03 -07003694 return MyTemplate<int>::Create(value);
Lukasz Anforowiczb1ff2e52022-05-16 10:54:23 -07003695 }
3696 }
3697 );
3698 assert_cc_matches!(
3699 rs_api_impl,
3700 quote! {
Lukasz Anforowicz07b33902022-07-13 15:27:03 -07003701 extern "C" int const*
Lukasz Anforowicz8d1e4322022-06-08 07:56:06 -07003702 __rust_thunk___ZNK10MyTemplateIiE5valueEv__2f_2ftest_3atesting_5ftarget(
Lukasz Anforowiczb1ff2e52022-05-16 10:54:23 -07003703 const class MyTemplate<int>*__this) {
Lukasz Anforowicz07b33902022-07-13 15:27:03 -07003704 return &__this->value();
Lukasz Anforowiczb1ff2e52022-05-16 10:54:23 -07003705 }
3706 }
3707 );
3708 Ok(())
3709 }
3710
3711 #[test]
Marcel Hlopkob4b28742021-09-15 12:45:20 +00003712 fn test_simple_struct() -> Result<()> {
Devin Jeanpierree9be70a2022-07-25 08:58:54 -07003713 let ir = ir_from_cc(
3714 r#"
3715 #pragma clang lifetime_elision
Devin Jeanpierre88343c72022-01-15 01:10:23 +00003716 struct SomeStruct final {
Devin Jeanpierree9be70a2022-07-25 08:58:54 -07003717 ~SomeStruct() {}
Marcel Hlopko89547752021-12-10 09:39:41 +00003718 int public_int;
3719 protected:
3720 int protected_int;
3721 private:
3722 int private_int;
3723 };
Devin Jeanpierree9be70a2022-07-25 08:58:54 -07003724 "#,
3725 )?;
Michael Forster028800b2021-10-05 12:39:59 +00003726
Devin Jeanpierree9850a72022-06-29 19:04:48 -07003727 let BindingsTokens { rs_api, rs_api_impl } = generate_bindings_tokens(ir)?;
Marcel Hlopko89547752021-12-10 09:39:41 +00003728 assert_rs_matches!(
3729 rs_api,
3730 quote! {
Devin Jeanpierree9be70a2022-07-25 08:58:54 -07003731 #[::ctor::recursively_pinned(PinnedDrop)]
Lukasz Anforowiczdf8fcae2022-06-02 14:54:43 -07003732 #[repr(C, align(4))]
Michael Forsterdb8101a2021-10-08 06:56:03 +00003733 pub struct SomeStruct {
Devin Jeanpierrecd5ebf32022-07-08 05:31:55 -07003734 __non_field_data: [::std::mem::MaybeUninit<u8>; 0],
Michael Forsterdb8101a2021-10-08 06:56:03 +00003735 pub public_int: i32,
Lukasz Anforowiczdf8fcae2022-06-02 14:54:43 -07003736 #[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 -07003737 pub(crate) protected_int: [::std::mem::MaybeUninit<u8>; 4],
Lukasz Anforowiczdf8fcae2022-06-02 14:54:43 -07003738 #[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 -07003739 pub(crate) private_int: [::std::mem::MaybeUninit<u8>; 4],
Marcel Hlopko89547752021-12-10 09:39:41 +00003740 }
3741 }
3742 );
3743 assert_rs_matches!(
3744 rs_api,
3745 quote! {
Devin Jeanpierrecd5ebf32022-07-08 05:31:55 -07003746 const _: () = assert!(::std::mem::size_of::<Option<&i32>>() == ::std::mem::size_of::<&i32>());
3747 const _: () = assert!(::std::mem::size_of::<crate::SomeStruct>() == 12);
3748 const _: () = assert!(::std::mem::align_of::<crate::SomeStruct>() == 4);
Devin Jeanpierree9be70a2022-07-25 08:58:54 -07003749 const _: () = { static_assertions::assert_not_impl_any!(crate::SomeStruct: Copy); };
3750 const _: () = { static_assertions::assert_impl_all!(crate::SomeStruct: Drop); };
Lukasz Anforowicz30360ba2022-05-23 12:15:12 -07003751 const _: () = assert!(memoffset_unstable_const::offset_of!(crate::SomeStruct, public_int) == 0);
3752 const _: () = assert!(memoffset_unstable_const::offset_of!(crate::SomeStruct, protected_int) == 4);
3753 const _: () = assert!(memoffset_unstable_const::offset_of!(crate::SomeStruct, private_int) == 8);
Marcel Hlopko89547752021-12-10 09:39:41 +00003754 }
Marcel Hlopkob4b28742021-09-15 12:45:20 +00003755 );
Marcel Hlopko89547752021-12-10 09:39:41 +00003756 assert_cc_matches!(
3757 rs_api_impl,
3758 quote! {
Lukasz Anforowiczd4742ff2022-07-11 17:05:02 -07003759 extern "C" void __rust_thunk___ZN10SomeStructD1Ev(struct SomeStruct * __this) {
Lukasz Anforowicz07b33902022-07-13 15:27:03 -07003760 std::destroy_at(__this);
Marcel Hlopko89547752021-12-10 09:39:41 +00003761 }
3762 }
3763 );
3764 assert_cc_matches!(
3765 rs_api_impl,
3766 quote! {
Lukasz Anforowiczd4742ff2022-07-11 17:05:02 -07003767 static_assert(sizeof(struct SomeStruct) == 12);
3768 static_assert(alignof(struct SomeStruct) == 4);
3769 static_assert(CRUBIT_OFFSET_OF(public_int, struct SomeStruct) == 0);
Marcel Hlopko89547752021-12-10 09:39:41 +00003770 }
Googler5ea88642021-09-29 08:05:59 +00003771 );
Marcel Hlopkob4b28742021-09-15 12:45:20 +00003772 Ok(())
3773 }
Devin Jeanpierre7a7328e2021-09-17 07:10:08 +00003774
Devin Jeanpierre2ed14ec2021-10-06 11:32:19 +00003775 #[test]
Lukasz Anforowiczd4742ff2022-07-11 17:05:02 -07003776 fn test_struct_vs_class() -> Result<()> {
Devin Jeanpierree9be70a2022-07-25 08:58:54 -07003777 let ir = ir_from_cc(
3778 r#"
3779 #pragma clang lifetime_elision
3780 struct SomeStruct final {
3781 SomeStruct() {}
3782 int field;
3783 };
3784 class SomeClass final {
3785 public:
3786 SomeClass() {}
3787 int field;
3788 };
3789 "#,
3790 )?;
Lukasz Anforowiczd4742ff2022-07-11 17:05:02 -07003791 let BindingsTokens { rs_api, rs_api_impl } = generate_bindings_tokens(ir)?;
3792
3793 // A Rust `struct` is generated for both `SomeStruct` and `SomeClass`.
3794 assert_rs_matches!(rs_api, quote! { pub struct SomeStruct },);
3795 assert_rs_matches!(rs_api, quote! { pub struct SomeClass },);
3796
3797 // But in C++ we still should refer to `struct SomeStruct` and `class
3798 // SomeClass`. See also b/238212337.
3799 assert_cc_matches!(rs_api_impl, quote! { struct SomeStruct * __this });
3800 assert_cc_matches!(rs_api_impl, quote! { class SomeClass * __this });
3801 assert_cc_matches!(rs_api_impl, quote! { static_assert(sizeof(struct SomeStruct) == 4); });
3802 assert_cc_matches!(rs_api_impl, quote! { static_assert(sizeof(class SomeClass) == 4); });
3803 Ok(())
3804 }
3805
3806 #[test]
Kinuko Yasuda8dd84642022-08-17 09:19:47 -07003807 fn test_struct_vs_typedefed_struct() -> Result<()> {
3808 let ir = ir_from_cc(
3809 r#"
3810 #pragma clang lifetime_elision
3811 struct SomeStruct final {
3812 int x;
3813 } __attribute__((aligned(16)));
3814 typedef struct {
3815 int x;
3816 } SomeAnonStruct __attribute__((aligned(16)));
3817 "#,
3818 )?;
3819 let BindingsTokens { rs_api, rs_api_impl } = generate_bindings_tokens(ir)?;
3820
3821 // A `struct` is generated for both `SomeStruct` and `SomeAnonStruct`, both
3822 // in Rust and in C++.
3823 assert_rs_matches!(rs_api, quote! { pub struct SomeStruct },);
3824 assert_rs_matches!(rs_api, quote! { pub struct SomeAnonStruct },);
3825 assert_rs_matches!(rs_api_impl, quote! { struct SomeStruct * __this },);
3826 assert_rs_matches!(rs_api_impl, quote! { SomeAnonStruct * __this },);
3827
3828 // In C++, both have align == 16, but size for `SomeAnonStruct` is not aligned.
3829 // `SomeAnonStruct` won't have `struct` in the assert.
3830 assert_cc_matches!(
3831 rs_api_impl,
3832 quote! { static_assert(alignof(struct SomeStruct) == 16); }
3833 );
3834 assert_cc_matches!(rs_api_impl, quote! { static_assert(alignof(SomeAnonStruct) == 16); });
3835 assert_cc_matches!(rs_api_impl, quote! { static_assert(sizeof(struct SomeStruct) == 16); });
3836 assert_cc_matches!(rs_api_impl, quote! { static_assert(sizeof(SomeAnonStruct) == 4); });
3837
3838 // In Rust, both have align == 16 and size == 16.
3839 assert_rs_matches!(
3840 rs_api,
3841 quote! { assert!(::std::mem::size_of::<crate::SomeStruct>() == 16); }
3842 );
3843 assert_rs_matches!(
3844 rs_api,
3845 quote! { assert!(::std::mem::align_of::<crate::SomeStruct>() == 16); }
3846 );
3847 assert_rs_matches!(
3848 rs_api,
3849 quote! { assert!(::std::mem::size_of::<crate::SomeAnonStruct>() == 16); }
3850 );
3851 assert_rs_matches!(
3852 rs_api,
3853 quote! { assert!(::std::mem::align_of::<crate::SomeAnonStruct>() == 16); }
3854 );
3855
3856 Ok(())
3857 }
3858
3859 #[test]
Lukasz Anforowicz275fa922022-01-05 16:13:20 +00003860 fn test_ref_to_struct_in_thunk_impls() -> Result<()> {
Lukasz Anforowiczd4742ff2022-07-11 17:05:02 -07003861 let ir = ir_from_cc("struct S{}; inline void foo(S& s) {} ")?;
Devin Jeanpierree9850a72022-06-29 19:04:48 -07003862 let rs_api_impl = generate_bindings_tokens(ir)?.rs_api_impl;
Lukasz Anforowicz275fa922022-01-05 16:13:20 +00003863 assert_cc_matches!(
3864 rs_api_impl,
3865 quote! {
Lukasz Anforowicz07b33902022-07-13 15:27:03 -07003866 extern "C" void __rust_thunk___Z3fooR1S(struct S* s) {
3867 foo(*s);
Lukasz Anforowicz275fa922022-01-05 16:13:20 +00003868 }
3869 }
3870 );
3871 Ok(())
3872 }
3873
3874 #[test]
3875 fn test_const_ref_to_struct_in_thunk_impls() -> Result<()> {
Lukasz Anforowiczd4742ff2022-07-11 17:05:02 -07003876 let ir = ir_from_cc("struct S{}; inline void foo(const S& s) {} ")?;
Devin Jeanpierree9850a72022-06-29 19:04:48 -07003877 let rs_api_impl = generate_bindings_tokens(ir)?.rs_api_impl;
Lukasz Anforowicz275fa922022-01-05 16:13:20 +00003878 assert_cc_matches!(
3879 rs_api_impl,
3880 quote! {
Lukasz Anforowicz07b33902022-07-13 15:27:03 -07003881 extern "C" void __rust_thunk___Z3fooRK1S(const struct S* s) {
3882 foo(*s);
Lukasz Anforowicz275fa922022-01-05 16:13:20 +00003883 }
3884 }
3885 );
3886 Ok(())
3887 }
3888
3889 #[test]
Lukasz Anforowicz957cbf22022-01-05 16:14:05 +00003890 fn test_unsigned_int_in_thunk_impls() -> Result<()> {
3891 let ir = ir_from_cc("inline void foo(unsigned int i) {} ")?;
Devin Jeanpierree9850a72022-06-29 19:04:48 -07003892 let rs_api_impl = generate_bindings_tokens(ir)?.rs_api_impl;
Lukasz Anforowicz957cbf22022-01-05 16:14:05 +00003893 assert_cc_matches!(
3894 rs_api_impl,
3895 quote! {
3896 extern "C" void __rust_thunk___Z3fooj(unsigned int i) {
Lukasz Anforowicz07b33902022-07-13 15:27:03 -07003897 foo(i);
Lukasz Anforowicz957cbf22022-01-05 16:14:05 +00003898 }
3899 }
3900 );
3901 Ok(())
3902 }
3903
3904 #[test]
Marcel Hlopkodd1fcb12021-12-22 14:13:59 +00003905 fn test_record_static_methods_qualify_call_in_thunk() -> Result<()> {
3906 let ir = ir_from_cc(&tokens_to_string(quote! {
3907 struct SomeStruct {
3908 static inline int some_func() { return 42; }
3909 };
3910 })?)?;
3911
3912 assert_cc_matches!(
Devin Jeanpierree9850a72022-06-29 19:04:48 -07003913 generate_bindings_tokens(ir)?.rs_api_impl,
Marcel Hlopkodd1fcb12021-12-22 14:13:59 +00003914 quote! {
3915 extern "C" int __rust_thunk___ZN10SomeStruct9some_funcEv() {
3916 return SomeStruct::some_func();
3917 }
3918 }
3919 );
3920 Ok(())
3921 }
3922
3923 #[test]
Lukasz Anforowiczb3d89aa2022-01-12 14:35:52 +00003924 fn test_record_instance_methods_deref_this_in_thunk() -> Result<()> {
3925 let ir = ir_from_cc(&tokens_to_string(quote! {
3926 struct SomeStruct {
3927 inline int some_func(int arg) const { return 42 + arg; }
3928 };
3929 })?)?;
3930
3931 assert_cc_matches!(
Devin Jeanpierree9850a72022-06-29 19:04:48 -07003932 generate_bindings_tokens(ir)?.rs_api_impl,
Lukasz Anforowiczb3d89aa2022-01-12 14:35:52 +00003933 quote! {
3934 extern "C" int __rust_thunk___ZNK10SomeStruct9some_funcEi(
Lukasz Anforowiczd4742ff2022-07-11 17:05:02 -07003935 const struct SomeStruct* __this, int arg) {
Lukasz Anforowicz07b33902022-07-13 15:27:03 -07003936 return __this->some_func(arg);
Lukasz Anforowiczb3d89aa2022-01-12 14:35:52 +00003937 }
3938 }
3939 );
3940 Ok(())
3941 }
3942
3943 #[test]
Lukasz Anforowiczfea0db92022-05-17 17:28:04 -07003944 fn test_record_with_unsupported_field_type() -> Result<()> {
3945 // Using a nested struct because it's currently not supported.
3946 // But... any other unsupported type would also work for this test.
3947 let ir = ir_from_cc(
3948 r#"
3949 struct StructWithUnsupportedField {
3950 struct NestedStruct {
3951 int nested_field;
3952 };
3953
3954 // Doc comment for `my_field`.
3955 NestedStruct my_field;
3956 };
3957 "#,
3958 )?;
Devin Jeanpierree9850a72022-06-29 19:04:48 -07003959 let rs_api = generate_bindings_tokens(ir)?.rs_api;
Lukasz Anforowiczfea0db92022-05-17 17:28:04 -07003960 assert_rs_matches!(
3961 rs_api,
3962 quote! {
3963 #[repr(C, align(4))]
3964 pub struct StructWithUnsupportedField {
3965 #[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 -07003966 pub(crate) my_field: [::std::mem::MaybeUninit<u8>; 4],
Lukasz Anforowiczfea0db92022-05-17 17:28:04 -07003967 }
3968 ...
3969 const _: () = assert!(
3970 memoffset_unstable_const::offset_of!(
Lukasz Anforowicz30360ba2022-05-23 12:15:12 -07003971 crate::StructWithUnsupportedField, my_field) == 0);
Lukasz Anforowiczfea0db92022-05-17 17:28:04 -07003972 }
3973 );
3974 Ok(())
3975 }
3976
3977 #[test]
Lukasz Anforowicze200e8a2022-05-18 12:36:33 -07003978 fn test_struct_with_unnamed_bitfield_member() -> Result<()> {
3979 // This test input causes `field_decl->getName()` to return an empty string.
3980 // This example is based on `struct timex` from
3981 // /usr/grte/v5/include/bits/timex.h
3982 let ir = ir_from_cc(
3983 r#"
3984 struct SomeStruct {
3985 int first_field;
3986 int :32;
3987 int last_field;
3988 }; "#,
3989 )?;
Devin Jeanpierree9850a72022-06-29 19:04:48 -07003990 let BindingsTokens { rs_api, .. } = generate_bindings_tokens(ir)?;
Lukasz Anforowicze200e8a2022-05-18 12:36:33 -07003991 assert_rs_matches!(
3992 rs_api,
3993 quote! {
3994 #[repr(C)]
3995 pub struct SomeStruct {
Michael Forster82c02d32022-05-20 21:47:33 -07003996 pub first_field: i32, ...
Devin Jeanpierrecd5ebf32022-07-08 05:31:55 -07003997 __bitfields1: [::std::mem::MaybeUninit<u8>; 4],
Lukasz Anforowicze200e8a2022-05-18 12:36:33 -07003998 pub last_field: i32,
3999 }
4000 ...
4001 const _: () = assert!(memoffset_unstable_const::offset_of!(
Lukasz Anforowicz30360ba2022-05-23 12:15:12 -07004002 crate::SomeStruct, first_field) == 0);
Lukasz Anforowicze200e8a2022-05-18 12:36:33 -07004003 const _: () = assert!(memoffset_unstable_const::offset_of!(
Lukasz Anforowicz30360ba2022-05-23 12:15:12 -07004004 crate::SomeStruct, last_field) == 8);
Lukasz Anforowicze200e8a2022-05-18 12:36:33 -07004005 }
4006 );
4007 Ok(())
4008 }
4009
4010 #[test]
Kinuko Yasuda6ff59f12022-08-11 08:41:45 -07004011 fn test_struct_with_inheritable_field() -> Result<()> {
4012 let ir = ir_from_cc(
4013 r#"
4014 struct TrivialButInheritable {
4015 int x;
4016 };
4017 struct StructWithInheritable final {
4018 TrivialButInheritable t;
4019 };
4020 "#,
4021 )?;
4022 let rs_api = generate_bindings_tokens(ir)?.rs_api;
4023 assert_rs_not_matches!(rs_api, quote! {derive ( ... Copy ... )});
4024 assert_rs_not_matches!(rs_api, quote! {derive ( ... Clone ... )});
4025 Ok(())
4026 }
4027
4028 #[test]
4029 fn test_union_with_inheritable_field() -> Result<()> {
4030 let ir = ir_from_cc(
4031 r#"
4032 struct TrivialButInheritable {
4033 int x;
4034 };
4035 union UnionWithInheritable {
4036 TrivialButInheritable t;
4037 };
4038 "#,
4039 )?;
4040 let rs_api = generate_bindings_tokens(ir)?.rs_api;
4041 assert_rs_not_matches!(rs_api, quote! {derive ( ... Copy ... )});
4042 assert_rs_not_matches!(rs_api, quote! {derive ( ... Clone ... )});
4043 Ok(())
4044 }
4045
Devin Jeanpierrea99e0a32022-08-17 01:36:58 -07004046 /// Classes with a non-public destructor shouldn't be constructible, not
4047 /// even via Copy/Clone.
4048 #[test]
4049 fn test_trivial_nonpublic_destructor() -> Result<()> {
4050 let ir = ir_from_cc(
Devin Jeanpierreccb67672022-08-17 10:05:47 -07004051 r#"#pragma clang lifetime_elision
Devin Jeanpierrea99e0a32022-08-17 01:36:58 -07004052 struct Indestructible final {
4053 Indestructible() = default;
4054 Indestructible(int);
4055 Indestructible(const Indestructible&) = default;
Devin Jeanpierreccb67672022-08-17 10:05:47 -07004056 void Foo() const;
Devin Jeanpierrea99e0a32022-08-17 01:36:58 -07004057 private:
4058 ~Indestructible() = default;
4059 };
4060
4061 Indestructible ReturnsValue();
4062 void TakesValue(Indestructible);
Devin Jeanpierreccb67672022-08-17 10:05:47 -07004063 void TakesReference(const Indestructible& x);
Devin Jeanpierrea99e0a32022-08-17 01:36:58 -07004064 "#,
4065 )?;
4066 let rs_api = generate_bindings_tokens(ir)?.rs_api;
Devin Jeanpierreccb67672022-08-17 10:05:47 -07004067 // It isn't available by value:
Devin Jeanpierrea99e0a32022-08-17 01:36:58 -07004068 assert_rs_not_matches!(rs_api, quote! {Default});
4069 assert_rs_not_matches!(rs_api, quote! {From});
4070 assert_rs_not_matches!(rs_api, quote! {derive ( ... Copy ... )});
4071 assert_rs_not_matches!(rs_api, quote! {derive ( ... Clone ... )});
4072 assert_rs_not_matches!(rs_api, quote! {ReturnsValue});
4073 assert_rs_not_matches!(rs_api, quote! {TakesValue});
Devin Jeanpierreccb67672022-08-17 10:05:47 -07004074 // ... but it is otherwise available:
4075 assert_rs_matches!(rs_api, quote! {struct Indestructible});
4076 assert_rs_matches!(rs_api, quote! {fn Foo<'a>(&'a self)});
4077 assert_rs_matches!(rs_api, quote! {fn TakesReference<'a>(x: &'a crate::Indestructible)});
Devin Jeanpierrea99e0a32022-08-17 01:36:58 -07004078 Ok(())
4079 }
4080
4081 #[test]
4082 fn test_nontrivial_nonpublic_destructor() -> Result<()> {
4083 let ir = ir_from_cc(
Devin Jeanpierreccb67672022-08-17 10:05:47 -07004084 r#"#pragma clang lifetime_elision
Devin Jeanpierrea99e0a32022-08-17 01:36:58 -07004085 struct Indestructible final {
4086 Indestructible() = default;
4087 Indestructible(int);
4088 Indestructible(const Indestructible&) = default;
Devin Jeanpierreccb67672022-08-17 10:05:47 -07004089 void Foo() const;
Devin Jeanpierrea99e0a32022-08-17 01:36:58 -07004090 private:
4091 ~Indestructible() {}
4092 };
4093
4094 Indestructible ReturnsValue();
4095 void TakesValue(Indestructible);
Devin Jeanpierreccb67672022-08-17 10:05:47 -07004096 void TakesReference(const Indestructible& x);
4097 "#,
4098 )?;
4099 let rs_api = generate_bindings_tokens(ir)?.rs_api;
4100 // It isn't available by value:
4101 assert_rs_not_matches!(rs_api, quote! {CtorNew});
4102 assert_rs_not_matches!(rs_api, quote! {ReturnsValue});
4103 assert_rs_not_matches!(rs_api, quote! {TakesValue});
4104 // ... but it is otherwise available:
4105 assert_rs_matches!(rs_api, quote! {struct Indestructible});
4106 assert_rs_matches!(rs_api, quote! {fn Foo<'a>(&'a self)});
4107 assert_rs_matches!(rs_api, quote! {fn TakesReference<'a>(x: &'a crate::Indestructible)});
4108 Ok(())
4109 }
4110
4111 /// trivial abstract structs shouldn't be constructible, not even via
4112 /// Copy/Clone.
4113 ///
4114 /// Right now, a struct can only be Copy/Clone if it's final, but that
4115 /// restriction will likely be lifted later.
4116 #[test]
4117 fn test_trivial_abstract_by_value() -> Result<()> {
4118 let ir = ir_from_cc(
4119 r#"#pragma clang lifetime_elision
4120 struct Abstract final {
4121 Abstract() = default;
4122 Abstract(int);
4123 Abstract(const Abstract&) = default;
4124 virtual void Foo() const = 0;
4125 void Nonvirtual() const;
4126 };
4127 void TakesAbstract(const Abstract& a);
4128 "#,
4129 )?;
4130 let rs_api = generate_bindings_tokens(ir)?.rs_api;
4131 // It isn't available by value:
4132 assert_rs_not_matches!(rs_api, quote! {Default});
4133 assert_rs_not_matches!(rs_api, quote! {From});
4134 assert_rs_not_matches!(rs_api, quote! {derive ( ... Copy ... )});
4135 assert_rs_not_matches!(rs_api, quote! {derive ( ... Clone ... )});
4136 // ... but it is otherwise available:
4137 assert_rs_matches!(rs_api, quote! {struct Abstract});
4138 assert_rs_matches!(rs_api, quote! {fn Foo<'a>(&'a self)});
4139 assert_rs_matches!(rs_api, quote! {fn Nonvirtual<'a>(&'a self)});
4140 assert_rs_matches!(rs_api, quote! {fn TakesAbstract<'a>(a: &'a crate::Abstract)});
4141 Ok(())
4142 }
4143
4144 #[test]
4145 fn test_nontrivial_abstract_by_value() -> Result<()> {
4146 let ir = ir_from_cc(
4147 r#"#pragma clang lifetime_elision
4148 struct Abstract final {
4149 Abstract() {};
4150 Abstract(int);
4151 Abstract(const Abstract&) {}
4152 virtual void Foo() const = 0;
4153 void Nonvirtual() const;
4154 };
4155 void TakesAbstract(const Abstract& a);
Devin Jeanpierrea99e0a32022-08-17 01:36:58 -07004156 "#,
4157 )?;
4158 let rs_api = generate_bindings_tokens(ir)?.rs_api;
4159 assert_rs_not_matches!(rs_api, quote! {CtorNew});
Devin Jeanpierreccb67672022-08-17 10:05:47 -07004160 // ... but it is otherwise available:
4161 assert_rs_matches!(rs_api, quote! {struct Abstract});
4162 assert_rs_matches!(rs_api, quote! {fn Foo<'a>(&'a self)});
4163 assert_rs_matches!(rs_api, quote! {fn Nonvirtual<'a>(&'a self)});
4164 assert_rs_matches!(rs_api, quote! {fn TakesAbstract<'a>(a: &'a crate::Abstract)});
Devin Jeanpierrea99e0a32022-08-17 01:36:58 -07004165 Ok(())
4166 }
4167
Kinuko Yasuda6ff59f12022-08-11 08:41:45 -07004168 #[test]
Lukasz Anforowicze200e8a2022-05-18 12:36:33 -07004169 fn test_struct_with_unnamed_struct_and_union_members() -> Result<()> {
4170 // This test input causes `field_decl->getName()` to return an empty string.
4171 // See also:
4172 // - https://en.cppreference.com/w/c/language/struct: "[...] an unnamed member
4173 // of a struct whose type is a struct without name is known as anonymous
4174 // struct."
4175 // - https://rust-lang.github.io/rfcs/2102-unnamed-fields.html
4176 let ir = ir_from_cc(
4177 r#"
4178 struct StructWithUnnamedMembers {
4179 int first_field;
4180
4181 struct {
4182 int anonymous_struct_field_1;
4183 int anonymous_struct_field_2;
4184 };
4185 union {
4186 int anonymous_union_field_1;
4187 int anonymous_union_field_2;
4188 };
4189
4190 int last_field;
4191 }; "#,
4192 )?;
Devin Jeanpierree9850a72022-06-29 19:04:48 -07004193 let BindingsTokens { rs_api, .. } = generate_bindings_tokens(ir)?;
Lukasz Anforowicze200e8a2022-05-18 12:36:33 -07004194 // TODO(b/200067824): Once nested structs anhd unions are supported,
4195 // `__unnamed_field1` and `__unnamed_field2` should have a real, usable
4196 // type.
4197 assert_rs_matches!(
4198 rs_api,
4199 quote! {
4200 #[repr(C, align(4))]
4201 pub struct StructWithUnnamedMembers {
4202 pub first_field: i32,
4203 #[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 -07004204 pub(crate) __unnamed_field1: [::std::mem::MaybeUninit<u8>; 8],
Lukasz Anforowicze200e8a2022-05-18 12:36:33 -07004205 #[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 -07004206 pub(crate) __unnamed_field2: [::std::mem::MaybeUninit<u8>; 4],
Lukasz Anforowicze200e8a2022-05-18 12:36:33 -07004207 pub last_field: i32,
4208 }
4209 ...
4210 const _: () = assert!(memoffset_unstable_const::offset_of!(
Lukasz Anforowicz30360ba2022-05-23 12:15:12 -07004211 crate::StructWithUnnamedMembers, first_field) == 0);
Lukasz Anforowicze200e8a2022-05-18 12:36:33 -07004212 const _: () = assert!(memoffset_unstable_const::offset_of!(
Lukasz Anforowicz30360ba2022-05-23 12:15:12 -07004213 crate::StructWithUnnamedMembers, __unnamed_field1) == 4);
Lukasz Anforowicze200e8a2022-05-18 12:36:33 -07004214 const _: () = assert!(memoffset_unstable_const::offset_of!(
Lukasz Anforowicz30360ba2022-05-23 12:15:12 -07004215 crate::StructWithUnnamedMembers, __unnamed_field2) == 12);
Lukasz Anforowicze200e8a2022-05-18 12:36:33 -07004216 const _: () = assert!(memoffset_unstable_const::offset_of!(
Lukasz Anforowicz30360ba2022-05-23 12:15:12 -07004217 crate::StructWithUnnamedMembers, last_field) == 16);
Lukasz Anforowicze200e8a2022-05-18 12:36:33 -07004218 }
4219 );
4220 Ok(())
4221 }
4222
4223 #[test]
Marcel Hlopkoa0f38662021-12-03 08:45:26 +00004224 fn test_struct_from_other_target() -> Result<()> {
4225 let ir = ir_from_cc_dependency("// intentionally empty", "struct SomeStruct {};")?;
Devin Jeanpierree9850a72022-06-29 19:04:48 -07004226 let BindingsTokens { rs_api, rs_api_impl } = generate_bindings_tokens(ir)?;
Devin Jeanpierre4f06f832022-04-26 15:51:30 -07004227 assert_rs_not_matches!(rs_api, quote! { SomeStruct });
4228 assert_cc_not_matches!(rs_api_impl, quote! { SomeStruct });
Marcel Hlopkoa0f38662021-12-03 08:45:26 +00004229 Ok(())
4230 }
4231
4232 #[test]
Devin Jeanpierre2ed14ec2021-10-06 11:32:19 +00004233 fn test_copy_derives() {
Devin Jeanpierreccfefc82021-10-27 10:54:00 +00004234 let record = ir_record("S");
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00004235 assert_eq!(generate_derives(&record), &["Clone", "Copy"]);
Devin Jeanpierre2ed14ec2021-10-06 11:32:19 +00004236 }
4237
4238 #[test]
4239 fn test_copy_derives_not_is_trivial_abi() {
Devin Jeanpierreccfefc82021-10-27 10:54:00 +00004240 let mut record = ir_record("S");
Devin Jeanpierre2ed14ec2021-10-06 11:32:19 +00004241 record.is_trivial_abi = false;
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00004242 assert_eq!(generate_derives(&record), &[""; 0]);
Devin Jeanpierre2ed14ec2021-10-06 11:32:19 +00004243 }
4244
Devin Jeanpierre88343c72022-01-15 01:10:23 +00004245 /// Even if it's trivially relocatable, !Unpin C++ type cannot be
4246 /// cloned/copied or otherwise used by value, because values would allow
4247 /// assignment into the Pin.
4248 ///
4249 /// All !Unpin C++ types, not just non trivially relocatable ones, are
4250 /// unsafe to assign in the Rust sense.
Devin Jeanpierree6e16652021-12-22 15:54:46 +00004251 #[test]
Devin Jeanpierre88343c72022-01-15 01:10:23 +00004252 fn test_copy_derives_not_final() {
Devin Jeanpierree6e16652021-12-22 15:54:46 +00004253 let mut record = ir_record("S");
Teddy Katzd2cd1422022-04-04 09:41:33 -07004254 record.is_inheritable = true;
Devin Jeanpierre88343c72022-01-15 01:10:23 +00004255 assert_eq!(generate_derives(&record), &[""; 0]);
Devin Jeanpierree6e16652021-12-22 15:54:46 +00004256 }
4257
Devin Jeanpierre2ed14ec2021-10-06 11:32:19 +00004258 #[test]
Devin Jeanpierre2ed14ec2021-10-06 11:32:19 +00004259 fn test_copy_derives_ctor_deleted() {
Devin Jeanpierreccfefc82021-10-27 10:54:00 +00004260 let mut record = ir_record("S");
Lukasz Anforowiczff7df4a2022-06-02 14:27:45 -07004261 record.copy_constructor = ir::SpecialMemberFunc::Unavailable;
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00004262 assert_eq!(generate_derives(&record), &[""; 0]);
Devin Jeanpierre2ed14ec2021-10-06 11:32:19 +00004263 }
4264
4265 #[test]
Devin Jeanpierrebe2f33b2021-10-21 12:54:19 +00004266 fn test_copy_derives_ctor_nontrivial_members() {
Devin Jeanpierreccfefc82021-10-27 10:54:00 +00004267 let mut record = ir_record("S");
Lukasz Anforowiczff7df4a2022-06-02 14:27:45 -07004268 record.copy_constructor = ir::SpecialMemberFunc::NontrivialMembers;
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00004269 assert_eq!(generate_derives(&record), &[""; 0]);
Devin Jeanpierrebe2f33b2021-10-21 12:54:19 +00004270 }
4271
4272 #[test]
4273 fn test_copy_derives_ctor_nontrivial_self() {
Devin Jeanpierreccfefc82021-10-27 10:54:00 +00004274 let mut record = ir_record("S");
Lukasz Anforowiczff7df4a2022-06-02 14:27:45 -07004275 record.copy_constructor = ir::SpecialMemberFunc::NontrivialUserDefined;
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00004276 assert_eq!(generate_derives(&record), &[""; 0]);
Devin Jeanpierre2ed14ec2021-10-06 11:32:19 +00004277 }
4278
Devin Jeanpierreb1e816a2022-04-29 20:14:22 -07004279 /// In Rust, a Drop type cannot be Copy.
4280 #[test]
4281 fn test_copy_derives_dtor_nontrivial_self() {
4282 let mut record = ir_record("S");
Lukasz Anforowiczff7df4a2022-06-02 14:27:45 -07004283 for definition in
4284 [ir::SpecialMemberFunc::NontrivialUserDefined, ir::SpecialMemberFunc::NontrivialMembers]
4285 {
4286 record.destructor = definition;
Devin Jeanpierreb1e816a2022-04-29 20:14:22 -07004287 assert_eq!(generate_derives(&record), &["Clone"]);
4288 }
4289 }
4290
Devin Jeanpierre7a7328e2021-09-17 07:10:08 +00004291 #[test]
4292 fn test_ptr_func() -> Result<()> {
Marcel Hlopko89547752021-12-10 09:39:41 +00004293 let ir = ir_from_cc(&tokens_to_string(quote! {
4294 inline int* Deref(int*const* p);
4295 })?)?;
Devin Jeanpierred6da7002021-10-21 12:55:20 +00004296
Devin Jeanpierree9850a72022-06-29 19:04:48 -07004297 let BindingsTokens { rs_api, rs_api_impl } = generate_bindings_tokens(ir)?;
Marcel Hlopko89547752021-12-10 09:39:41 +00004298 assert_rs_matches!(
4299 rs_api,
4300 quote! {
Michael Forsterdb8101a2021-10-08 06:56:03 +00004301 #[inline(always)]
Lukasz Anforowiczf7bdd392022-01-21 00:33:39 +00004302 pub unsafe fn Deref(p: *const *mut i32) -> *mut i32 {
Rosica Dejanovska68c11ac2022-05-12 03:48:59 -07004303 crate::detail::__rust_thunk___Z5DerefPKPi(p)
Marcel Hlopko89547752021-12-10 09:39:41 +00004304 }
4305 }
4306 );
4307 assert_rs_matches!(
4308 rs_api,
4309 quote! {
Michael Forsterdb8101a2021-10-08 06:56:03 +00004310 mod detail {
Googler55647142022-01-11 12:37:39 +00004311 #[allow(unused_imports)]
Devin Jeanpierred4dde0e2021-10-13 20:48:25 +00004312 use super::*;
Michael Forsterdb8101a2021-10-08 06:56:03 +00004313 extern "C" {
Googlera675ae02021-12-07 08:04:59 +00004314 pub(crate) fn __rust_thunk___Z5DerefPKPi(p: *const *mut i32) -> *mut i32;
Marcel Hlopko89547752021-12-10 09:39:41 +00004315 }
4316 }
4317 }
Devin Jeanpierre7a7328e2021-09-17 07:10:08 +00004318 );
Devin Jeanpierre184f9ac2021-09-17 13:47:03 +00004319
Marcel Hlopko89547752021-12-10 09:39:41 +00004320 assert_cc_matches!(
Devin Jeanpierre4f06f832022-04-26 15:51:30 -07004321 rs_api_impl,
Marcel Hlopko89547752021-12-10 09:39:41 +00004322 quote! {
Googlera675ae02021-12-07 08:04:59 +00004323 extern "C" int* __rust_thunk___Z5DerefPKPi(int* const * p) {
Lukasz Anforowicz07b33902022-07-13 15:27:03 -07004324 return Deref(p);
Devin Jeanpierre184f9ac2021-09-17 13:47:03 +00004325 }
Marcel Hlopko89547752021-12-10 09:39:41 +00004326 }
Devin Jeanpierre184f9ac2021-09-17 13:47:03 +00004327 );
Devin Jeanpierre7a7328e2021-09-17 07:10:08 +00004328 Ok(())
4329 }
Michael Forstered642022021-10-04 09:48:25 +00004330
4331 #[test]
Googlerdb111532022-01-05 06:12:13 +00004332 fn test_const_char_ptr_func() -> Result<()> {
4333 // This is a regression test: We used to include the "const" in the name
4334 // of the CcType, which caused a panic in the code generator
4335 // ('"const char" is not a valid Ident').
4336 // It's therefore important that f() is inline so that we need to
4337 // generate a thunk for it (where we then process the CcType).
4338 let ir = ir_from_cc(&tokens_to_string(quote! {
4339 inline void f(const char *str);
4340 })?)?;
4341
Devin Jeanpierree9850a72022-06-29 19:04:48 -07004342 let BindingsTokens { rs_api, rs_api_impl } = generate_bindings_tokens(ir)?;
Googlerdb111532022-01-05 06:12:13 +00004343 assert_rs_matches!(
4344 rs_api,
4345 quote! {
4346 #[inline(always)]
Lukasz Anforowiczf7bdd392022-01-21 00:33:39 +00004347 pub unsafe fn f(str: *const i8) {
Rosica Dejanovska68c11ac2022-05-12 03:48:59 -07004348 crate::detail::__rust_thunk___Z1fPKc(str)
Googlerdb111532022-01-05 06:12:13 +00004349 }
4350 }
4351 );
4352 assert_rs_matches!(
4353 rs_api,
4354 quote! {
4355 extern "C" {
4356 pub(crate) fn __rust_thunk___Z1fPKc(str: *const i8);
4357 }
4358 }
4359 );
4360
4361 assert_cc_matches!(
Devin Jeanpierre4f06f832022-04-26 15:51:30 -07004362 rs_api_impl,
Googlerdb111532022-01-05 06:12:13 +00004363 quote! {
Lukasz Anforowicz07b33902022-07-13 15:27:03 -07004364 extern "C" void __rust_thunk___Z1fPKc(char const * str){ f(str); }
Googlerdb111532022-01-05 06:12:13 +00004365 }
4366 );
4367 Ok(())
4368 }
4369
4370 #[test]
Lukasz Anforowiczcf230fd2022-02-18 19:20:39 +00004371 fn test_func_ptr_where_params_are_primitive_types() -> Result<()> {
4372 let ir = ir_from_cc(r#" int (*get_ptr_to_func())(float, double); "#)?;
Devin Jeanpierree9850a72022-06-29 19:04:48 -07004373 let BindingsTokens { rs_api, rs_api_impl } = generate_bindings_tokens(ir)?;
Lukasz Anforowiczcf230fd2022-02-18 19:20:39 +00004374 assert_rs_matches!(
4375 rs_api,
4376 quote! {
4377 #[inline(always)]
4378 pub fn get_ptr_to_func() -> Option<extern "C" fn (f32, f64) -> i32> {
Rosica Dejanovska68c11ac2022-05-12 03:48:59 -07004379 unsafe { crate::detail::__rust_thunk___Z15get_ptr_to_funcv() }
Lukasz Anforowiczcf230fd2022-02-18 19:20:39 +00004380 }
4381 }
4382 );
4383 assert_rs_matches!(
4384 rs_api,
4385 quote! {
4386 mod detail {
4387 #[allow(unused_imports)]
4388 use super::*;
4389 extern "C" {
4390 #[link_name = "_Z15get_ptr_to_funcv"]
4391 pub(crate) fn __rust_thunk___Z15get_ptr_to_funcv()
4392 -> Option<extern "C" fn(f32, f64) -> i32>;
4393 }
4394 }
4395 }
4396 );
4397 // Verify that no C++ thunk got generated.
4398 assert_cc_not_matches!(rs_api_impl, quote! { __rust_thunk___Z15get_ptr_to_funcv });
4399
4400 // TODO(b/217419782): Add another test for more exotic calling conventions /
4401 // abis.
4402
4403 // TODO(b/217419782): Add another test for pointer to a function that
4404 // takes/returns non-trivially-movable types by value. See also
4405 // <internal link>
4406
4407 Ok(())
4408 }
4409
4410 #[test]
Lukasz Anforowicz92c81c32022-03-04 19:03:56 +00004411 fn test_func_ref() -> Result<()> {
4412 let ir = ir_from_cc(r#" int (&get_ref_to_func())(float, double); "#)?;
Devin Jeanpierree9850a72022-06-29 19:04:48 -07004413 let rs_api = generate_bindings_tokens(ir)?.rs_api;
Lukasz Anforowicz92c81c32022-03-04 19:03:56 +00004414 assert_rs_matches!(
4415 rs_api,
4416 quote! {
4417 #[inline(always)]
4418 pub fn get_ref_to_func() -> extern "C" fn (f32, f64) -> i32 {
Rosica Dejanovska68c11ac2022-05-12 03:48:59 -07004419 unsafe { crate::detail::__rust_thunk___Z15get_ref_to_funcv() }
Lukasz Anforowicz92c81c32022-03-04 19:03:56 +00004420 }
4421 }
4422 );
4423 Ok(())
4424 }
4425
4426 #[test]
Lukasz Anforowiczcf230fd2022-02-18 19:20:39 +00004427 fn test_func_ptr_with_non_static_lifetime() -> Result<()> {
Martin Brænnee5ba6b62022-06-23 07:38:40 -07004428 let ir = ir_from_cc(&with_lifetime_macros(
Lukasz Anforowiczcf230fd2022-02-18 19:20:39 +00004429 r#"
Martin Brænnee5ba6b62022-06-23 07:38:40 -07004430 int (* $a get_ptr_to_func())(float, double); "#,
4431 ))?;
Devin Jeanpierree9850a72022-06-29 19:04:48 -07004432 let rs_api = generate_bindings_tokens(ir)?.rs_api;
Lukasz Anforowiczcf230fd2022-02-18 19:20:39 +00004433 assert_rs_matches!(
4434 rs_api,
4435 quote! {
4436 // Error while generating bindings for item 'get_ptr_to_func':
4437 // Return type is not supported: Function pointers with non-'static lifetimes are not supported: int (*)(float, double)
4438 }
4439 );
4440 Ok(())
4441 }
4442
4443 #[test]
4444 fn test_func_ptr_where_params_are_raw_ptrs() -> Result<()> {
4445 let ir = ir_from_cc(r#" const int* (*get_ptr_to_func())(const int*); "#)?;
Devin Jeanpierree9850a72022-06-29 19:04:48 -07004446 let BindingsTokens { rs_api, rs_api_impl } = generate_bindings_tokens(ir)?;
Lukasz Anforowiczcf230fd2022-02-18 19:20:39 +00004447 assert_rs_matches!(
4448 rs_api,
4449 quote! {
4450 #[inline(always)]
4451 pub fn get_ptr_to_func() -> Option<extern "C" fn (*const i32) -> *const i32> {
Rosica Dejanovska68c11ac2022-05-12 03:48:59 -07004452 unsafe { crate::detail::__rust_thunk___Z15get_ptr_to_funcv() }
Lukasz Anforowiczcf230fd2022-02-18 19:20:39 +00004453 }
4454 }
4455 );
4456 assert_rs_matches!(
4457 rs_api,
4458 quote! {
4459 mod detail {
4460 #[allow(unused_imports)]
4461 use super::*;
4462 extern "C" {
4463 #[link_name = "_Z15get_ptr_to_funcv"]
4464 pub(crate) fn __rust_thunk___Z15get_ptr_to_funcv()
4465 -> Option<extern "C" fn(*const i32) -> *const i32>;
4466 }
4467 }
4468 }
4469 );
4470 // Verify that no C++ thunk got generated.
4471 assert_cc_not_matches!(rs_api_impl, quote! { __rust_thunk___Z15get_ptr_to_funcv });
4472
4473 // TODO(b/217419782): Add another test where params (and the return
4474 // type) are references with lifetimes. Something like this:
4475 // #pragma clang lifetime_elision
4476 // const int& (*get_ptr_to_func())(const int&, const int&); "#)?;
4477 // 1) Need to investigate why this fails - seeing raw pointers in Rust
4478 // seems to indicate that no lifetimes are present at the `importer.cc`
4479 // level. Maybe lifetime elision doesn't support this scenario? Unclear
Googler53f65942022-02-23 11:23:30 +00004480 // how to explicitly apply [[clang::annotate("lifetimes", "a, b -> a")]]
Lukasz Anforowiczcf230fd2022-02-18 19:20:39 +00004481 // to the _inner_ function.
4482 // 2) It is important to have 2 reference parameters, so see if the problem
4483 // of passing `lifetimes` by value would have been caught - see:
4484 // cl/428079010/depot/rs_bindings_from_cc/
4485 // importer.cc?version=s6#823
4486
4487 // TODO(b/217419782): Decide what to do if the C++ pointer is *not*
4488 // annotated with a lifetime - emit `unsafe fn(...) -> ...` in that
4489 // case?
4490
4491 Ok(())
4492 }
4493
4494 #[test]
Lukasz Anforowicz71e9b592022-03-04 19:03:02 +00004495 fn test_func_ptr_with_custom_abi() -> Result<()> {
4496 let ir = ir_from_cc(r#" int (*get_ptr_to_func())(float, double) [[clang::vectorcall]]; "#)?;
4497
4498 // Verify that the test input correctly represents what we intend to
4499 // test - we want [[clang::vectorcall]] to apply to the returned
4500 // function pointer, but *not* apply to the `get_ptr_to_func` function.
Lukasz Anforowicz71e9b592022-03-04 19:03:02 +00004501 assert_ir_matches!(
4502 ir,
4503 quote! {
4504 Func(Func {
4505 name: "get_ptr_to_func", ...
4506 return_type: MappedType {
4507 rs_type: RsType {
4508 name: Some("Option"), ...
4509 type_args: [RsType { name: Some("#funcPtr vectorcall"), ... }], ...
4510 },
4511 cc_type: CcType {
4512 name: Some("*"), ...
4513 type_args: [CcType { name: Some("#funcValue vectorcall"), ... }], ...
4514 },
Lukasz Anforowicz0b6a6ac2022-03-22 22:32:23 +00004515 }, ...
4516 has_c_calling_convention: true, ...
Lukasz Anforowicz71e9b592022-03-04 19:03:02 +00004517 }),
4518 }
4519 );
4520
Devin Jeanpierree9850a72022-06-29 19:04:48 -07004521 let BindingsTokens { rs_api, rs_api_impl } = generate_bindings_tokens(ir)?;
Lukasz Anforowicz71e9b592022-03-04 19:03:02 +00004522 // Check that the custom "vectorcall" ABI gets propagated into the
4523 // return type (i.e. into `extern "vectorcall" fn`).
Lukasz Anforowicz71e9b592022-03-04 19:03:02 +00004524 assert_rs_matches!(
4525 rs_api,
4526 quote! {
4527 #[inline(always)]
4528 pub fn get_ptr_to_func() -> Option<extern "vectorcall" fn (f32, f64) -> i32> {
Rosica Dejanovska68c11ac2022-05-12 03:48:59 -07004529 unsafe { crate::detail::__rust_thunk___Z15get_ptr_to_funcv() }
Lukasz Anforowicz71e9b592022-03-04 19:03:02 +00004530 }
4531 }
4532 );
4533
4534 // The usual `extern "C"` ABI should be used for "get_ptr_to_func".
4535 assert_rs_matches!(
4536 rs_api,
4537 quote! {
4538 mod detail {
4539 #[allow(unused_imports)]
4540 use super::*;
4541 extern "C" {
4542 #[link_name = "_Z15get_ptr_to_funcv"]
4543 pub(crate) fn __rust_thunk___Z15get_ptr_to_funcv()
4544 -> Option<extern "vectorcall" fn(f32, f64) -> i32>;
4545 }
4546 }
4547 }
4548 );
4549
4550 // Verify that no C++ thunk got generated.
Lukasz Anforowicz71e9b592022-03-04 19:03:02 +00004551 assert_cc_not_matches!(rs_api_impl, quote! { __rust_thunk___Z15get_ptr_to_funcv });
4552 Ok(())
4553 }
4554
4555 #[test]
4556 fn test_func_ptr_thunk() -> Result<()> {
4557 // Using an `inline` keyword forces generation of a C++ thunk in
4558 // `rs_api_impl` (i.e. exercises `format_cc_type` and similar code).
4559 let ir = ir_from_cc(
4560 r#"
4561 int multiply(int x, int y);
4562 inline int (*inline_get_pointer_to_function())(int, int) {
4563 return multiply;
4564 }
4565 "#,
4566 )?;
Devin Jeanpierree9850a72022-06-29 19:04:48 -07004567 let rs_api_impl = generate_bindings_tokens(ir)?.rs_api_impl;
Lukasz Anforowicz71e9b592022-03-04 19:03:02 +00004568 assert_cc_matches!(
4569 rs_api_impl,
4570 quote! {
Lukasz Anforowicz23171542022-04-11 15:26:17 -07004571 extern "C" crubit::type_identity_t<int(int , int)>*
Lukasz Anforowicz71e9b592022-03-04 19:03:02 +00004572 __rust_thunk___Z30inline_get_pointer_to_functionv() {
4573 return inline_get_pointer_to_function();
4574 }
4575 }
4576 );
4577 Ok(())
4578 }
4579
4580 #[test]
Lukasz Anforowicz0b6a6ac2022-03-22 22:32:23 +00004581 fn test_func_ptr_with_custom_abi_thunk() -> Result<()> {
4582 // Using an `inline` keyword forces generation of a C++ thunk in
4583 // `rs_api_impl` (i.e. exercises `format_cc_type`,
4584 // `format_cc_call_conv_as_clang_attribute` and similar code).
4585 let ir = ir_from_cc(
4586 r#"
4587 inline int (*inline_get_ptr_to_func())(float, double) [[clang::vectorcall]];
4588 "#,
4589 )?;
4590
4591 // Verify that the test input correctly represents what we intend to
4592 // test - we want [[clang::vectorcall]] to apply to the returned
4593 // function pointer, but *not* apply to the `get_ptr_to_func` function.
4594 assert_ir_matches!(
4595 ir,
4596 quote! {
4597 Func(Func {
4598 name: "inline_get_ptr_to_func", ...
4599 return_type: MappedType {
4600 rs_type: RsType {
4601 name: Some("Option"), ...
4602 type_args: [RsType { name: Some("#funcPtr vectorcall"), ... }], ...
4603 },
4604 cc_type: CcType {
4605 name: Some("*"), ...
4606 type_args: [CcType { name: Some("#funcValue vectorcall"), ... }], ...
4607 },
4608 }, ...
4609 has_c_calling_convention: true, ...
4610 }),
4611 }
4612 );
4613
4614 // This test is quite similar to `test_func_ptr_thunk` - the main
4615 // difference is verification of the `__attribute__((vectorcall))` in
4616 // the expected signature of the generated thunk below.
Devin Jeanpierree9850a72022-06-29 19:04:48 -07004617 let rs_api_impl = generate_bindings_tokens(ir)?.rs_api_impl;
Lukasz Anforowicz0b6a6ac2022-03-22 22:32:23 +00004618 assert_cc_matches!(
4619 rs_api_impl,
4620 quote! {
Lukasz Anforowicz23171542022-04-11 15:26:17 -07004621 extern "C" crubit::type_identity_t<
Lukasz Anforowicz0b6a6ac2022-03-22 22:32:23 +00004622 int(float , double) __attribute__((vectorcall))
4623 >* __rust_thunk___Z22inline_get_ptr_to_funcv() {
4624 return inline_get_ptr_to_func();
4625 }
4626 }
4627 );
4628 Ok(())
4629 }
4630
4631 #[test]
Michael Forstered642022021-10-04 09:48:25 +00004632 fn test_item_order() -> Result<()> {
Marcel Hlopko89547752021-12-10 09:39:41 +00004633 let ir = ir_from_cc(
4634 "int first_func();
4635 struct FirstStruct {};
4636 int second_func();
4637 struct SecondStruct {};",
4638 )?;
Michael Forstered642022021-10-04 09:48:25 +00004639
Devin Jeanpierree9850a72022-06-29 19:04:48 -07004640 let rs_api = rs_tokens_to_formatted_string_for_tests(generate_bindings_tokens(ir)?.rs_api)?;
Marcel Hlopko89547752021-12-10 09:39:41 +00004641
Lukasz Anforowiczdd9ae0f2022-02-17 15:52:53 +00004642 let idx = |s: &str| rs_api.find(s).ok_or_else(|| anyhow!("'{}' missing", s));
Michael Forstered642022021-10-04 09:48:25 +00004643
4644 let f1 = idx("fn first_func")?;
4645 let f2 = idx("fn second_func")?;
4646 let s1 = idx("struct FirstStruct")?;
4647 let s2 = idx("struct SecondStruct")?;
Googlera675ae02021-12-07 08:04:59 +00004648 let t1 = idx("fn __rust_thunk___Z10first_funcv")?;
4649 let t2 = idx("fn __rust_thunk___Z11second_funcv")?;
Michael Forstered642022021-10-04 09:48:25 +00004650
4651 assert!(f1 < s1);
4652 assert!(s1 < f2);
4653 assert!(f2 < s2);
4654 assert!(s2 < t1);
4655 assert!(t1 < t2);
4656
4657 Ok(())
4658 }
Michael Forster028800b2021-10-05 12:39:59 +00004659
4660 #[test]
Devin Jeanpierrec80e6242022-02-03 01:56:40 +00004661 fn test_base_class_subobject_layout() -> Result<()> {
4662 let ir = ir_from_cc(
4663 r#"
4664 // We use a class here to force `Derived::z` to live inside the tail padding of `Base`.
4665 // On the Itanium ABI, this would not happen if `Base` were a POD type.
Devin Jeanpierre56777022022-02-03 01:57:15 +00004666 class Base {__INT64_TYPE__ x; char y;};
4667 struct Derived final : Base {__INT16_TYPE__ z;};
Devin Jeanpierrec80e6242022-02-03 01:56:40 +00004668 "#,
4669 )?;
Devin Jeanpierree9850a72022-06-29 19:04:48 -07004670 let rs_api = generate_bindings_tokens(ir)?.rs_api;
Devin Jeanpierrec80e6242022-02-03 01:56:40 +00004671 assert_rs_matches!(
4672 rs_api,
4673 quote! {
4674 #[repr(C, align(8))]
4675 pub struct Derived {
Devin Jeanpierrecd5ebf32022-07-08 05:31:55 -07004676 __non_field_data: [::std::mem::MaybeUninit<u8>; 10],
Devin Jeanpierrec80e6242022-02-03 01:56:40 +00004677 pub z: i16,
4678 }
4679 }
4680 );
4681 Ok(())
4682 }
4683
4684 /// The same as test_base_class_subobject_layout, but with multiple
4685 /// inheritance.
4686 #[test]
4687 fn test_base_class_multiple_inheritance_subobject_layout() -> Result<()> {
4688 let ir = ir_from_cc(
4689 r#"
Devin Jeanpierre56777022022-02-03 01:57:15 +00004690 class Base1 {__INT64_TYPE__ x;};
Devin Jeanpierrec80e6242022-02-03 01:56:40 +00004691 class Base2 {char y;};
Devin Jeanpierre56777022022-02-03 01:57:15 +00004692 struct Derived final : Base1, Base2 {__INT16_TYPE__ z;};
Devin Jeanpierrec80e6242022-02-03 01:56:40 +00004693 "#,
4694 )?;
Devin Jeanpierree9850a72022-06-29 19:04:48 -07004695 let rs_api = generate_bindings_tokens(ir)?.rs_api;
Devin Jeanpierrec80e6242022-02-03 01:56:40 +00004696 assert_rs_matches!(
4697 rs_api,
4698 quote! {
4699 #[repr(C, align(8))]
4700 pub struct Derived {
Devin Jeanpierrecd5ebf32022-07-08 05:31:55 -07004701 __non_field_data: [::std::mem::MaybeUninit<u8>; 10],
Devin Jeanpierrec80e6242022-02-03 01:56:40 +00004702 pub z: i16,
4703 }
4704 }
4705 );
4706 Ok(())
4707 }
4708
4709 /// The same as test_base_class_subobject_layout, but with a chain of
4710 /// inheritance.
4711 #[test]
4712 fn test_base_class_deep_inheritance_subobject_layout() -> Result<()> {
4713 let ir = ir_from_cc(
4714 r#"
Devin Jeanpierre56777022022-02-03 01:57:15 +00004715 class Base1 {__INT64_TYPE__ x;};
Devin Jeanpierrec80e6242022-02-03 01:56:40 +00004716 class Base2 : Base1 {char y;};
Devin Jeanpierre56777022022-02-03 01:57:15 +00004717 struct Derived final : Base2 {__INT16_TYPE__ z;};
Devin Jeanpierrec80e6242022-02-03 01:56:40 +00004718 "#,
4719 )?;
Devin Jeanpierree9850a72022-06-29 19:04:48 -07004720 let rs_api = generate_bindings_tokens(ir)?.rs_api;
Devin Jeanpierrec80e6242022-02-03 01:56:40 +00004721 assert_rs_matches!(
4722 rs_api,
4723 quote! {
4724 #[repr(C, align(8))]
4725 pub struct Derived {
Devin Jeanpierrecd5ebf32022-07-08 05:31:55 -07004726 __non_field_data: [::std::mem::MaybeUninit<u8>; 10],
Devin Jeanpierrec80e6242022-02-03 01:56:40 +00004727 pub z: i16,
4728 }
4729 }
4730 );
4731 Ok(())
4732 }
4733
4734 /// For derived classes with no data members, we can't use the offset of the
4735 /// first member to determine the size of the base class subobjects.
4736 #[test]
4737 fn test_base_class_subobject_fieldless_layout() -> Result<()> {
4738 let ir = ir_from_cc(
4739 r#"
Devin Jeanpierre56777022022-02-03 01:57:15 +00004740 class Base {__INT64_TYPE__ x; char y;};
Devin Jeanpierrec80e6242022-02-03 01:56:40 +00004741 struct Derived final : Base {};
4742 "#,
4743 )?;
Devin Jeanpierree9850a72022-06-29 19:04:48 -07004744 let rs_api = generate_bindings_tokens(ir)?.rs_api;
Devin Jeanpierrec80e6242022-02-03 01:56:40 +00004745 assert_rs_matches!(
4746 rs_api,
4747 quote! {
4748 #[repr(C, align(8))]
4749 pub struct Derived {
Devin Jeanpierrecd5ebf32022-07-08 05:31:55 -07004750 __non_field_data: [::std::mem::MaybeUninit<u8>; 16],
Devin Jeanpierrec80e6242022-02-03 01:56:40 +00004751 }
4752 }
4753 );
4754 Ok(())
4755 }
4756
4757 #[test]
4758 fn test_base_class_subobject_empty_fieldless() -> Result<()> {
4759 let ir = ir_from_cc(
4760 r#"
4761 class Base {};
4762 struct Derived final : Base {};
4763 "#,
4764 )?;
Devin Jeanpierree9850a72022-06-29 19:04:48 -07004765 let rs_api = generate_bindings_tokens(ir)?.rs_api;
Devin Jeanpierrec80e6242022-02-03 01:56:40 +00004766 assert_rs_matches!(
4767 rs_api,
4768 quote! {
4769 #[repr(C)]
4770 pub struct Derived {
Devin Jeanpierrecd5ebf32022-07-08 05:31:55 -07004771 __non_field_data: [::std::mem::MaybeUninit<u8>; 1],
Devin Jeanpierrec80e6242022-02-03 01:56:40 +00004772 }
4773 }
4774 );
4775 Ok(())
4776 }
4777
4778 #[test]
4779 fn test_base_class_subobject_empty() -> Result<()> {
4780 let ir = ir_from_cc(
4781 r#"
4782 class Base {};
Devin Jeanpierre1221c2a2022-05-05 22:36:22 -07004783 struct Derived final : Base {
4784 __INT16_TYPE__ x;
4785 };
Devin Jeanpierrec80e6242022-02-03 01:56:40 +00004786 "#,
4787 )?;
Devin Jeanpierree9850a72022-06-29 19:04:48 -07004788 let rs_api = generate_bindings_tokens(ir)?.rs_api;
Devin Jeanpierrec80e6242022-02-03 01:56:40 +00004789 assert_rs_matches!(
4790 rs_api,
4791 quote! {
Devin Jeanpierrec80e6242022-02-03 01:56:40 +00004792 pub struct Derived {
Devin Jeanpierrea2be2a22022-05-18 18:59:05 -07004793 // TODO(b/232984274): delete this.
4794 // Currently, our tests use C++14 instead of C++17. In C++14, `Derived`
4795 // is not an aggregate, because it has a base class. C++17 removed this
4796 // restriction, and allows aggregates to have base classes.
Devin Jeanpierrecd5ebf32022-07-08 05:31:55 -07004797 __non_field_data: [::std::mem::MaybeUninit<u8>; 0],
Devin Jeanpierrea2be2a22022-05-18 18:59:05 -07004798 pub x: i16,
4799 }
4800 }
4801 );
4802 Ok(())
4803 }
4804
4805 /// Non-aggregate structs can't be directly initialized, because we add
4806 /// a zero-sized private field to the bindings.
4807 #[test]
4808 fn test_non_aggregate_struct_private_field() -> Result<()> {
4809 let ir = ir_from_cc(
4810 r#"
4811 struct NonAggregate {
4812 NonAggregate() {}
4813
4814 __INT16_TYPE__ x = 0;
4815 };
4816 "#,
4817 )?;
Devin Jeanpierree9850a72022-06-29 19:04:48 -07004818 let rs_api = generate_bindings_tokens(ir)?.rs_api;
Devin Jeanpierrea2be2a22022-05-18 18:59:05 -07004819 assert_rs_matches!(
4820 rs_api,
4821 quote! {
4822 pub struct NonAggregate {
Devin Jeanpierrecd5ebf32022-07-08 05:31:55 -07004823 __non_field_data: [::std::mem::MaybeUninit<u8>; 0],
Devin Jeanpierre1221c2a2022-05-05 22:36:22 -07004824 pub x: i16,
Devin Jeanpierrec80e6242022-02-03 01:56:40 +00004825 }
4826 }
4827 );
4828 Ok(())
4829 }
4830
Devin Jeanpierreb69bcae2022-02-03 09:45:50 +00004831 /// When a field is [[no_unique_address]], it occupies the space up to the
4832 /// next field.
4833 #[test]
4834 fn test_no_unique_address() -> Result<()> {
4835 let ir = ir_from_cc(
4836 r#"
4837 class Field1 {__INT64_TYPE__ x;};
4838 class Field2 {char y;};
4839 struct Struct final {
4840 [[no_unique_address]] Field1 field1;
4841 [[no_unique_address]] Field2 field2;
4842 __INT16_TYPE__ z;
4843 };
4844 "#,
4845 )?;
Devin Jeanpierree9850a72022-06-29 19:04:48 -07004846 let rs_api = generate_bindings_tokens(ir)?.rs_api;
Devin Jeanpierreb69bcae2022-02-03 09:45:50 +00004847 assert_rs_matches!(
4848 rs_api,
4849 quote! {
Devin Jeanpierreb69bcae2022-02-03 09:45:50 +00004850 #[repr(C, align(8))]
4851 pub struct Struct {
Devin Jeanpierrecd5ebf32022-07-08 05:31:55 -07004852 pub(crate) field1: [::std::mem::MaybeUninit<u8>; 8],
4853 pub(crate) field2: [::std::mem::MaybeUninit<u8>; 2],
Devin Jeanpierreb69bcae2022-02-03 09:45:50 +00004854 pub z: i16,
4855 }
Devin Jeanpierrec0543eb2022-04-20 16:00:34 -07004856 }
4857 );
Devin Jeanpierre27450132022-04-11 13:52:01 -07004858 assert_rs_matches!(
4859 rs_api,
4860 quote! {
Devin Jeanpierre58181ac2022-02-14 21:30:05 +00004861 impl Struct {
Rosica Dejanovska8283fe62022-05-09 10:04:28 -07004862 pub fn field1(&self) -> &crate::Field1 {
4863 unsafe {&* (&self.field1 as *const _ as *const crate::Field1)}
Devin Jeanpierre58181ac2022-02-14 21:30:05 +00004864 }
Rosica Dejanovska8283fe62022-05-09 10:04:28 -07004865 pub fn field2(&self) -> &crate::Field2 {
4866 unsafe {&* (&self.field2 as *const _ as *const crate::Field2)}
Devin Jeanpierre58181ac2022-02-14 21:30:05 +00004867 }
4868 }
Devin Jeanpierreb69bcae2022-02-03 09:45:50 +00004869 }
4870 );
4871 Ok(())
4872 }
4873
4874 /// When a [[no_unique_address]] field is the last one, it occupies the rest
4875 /// of the object.
4876 #[test]
4877 fn test_no_unique_address_last_field() -> Result<()> {
4878 let ir = ir_from_cc(
4879 r#"
4880 class Field1 {__INT64_TYPE__ x;};
4881 class Field2 {char y;};
4882 struct Struct final {
4883 [[no_unique_address]] Field1 field1;
4884 [[no_unique_address]] Field2 field2;
4885 };
4886 "#,
4887 )?;
Devin Jeanpierree9850a72022-06-29 19:04:48 -07004888 let rs_api = generate_bindings_tokens(ir)?.rs_api;
Devin Jeanpierreb69bcae2022-02-03 09:45:50 +00004889 assert_rs_matches!(
4890 rs_api,
4891 quote! {
Devin Jeanpierreb69bcae2022-02-03 09:45:50 +00004892 #[repr(C, align(8))]
4893 pub struct Struct {
Devin Jeanpierrecd5ebf32022-07-08 05:31:55 -07004894 pub(crate) field1: [::std::mem::MaybeUninit<u8>; 8],
4895 pub(crate) field2: [::std::mem::MaybeUninit<u8>; 8],
Devin Jeanpierreb69bcae2022-02-03 09:45:50 +00004896 }
4897 }
4898 );
4899 Ok(())
4900 }
4901
4902 #[test]
4903 fn test_no_unique_address_empty() -> Result<()> {
4904 let ir = ir_from_cc(
4905 r#"
4906 class Field {};
4907 struct Struct final {
4908 [[no_unique_address]] Field field;
4909 int x;
4910 };
4911 "#,
4912 )?;
Devin Jeanpierree9850a72022-06-29 19:04:48 -07004913 let rs_api = generate_bindings_tokens(ir)?.rs_api;
Devin Jeanpierreb69bcae2022-02-03 09:45:50 +00004914 assert_rs_matches!(
4915 rs_api,
4916 quote! {
4917 #[repr(C, align(4))]
4918 pub struct Struct {
Devin Jeanpierrecd5ebf32022-07-08 05:31:55 -07004919 pub(crate) field: [::std::mem::MaybeUninit<u8>; 0],
Devin Jeanpierreb69bcae2022-02-03 09:45:50 +00004920 pub x: i32,
4921 }
4922 }
4923 );
4924 Ok(())
4925 }
4926
4927 #[test]
4928 fn test_base_class_subobject_empty_last_field() -> Result<()> {
4929 let ir = ir_from_cc(
4930 r#"
4931 class Field {};
4932 struct Struct final {
4933 [[no_unique_address]] Field field;
4934 };
4935 "#,
4936 )?;
Devin Jeanpierree9850a72022-06-29 19:04:48 -07004937 let rs_api = generate_bindings_tokens(ir)?.rs_api;
Devin Jeanpierreb69bcae2022-02-03 09:45:50 +00004938 assert_rs_matches!(
4939 rs_api,
4940 quote! {
4941 #[repr(C)]
4942 pub struct Struct {
Devin Jeanpierrecd5ebf32022-07-08 05:31:55 -07004943 pub(crate) field: [::std::mem::MaybeUninit<u8>; 1],
Devin Jeanpierreb69bcae2022-02-03 09:45:50 +00004944 }
4945 }
4946 );
4947 Ok(())
4948 }
4949
Devin Jeanpierrec80e6242022-02-03 01:56:40 +00004950 #[test]
Teddy Katz76fa42b2022-02-23 01:22:56 +00004951 fn test_generate_enum_basic() -> Result<()> {
4952 let ir = ir_from_cc("enum Color { kRed = 5, kBlue };")?;
Devin Jeanpierree9850a72022-06-29 19:04:48 -07004953 let rs_api = generate_bindings_tokens(ir)?.rs_api;
Teddy Katz76fa42b2022-02-23 01:22:56 +00004954 assert_rs_matches!(
4955 rs_api,
4956 quote! {
4957 #[repr(transparent)]
4958 #[derive(Debug, PartialEq, Eq, Copy, Clone, Hash, PartialOrd, Ord)]
4959 pub struct Color(u32);
4960 impl Color {
4961 pub const kRed: Color = Color(5);
4962 pub const kBlue: Color = Color(6);
4963 }
4964 impl From<u32> for Color {
4965 fn from(value: u32) -> Color {
Marcel Hlopko872b41a2022-05-10 01:49:33 -07004966 Color(value)
Teddy Katz76fa42b2022-02-23 01:22:56 +00004967 }
4968 }
4969 impl From<Color> for u32 {
4970 fn from(value: Color) -> u32 {
Marcel Hlopko872b41a2022-05-10 01:49:33 -07004971 value.0
Teddy Katz76fa42b2022-02-23 01:22:56 +00004972 }
4973 }
4974 }
4975 );
4976 Ok(())
4977 }
4978
4979 #[test]
4980 fn test_generate_scoped_enum_basic() -> Result<()> {
4981 let ir = ir_from_cc("enum class Color { kRed = -5, kBlue };")?;
Devin Jeanpierree9850a72022-06-29 19:04:48 -07004982 let rs_api = generate_bindings_tokens(ir)?.rs_api;
Teddy Katz76fa42b2022-02-23 01:22:56 +00004983 assert_rs_matches!(
4984 rs_api,
4985 quote! {
4986 #[repr(transparent)]
4987 #[derive(Debug, PartialEq, Eq, Copy, Clone, Hash, PartialOrd, Ord)]
4988 pub struct Color(i32);
4989 impl Color {
4990 pub const kRed: Color = Color(-5);
4991 pub const kBlue: Color = Color(-4);
4992 }
4993 impl From<i32> for Color {
4994 fn from(value: i32) -> Color {
Marcel Hlopko872b41a2022-05-10 01:49:33 -07004995 Color(value)
Teddy Katz76fa42b2022-02-23 01:22:56 +00004996 }
4997 }
4998 impl From<Color> for i32 {
4999 fn from(value: Color) -> i32 {
Marcel Hlopko872b41a2022-05-10 01:49:33 -07005000 value.0
Teddy Katz76fa42b2022-02-23 01:22:56 +00005001 }
5002 }
5003 }
5004 );
5005 Ok(())
5006 }
5007
5008 #[test]
5009 fn test_generate_enum_with_64_bit_signed_vals() -> Result<()> {
5010 let ir = ir_from_cc(
5011 "enum Color : long { kViolet = -9223372036854775807 - 1LL, kRed = -5, kBlue, kGreen = 3, kMagenta = 9223372036854775807 };",
5012 )?;
Devin Jeanpierree9850a72022-06-29 19:04:48 -07005013 let rs_api = generate_bindings_tokens(ir)?.rs_api;
Teddy Katz76fa42b2022-02-23 01:22:56 +00005014 assert_rs_matches!(
5015 rs_api,
5016 quote! {
5017 #[repr(transparent)]
5018 #[derive(Debug, PartialEq, Eq, Copy, Clone, Hash, PartialOrd, Ord)]
5019 pub struct Color(i64);
5020 impl Color {
5021 pub const kViolet: Color = Color(-9223372036854775808);
5022 pub const kRed: Color = Color(-5);
5023 pub const kBlue: Color = Color(-4);
5024 pub const kGreen: Color = Color(3);
5025 pub const kMagenta: Color = Color(9223372036854775807);
5026 }
5027 impl From<i64> for Color {
5028 fn from(value: i64) -> Color {
Marcel Hlopko872b41a2022-05-10 01:49:33 -07005029 Color(value)
Teddy Katz76fa42b2022-02-23 01:22:56 +00005030 }
5031 }
5032 impl From<Color> for i64 {
5033 fn from(value: Color) -> i64 {
Marcel Hlopko872b41a2022-05-10 01:49:33 -07005034 value.0
Teddy Katz76fa42b2022-02-23 01:22:56 +00005035 }
5036 }
5037 }
5038 );
5039 Ok(())
5040 }
5041
5042 #[test]
5043 fn test_generate_enum_with_64_bit_unsigned_vals() -> Result<()> {
5044 let ir = ir_from_cc(
5045 "enum Color: unsigned long { kRed, kBlue, kLimeGreen = 18446744073709551615 };",
5046 )?;
Devin Jeanpierree9850a72022-06-29 19:04:48 -07005047 let rs_api = generate_bindings_tokens(ir)?.rs_api;
Teddy Katz76fa42b2022-02-23 01:22:56 +00005048 assert_rs_matches!(
5049 rs_api,
5050 quote! {
5051 #[repr(transparent)]
5052 #[derive(Debug, PartialEq, Eq, Copy, Clone, Hash, PartialOrd, Ord)]
5053 pub struct Color(u64);
5054 impl Color {
5055 pub const kRed: Color = Color(0);
5056 pub const kBlue: Color = Color(1);
5057 pub const kLimeGreen: Color = Color(18446744073709551615);
5058 }
5059 impl From<u64> for Color {
5060 fn from(value: u64) -> Color {
Marcel Hlopko872b41a2022-05-10 01:49:33 -07005061 Color(value)
Teddy Katz76fa42b2022-02-23 01:22:56 +00005062 }
5063 }
5064 impl From<Color> for u64 {
5065 fn from(value: Color) -> u64 {
Marcel Hlopko872b41a2022-05-10 01:49:33 -07005066 value.0
Teddy Katz76fa42b2022-02-23 01:22:56 +00005067 }
5068 }
5069 }
5070 );
5071 Ok(())
5072 }
5073
5074 #[test]
5075 fn test_generate_enum_with_32_bit_signed_vals() -> Result<()> {
5076 let ir = ir_from_cc(
5077 "enum Color { kViolet = -2147483647 - 1, kRed = -5, kBlue, kGreen = 3, kMagenta = 2147483647 };",
5078 )?;
Devin Jeanpierree9850a72022-06-29 19:04:48 -07005079 let rs_api = generate_bindings_tokens(ir)?.rs_api;
Teddy Katz76fa42b2022-02-23 01:22:56 +00005080 assert_rs_matches!(
5081 rs_api,
5082 quote! {
5083 #[repr(transparent)]
5084 #[derive(Debug, PartialEq, Eq, Copy, Clone, Hash, PartialOrd, Ord)]
5085 pub struct Color(i32);
5086 impl Color {
5087 pub const kViolet: Color = Color(-2147483648);
5088 pub const kRed: Color = Color(-5);
5089 pub const kBlue: Color = Color(-4);
5090 pub const kGreen: Color = Color(3);
5091 pub const kMagenta: Color = Color(2147483647);
5092 }
5093 impl From<i32> for Color {
5094 fn from(value: i32) -> Color {
Marcel Hlopko872b41a2022-05-10 01:49:33 -07005095 Color(value)
Teddy Katz76fa42b2022-02-23 01:22:56 +00005096 }
5097 }
5098 impl From<Color> for i32 {
5099 fn from(value: Color) -> i32 {
Marcel Hlopko872b41a2022-05-10 01:49:33 -07005100 value.0
Teddy Katz76fa42b2022-02-23 01:22:56 +00005101 }
5102 }
5103 }
5104 );
5105 Ok(())
5106 }
5107
5108 #[test]
5109 fn test_generate_enum_with_32_bit_unsigned_vals() -> Result<()> {
5110 let ir = ir_from_cc("enum Color: unsigned int { kRed, kBlue, kLimeGreen = 4294967295 };")?;
Devin Jeanpierree9850a72022-06-29 19:04:48 -07005111 let rs_api = generate_bindings_tokens(ir)?.rs_api;
Teddy Katz76fa42b2022-02-23 01:22:56 +00005112 assert_rs_matches!(
5113 rs_api,
5114 quote! {
5115 #[repr(transparent)]
5116 #[derive(Debug, PartialEq, Eq, Copy, Clone, Hash, PartialOrd, Ord)]
5117 pub struct Color(u32);
5118 impl Color {
5119 pub const kRed: Color = Color(0);
5120 pub const kBlue: Color = Color(1);
5121 pub const kLimeGreen: Color = Color(4294967295);
5122 }
5123 impl From<u32> for Color {
5124 fn from(value: u32) -> Color {
Marcel Hlopko872b41a2022-05-10 01:49:33 -07005125 Color(value)
Teddy Katz76fa42b2022-02-23 01:22:56 +00005126 }
5127 }
5128 impl From<Color> for u32 {
5129 fn from(value: Color) -> u32 {
Marcel Hlopko872b41a2022-05-10 01:49:33 -07005130 value.0
Teddy Katz76fa42b2022-02-23 01:22:56 +00005131 }
5132 }
5133 }
5134 );
5135 Ok(())
5136 }
5137
5138 #[test]
Michael Forster409d9412021-10-07 08:35:29 +00005139 fn test_doc_comment_func() -> Result<()> {
Marcel Hlopko89547752021-12-10 09:39:41 +00005140 let ir = ir_from_cc(
5141 "
5142 // Doc Comment
5143 // with two lines
5144 int func();",
5145 )?;
Michael Forster409d9412021-10-07 08:35:29 +00005146
Marcel Hlopko89547752021-12-10 09:39:41 +00005147 assert_rs_matches!(
Devin Jeanpierree9850a72022-06-29 19:04:48 -07005148 generate_bindings_tokens(ir)?.rs_api,
Marcel Hlopko89547752021-12-10 09:39:41 +00005149 // leading space is intentional so there is a space between /// and the text of the
5150 // comment
5151 quote! {
5152 #[doc = " Doc Comment\n with two lines"]
5153 #[inline(always)]
5154 pub fn func
5155 }
Michael Forster409d9412021-10-07 08:35:29 +00005156 );
5157
5158 Ok(())
5159 }
5160
5161 #[test]
5162 fn test_doc_comment_record() -> Result<()> {
Marcel Hlopko89547752021-12-10 09:39:41 +00005163 let ir = ir_from_cc(
5164 "// Doc Comment\n\
5165 //\n\
5166 // * with bullet\n\
Devin Jeanpierre88343c72022-01-15 01:10:23 +00005167 struct SomeStruct final {\n\
Marcel Hlopko89547752021-12-10 09:39:41 +00005168 // Field doc\n\
5169 int field;\
5170 };",
5171 )?;
Michael Forster028800b2021-10-05 12:39:59 +00005172
Marcel Hlopko89547752021-12-10 09:39:41 +00005173 assert_rs_matches!(
Devin Jeanpierree9850a72022-06-29 19:04:48 -07005174 generate_bindings_tokens(ir)?.rs_api,
Marcel Hlopko89547752021-12-10 09:39:41 +00005175 quote! {
5176 #[doc = " Doc Comment\n \n * with bullet"]
5177 #[derive(Clone, Copy)]
5178 #[repr(C)]
5179 pub struct SomeStruct {
5180 # [doc = " Field doc"]
5181 pub field: i32,
5182 }
5183 }
Michael Forstercc5941a2021-10-07 07:12:24 +00005184 );
Michael Forster028800b2021-10-05 12:39:59 +00005185 Ok(())
5186 }
Devin Jeanpierre91de7012021-10-21 12:53:51 +00005187
Devin Jeanpierre96839c12021-12-14 00:27:38 +00005188 #[test]
Teddy Katzd2cd1422022-04-04 09:41:33 -07005189 fn test_basic_union() -> Result<()> {
5190 let ir = ir_from_cc(
5191 r#"
Devin Jeanpierree9be70a2022-07-25 08:58:54 -07005192 #pragma clang lifetime_elision
Teddy Katzd2cd1422022-04-04 09:41:33 -07005193 union SomeUnion {
5194 int some_field;
5195 long long some_bigger_field;
5196 };
5197 "#,
5198 )?;
Devin Jeanpierree9850a72022-06-29 19:04:48 -07005199 let BindingsTokens { rs_api, rs_api_impl } = generate_bindings_tokens(ir)?;
Teddy Katzd2cd1422022-04-04 09:41:33 -07005200
5201 assert_rs_matches!(
5202 rs_api,
5203 quote! {
5204 #[derive(Clone, Copy)]
5205 #[repr(C)]
5206 pub union SomeUnion {
5207 pub some_field: i32,
5208 pub some_bigger_field: i64,
5209 }
5210 }
5211 );
Marcel Hlopko4c29c6f2022-05-04 00:49:14 -07005212 assert_cc_matches!(
5213 rs_api_impl,
5214 quote! {
5215 extern "C" void __rust_thunk___ZN9SomeUnionC1Ev(union SomeUnion*__this) {...}
5216 }
5217 );
Marcel Hlopko4c29c6f2022-05-04 00:49:14 -07005218 assert_cc_matches!(rs_api_impl, quote! { static_assert(sizeof(union SomeUnion)==8) });
5219 assert_cc_matches!(rs_api_impl, quote! { static_assert(alignof(union SomeUnion)==8) });
5220 assert_cc_matches!(
5221 rs_api_impl,
Lukasz Anforowicz30360ba2022-05-23 12:15:12 -07005222 quote! { static_assert(CRUBIT_OFFSET_OF(some_field, union SomeUnion)==0) }
Marcel Hlopko4c29c6f2022-05-04 00:49:14 -07005223 );
5224 assert_cc_matches!(
5225 rs_api_impl,
Lukasz Anforowicz30360ba2022-05-23 12:15:12 -07005226 quote! { static_assert(CRUBIT_OFFSET_OF(some_bigger_field, union SomeUnion)==0) }
Marcel Hlopko4c29c6f2022-05-04 00:49:14 -07005227 );
Teddy Katzd2cd1422022-04-04 09:41:33 -07005228 Ok(())
5229 }
5230
5231 #[test]
Marcel Hlopkof05621b2022-05-25 00:26:06 -07005232 fn test_union_with_opaque_field() -> Result<()> {
5233 let ir = ir_from_cc(
5234 r#"
5235 union MyUnion {
5236 char first_field[56];
5237 int second_field;
5238 };
5239 "#,
5240 )?;
Devin Jeanpierree9850a72022-06-29 19:04:48 -07005241 let rs_api = generate_bindings_tokens(ir)?.rs_api;
Marcel Hlopkof05621b2022-05-25 00:26:06 -07005242
5243 assert_rs_matches!(
5244 rs_api,
5245 quote! {
5246 #[repr(C, align(4))]
5247 pub union MyUnion { ...
Devin Jeanpierrecd5ebf32022-07-08 05:31:55 -07005248 first_field: [::std::mem::MaybeUninit<u8>; 56],
Marcel Hlopkof05621b2022-05-25 00:26:06 -07005249 pub second_field: i32,
5250 }
5251 }
5252 );
5253
5254 assert_rs_matches!(
5255 rs_api,
Devin Jeanpierrecd5ebf32022-07-08 05:31:55 -07005256 quote! { const _: () = assert!(::std::mem::size_of::<crate::MyUnion>() == 56); }
Marcel Hlopkof05621b2022-05-25 00:26:06 -07005257 );
5258 assert_rs_matches!(
5259 rs_api,
Devin Jeanpierrecd5ebf32022-07-08 05:31:55 -07005260 quote! { const _: () = assert!(::std::mem::align_of::<crate::MyUnion>() == 4); }
Marcel Hlopkof05621b2022-05-25 00:26:06 -07005261 );
5262 Ok(())
5263 }
5264
5265 #[test]
Marcel Hlopkofa9a3952022-05-10 01:34:13 -07005266 // TODO(https://github.com/Gilnaa/memoffset/issues/66): generate assertions for unions once
5267 // offsetof supports them.
5268 fn test_currently_no_offset_assertions_for_unions() -> Result<()> {
5269 let ir = ir_from_cc(
5270 r#"
5271 union SomeUnion {
5272 int some_field;
5273 long long some_bigger_field;
5274 };
5275 "#,
5276 )?;
Devin Jeanpierree9850a72022-06-29 19:04:48 -07005277 let BindingsTokens { rs_api, .. } = generate_bindings_tokens(ir)?;
Marcel Hlopkofa9a3952022-05-10 01:34:13 -07005278
5279 assert_rs_not_matches!(rs_api, quote! { offset_of! });
5280 Ok(())
5281 }
5282
5283 #[test]
Teddy Katzd2cd1422022-04-04 09:41:33 -07005284 fn test_union_with_private_fields() -> Result<()> {
5285 let ir = ir_from_cc(
5286 r#"
5287 union SomeUnionWithPrivateFields {
5288 public:
5289 int public_field;
5290 private:
5291 long long private_field;
5292 };
5293 "#,
5294 )?;
Devin Jeanpierree9850a72022-06-29 19:04:48 -07005295 let rs_api = generate_bindings_tokens(ir)?.rs_api;
Teddy Katzd2cd1422022-04-04 09:41:33 -07005296
5297 assert_rs_matches!(
5298 rs_api,
5299 quote! {
5300 #[derive(Clone, Copy)]
Lukasz Anforowiczdf8fcae2022-06-02 14:54:43 -07005301 #[repr(C, align(8))]
Teddy Katzd2cd1422022-04-04 09:41:33 -07005302 pub union SomeUnionWithPrivateFields {
5303 pub public_field: i32,
Lukasz Anforowiczdf8fcae2022-06-02 14:54:43 -07005304 #[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 -07005305 pub(crate) private_field: [::std::mem::MaybeUninit<u8>; 8],
Teddy Katzd2cd1422022-04-04 09:41:33 -07005306 }
5307 }
5308 );
5309
5310 assert_rs_matches!(
5311 rs_api,
5312 quote! {
Devin Jeanpierrecd5ebf32022-07-08 05:31:55 -07005313 const _: () = assert!(::std::mem::size_of::<crate::SomeUnionWithPrivateFields>() == 8);
5314 const _: () = assert!(::std::mem::align_of::<crate::SomeUnionWithPrivateFields>() == 8);
Teddy Katzd2cd1422022-04-04 09:41:33 -07005315 const _: () = {
Rosica Dejanovska8283fe62022-05-09 10:04:28 -07005316 static_assertions::assert_impl_all!(crate::SomeUnionWithPrivateFields: Clone);
Teddy Katzd2cd1422022-04-04 09:41:33 -07005317 };
5318 const _: () = {
Rosica Dejanovska8283fe62022-05-09 10:04:28 -07005319 static_assertions::assert_impl_all!(crate::SomeUnionWithPrivateFields: Copy);
Teddy Katzd2cd1422022-04-04 09:41:33 -07005320 };
5321 const _: () = {
Lukasz Anforowiczb4d17782022-07-07 08:09:13 -07005322 static_assertions::assert_not_impl_any!(crate::SomeUnionWithPrivateFields: Drop);
Teddy Katzd2cd1422022-04-04 09:41:33 -07005323 };
Teddy Katzd2cd1422022-04-04 09:41:33 -07005324 }
5325 );
5326 Ok(())
5327 }
5328
5329 #[test]
Marcel Hlopko45465732022-05-24 00:51:04 -07005330 fn test_nontrivial_unions() -> Result<()> {
5331 let ir = ir_from_cc_dependency(
5332 r#"
5333 union UnionWithNontrivialField {
5334 NonTrivialStruct my_field;
5335 };
5336 "#,
5337 r#"
5338 struct NonTrivialStruct {
5339 NonTrivialStruct(NonTrivialStruct&&);
5340 };
5341 "#,
5342 )?;
Devin Jeanpierree9850a72022-06-29 19:04:48 -07005343 let rs_api = generate_bindings_tokens(ir)?.rs_api;
Marcel Hlopko45465732022-05-24 00:51:04 -07005344
5345 assert_rs_not_matches!(rs_api, quote! {derive ( ... Copy ... )});
5346 assert_rs_not_matches!(rs_api, quote! {derive ( ... Clone ... )});
Devin Jeanpierre190b90a2022-05-24 06:00:34 -07005347 assert_rs_matches!(
5348 rs_api,
5349 quote! {
Devin Jeanpierrecd5ebf32022-07-08 05:31:55 -07005350 #[::ctor::recursively_pinned]
Devin Jeanpierre190b90a2022-05-24 06:00:34 -07005351 #[repr(C)]
5352 pub union UnionWithNontrivialField { ... }
5353 }
5354 );
Marcel Hlopko45465732022-05-24 00:51:04 -07005355 Ok(())
5356 }
5357
5358 #[test]
Devin Jeanpierre1221c2a2022-05-05 22:36:22 -07005359 fn test_empty_struct() -> Result<()> {
5360 let ir = ir_from_cc(
5361 r#"
5362 struct EmptyStruct final {};
5363 "#,
5364 )?;
Devin Jeanpierree9850a72022-06-29 19:04:48 -07005365 let rs_api = generate_bindings_tokens(ir)?.rs_api;
Devin Jeanpierre1221c2a2022-05-05 22:36:22 -07005366
5367 assert_rs_matches!(
5368 rs_api,
5369 quote! {
5370 #[derive(Clone, Copy)]
5371 #[repr(C)]
5372 pub struct EmptyStruct {
Devin Jeanpierrecd5ebf32022-07-08 05:31:55 -07005373 __non_field_data: [::std::mem::MaybeUninit<u8>; 1],
Devin Jeanpierre1221c2a2022-05-05 22:36:22 -07005374 }
5375 }
5376 );
5377
5378 assert_rs_matches!(
5379 rs_api,
5380 quote! {
Devin Jeanpierrecd5ebf32022-07-08 05:31:55 -07005381 const _: () = assert!(::std::mem::size_of::<crate::EmptyStruct>() == 1);
5382 const _: () = assert!(::std::mem::align_of::<crate::EmptyStruct>() == 1);
Devin Jeanpierre1221c2a2022-05-05 22:36:22 -07005383 }
5384 );
5385
5386 Ok(())
5387 }
5388
5389 #[test]
Teddy Katzd2cd1422022-04-04 09:41:33 -07005390 fn test_empty_union() -> Result<()> {
5391 let ir = ir_from_cc(
5392 r#"
5393 union EmptyUnion {};
5394 "#,
5395 )?;
Devin Jeanpierree9850a72022-06-29 19:04:48 -07005396 let rs_api = generate_bindings_tokens(ir)?.rs_api;
Teddy Katzd2cd1422022-04-04 09:41:33 -07005397
5398 assert_rs_matches!(
5399 rs_api,
5400 quote! {
5401 #[derive(Clone, Copy)]
5402 #[repr(C)]
5403 pub union EmptyUnion {
Devin Jeanpierrecd5ebf32022-07-08 05:31:55 -07005404 __non_field_data: [::std::mem::MaybeUninit<u8>; 1],
Teddy Katzd2cd1422022-04-04 09:41:33 -07005405 }
5406 }
5407 );
5408
5409 assert_rs_matches!(
5410 rs_api,
5411 quote! {
Devin Jeanpierrecd5ebf32022-07-08 05:31:55 -07005412 const _: () = assert!(::std::mem::size_of::<crate::EmptyUnion>() == 1);
5413 const _: () = assert!(::std::mem::align_of::<crate::EmptyUnion>() == 1);
Teddy Katzd2cd1422022-04-04 09:41:33 -07005414 }
5415 );
5416
5417 Ok(())
5418 }
5419
5420 #[test]
5421 fn test_union_field_with_nontrivial_destructor() -> Result<()> {
5422 let ir = ir_from_cc(
5423 r#"
5424 struct NontrivialStruct { ~NontrivialStruct(); };
5425 union UnionWithNontrivialField {
5426 int trivial_field;
5427 NontrivialStruct nontrivial_field;
5428 };
5429 "#,
5430 )?;
Devin Jeanpierree9850a72022-06-29 19:04:48 -07005431 let rs_api = generate_bindings_tokens(ir)?.rs_api;
Teddy Katzd2cd1422022-04-04 09:41:33 -07005432
5433 assert_rs_matches!(
5434 rs_api,
5435 quote! {
Teddy Katzd2cd1422022-04-04 09:41:33 -07005436 #[repr(C)]
5437 pub union UnionWithNontrivialField {
5438 pub trivial_field: i32,
Devin Jeanpierrecd5ebf32022-07-08 05:31:55 -07005439 pub nontrivial_field: ::std::mem::ManuallyDrop<crate::NontrivialStruct>,
Teddy Katzd2cd1422022-04-04 09:41:33 -07005440 }
5441 }
5442 );
5443
5444 assert_rs_matches!(
5445 rs_api,
5446 quote! {
Devin Jeanpierrecd5ebf32022-07-08 05:31:55 -07005447 const _: () = assert!(::std::mem::size_of::<crate::UnionWithNontrivialField>() == 4);
5448 const _: () = assert!(::std::mem::align_of::<crate::UnionWithNontrivialField>() == 4);
Teddy Katzd2cd1422022-04-04 09:41:33 -07005449 }
5450 );
5451 Ok(())
5452 }
5453
5454 #[test]
5455 fn test_union_with_constructors() -> Result<()> {
5456 let ir = ir_from_cc(
5457 r#"
5458 #pragma clang lifetime_elision
5459 union UnionWithDefaultConstructors {
5460 int a;
5461 };
5462 "#,
5463 )?;
Devin Jeanpierree9850a72022-06-29 19:04:48 -07005464 let rs_api = generate_bindings_tokens(ir)?.rs_api;
Teddy Katzd2cd1422022-04-04 09:41:33 -07005465
5466 assert_rs_matches!(
5467 rs_api,
5468 quote! {
5469 #[derive(Clone, Copy)]
5470 #[repr(C)]
5471 pub union UnionWithDefaultConstructors {
5472 pub a: i32,
5473 }
5474 }
5475 );
5476
5477 assert_rs_matches!(
5478 rs_api,
5479 quote! {
5480 impl Default for UnionWithDefaultConstructors {
5481 #[inline(always)]
5482 fn default() -> Self {
Devin Jeanpierrecd5ebf32022-07-08 05:31:55 -07005483 let mut tmp = ::std::mem::MaybeUninit::<Self>::zeroed();
Teddy Katzd2cd1422022-04-04 09:41:33 -07005484 unsafe {
Rosica Dejanovska68c11ac2022-05-12 03:48:59 -07005485 crate::detail::__rust_thunk___ZN28UnionWithDefaultConstructorsC1Ev(&mut tmp);
Teddy Katzd2cd1422022-04-04 09:41:33 -07005486 tmp.assume_init()
5487 }
5488 }
5489 }
5490 }
5491 );
5492
5493 assert_rs_matches!(
5494 rs_api,
5495 quote! {
Devin Jeanpierrecd5ebf32022-07-08 05:31:55 -07005496 impl<'b> From<::ctor::RvalueReference<'b, crate::UnionWithDefaultConstructors>> for UnionWithDefaultConstructors {
Teddy Katzd2cd1422022-04-04 09:41:33 -07005497 #[inline(always)]
Devin Jeanpierrecd5ebf32022-07-08 05:31:55 -07005498 fn from(__param_0: ::ctor::RvalueReference<'b, crate::UnionWithDefaultConstructors>) -> Self {
5499 let mut tmp = ::std::mem::MaybeUninit::<Self>::zeroed();
Teddy Katzd2cd1422022-04-04 09:41:33 -07005500 unsafe {
Rosica Dejanovska68c11ac2022-05-12 03:48:59 -07005501 crate::detail::__rust_thunk___ZN28UnionWithDefaultConstructorsC1EOS_(&mut tmp, __param_0);
Teddy Katzd2cd1422022-04-04 09:41:33 -07005502 tmp.assume_init()
5503 }
5504 }
5505 }
5506 }
5507 );
5508
5509 Ok(())
5510 }
5511
5512 #[test]
Devin Jeanpierre56777022022-02-03 01:57:15 +00005513 fn test_unambiguous_public_bases() -> Result<()> {
5514 let ir = ir_from_cc_dependency(
5515 "
5516 struct VirtualBase {};
5517 struct PrivateBase {};
5518 struct ProtectedBase {};
5519 struct UnambiguousPublicBase {};
5520 struct AmbiguousPublicBase {};
5521 struct MultipleInheritance : UnambiguousPublicBase, AmbiguousPublicBase {};
5522 struct Derived : private PrivateBase, protected ProtectedBase, MultipleInheritance, AmbiguousPublicBase, virtual VirtualBase {};
5523 ",
5524 "",
5525 )?;
Devin Jeanpierree9850a72022-06-29 19:04:48 -07005526 let rs_api = generate_bindings_tokens(ir)?.rs_api;
Devin Jeanpierref99db3e2022-04-27 23:10:33 -07005527 assert_rs_matches!(
5528 rs_api,
5529 quote! {
Lukasz Anforowicz90bb7462022-09-01 08:13:28 -07005530 unsafe impl oops::Inherits<crate::VirtualBase> for crate::Derived {
Rosica Dejanovska8283fe62022-05-09 10:04:28 -07005531 unsafe fn upcast_ptr(derived: *const Self) -> *const crate::VirtualBase {
Lukasz Anforowicz3f133972022-09-01 09:01:02 -07005532 crate::detail::__crubit_dynamic_upcast__7Derived__to__11VirtualBase(derived)
Devin Jeanpierref99db3e2022-04-27 23:10:33 -07005533 }
5534 }
5535 }
5536 );
Devin Jeanpierreb368e682022-05-03 02:23:44 -07005537 assert_rs_matches!(
5538 rs_api,
Lukasz Anforowicz90bb7462022-09-01 08:13:28 -07005539 quote! { unsafe impl oops::Inherits<crate::UnambiguousPublicBase> for crate::Derived }
Devin Jeanpierreb368e682022-05-03 02:23:44 -07005540 );
5541 assert_rs_matches!(
5542 rs_api,
Lukasz Anforowicz90bb7462022-09-01 08:13:28 -07005543 quote! { unsafe impl oops::Inherits<crate::MultipleInheritance> for crate::Derived }
Devin Jeanpierreb368e682022-05-03 02:23:44 -07005544 );
5545 assert_rs_not_matches!(
5546 rs_api,
Lukasz Anforowicz90bb7462022-09-01 08:13:28 -07005547 quote! {unsafe impl oops::Inherits<crate::PrivateBase> for crate::Derived}
Devin Jeanpierreb368e682022-05-03 02:23:44 -07005548 );
5549 assert_rs_not_matches!(
5550 rs_api,
Lukasz Anforowicz90bb7462022-09-01 08:13:28 -07005551 quote! {unsafe impl oops::Inherits<crate::ProtectedBase> for crate::Derived}
Devin Jeanpierreb368e682022-05-03 02:23:44 -07005552 );
5553 assert_rs_not_matches!(
5554 rs_api,
Lukasz Anforowicz90bb7462022-09-01 08:13:28 -07005555 quote! {unsafe impl oops::Inherits<crate::AmbiguousPublicBase> for crate::Derived}
Devin Jeanpierreb368e682022-05-03 02:23:44 -07005556 );
Devin Jeanpierre56777022022-02-03 01:57:15 +00005557 Ok(())
5558 }
5559
5560 /// Contrary to intuitions: a base class conversion is ambiguous even if the
5561 /// ambiguity is from a private base class cast that you can't even
5562 /// perform.
5563 ///
5564 /// Explanation (courtesy James Dennett):
5565 ///
5566 /// > Once upon a time, there was a rule in C++ that changing all access
5567 /// > specifiers to "public" would not change the meaning of code.
5568 /// > That's no longer true, but some of its effects can still be seen.
5569 ///
5570 /// So, we need to be sure to not allow casting to privately-ambiguous
5571 /// bases.
5572 #[test]
5573 fn test_unambiguous_public_bases_private_ambiguity() -> Result<()> {
5574 let ir = ir_from_cc_dependency(
5575 "
5576 struct Base {};
5577 struct Intermediate : public Base {};
5578 struct Derived : Base, private Intermediate {};
5579 ",
5580 "",
5581 )?;
Devin Jeanpierree9850a72022-06-29 19:04:48 -07005582 let rs_api = generate_bindings_tokens(ir)?.rs_api;
Rosica Dejanovska8283fe62022-05-09 10:04:28 -07005583 assert_rs_not_matches!(
5584 rs_api,
5585 quote! { unsafe impl oops::Inherits<crate::Base> for Derived }
5586 );
Devin Jeanpierre56777022022-02-03 01:57:15 +00005587 Ok(())
5588 }
5589
5590 #[test]
Devin Jeanpierre96839c12021-12-14 00:27:38 +00005591 fn test_virtual_thunk() -> Result<()> {
5592 let ir = ir_from_cc("struct Polymorphic { virtual void Foo(); };")?;
5593
5594 assert_cc_matches!(
Devin Jeanpierree9850a72022-06-29 19:04:48 -07005595 generate_bindings_tokens(ir)?.rs_api_impl,
Devin Jeanpierre96839c12021-12-14 00:27:38 +00005596 quote! {
Lukasz Anforowiczd4742ff2022-07-11 17:05:02 -07005597 extern "C" void __rust_thunk___ZN11Polymorphic3FooEv(struct Polymorphic * __this)
Devin Jeanpierre96839c12021-12-14 00:27:38 +00005598 }
5599 );
5600 Ok(())
5601 }
5602
Lukasz Anforowicz0b6a6ac2022-03-22 22:32:23 +00005603 #[test]
5604 fn test_custom_abi_thunk() -> Result<()> {
5605 let ir = ir_from_cc(
5606 r#"
5607 float f_vectorcall_calling_convention(float p1, float p2) [[clang::vectorcall]];
5608 double f_c_calling_convention(double p1, double p2);
5609 "#,
5610 )?;
Devin Jeanpierree9850a72022-06-29 19:04:48 -07005611 let BindingsTokens { rs_api, rs_api_impl } = generate_bindings_tokens(ir)?;
Lukasz Anforowicz0b6a6ac2022-03-22 22:32:23 +00005612 assert_rs_matches!(
5613 rs_api,
5614 quote! {
5615 #[inline(always)]
5616 pub fn f_vectorcall_calling_convention(p1: f32, p2: f32) -> f32 {
5617 unsafe {
Rosica Dejanovska68c11ac2022-05-12 03:48:59 -07005618 crate::detail::__rust_thunk___Z31f_vectorcall_calling_conventionff(p1, p2)
Lukasz Anforowicz0b6a6ac2022-03-22 22:32:23 +00005619 }
5620 }
5621 }
5622 );
5623 assert_rs_matches!(
5624 rs_api,
5625 quote! {
5626 #[inline(always)]
5627 pub fn f_c_calling_convention(p1: f64, p2: f64) -> f64 {
Rosica Dejanovska68c11ac2022-05-12 03:48:59 -07005628 unsafe { crate::detail::__rust_thunk___Z22f_c_calling_conventiondd(p1, p2) }
Lukasz Anforowicz0b6a6ac2022-03-22 22:32:23 +00005629 }
5630 }
5631 );
5632 // `link_name` (i.e. no thunk) for `f_c_calling_convention`. No
5633 // `link_name` (i.e. indicates presence of a thunk) for
5634 // `f_vectorcall_calling_convention`.
5635 assert_rs_matches!(
5636 rs_api,
5637 quote! {
5638 mod detail {
5639 #[allow(unused_imports)]
5640 use super::*;
5641 extern "C" {
5642 pub(crate) fn __rust_thunk___Z31f_vectorcall_calling_conventionff(
5643 p1: f32, p2: f32) -> f32;
5644 #[link_name = "_Z22f_c_calling_conventiondd"]
5645 pub(crate) fn __rust_thunk___Z22f_c_calling_conventiondd(
5646 p1: f64, p2: f64) -> f64;
5647 }
5648 }
5649 }
5650 );
5651 // C++ thunk needed for `f_vectorcall_calling_convention`.
5652 assert_cc_matches!(
5653 rs_api_impl,
5654 quote! {
5655 extern "C" float __rust_thunk___Z31f_vectorcall_calling_conventionff(
5656 float p1, float p2) {
Lukasz Anforowicz07b33902022-07-13 15:27:03 -07005657 return f_vectorcall_calling_convention(p1, p2);
Lukasz Anforowicz0b6a6ac2022-03-22 22:32:23 +00005658 }
5659 }
5660 );
5661 // No C++ thunk expected for `f_c_calling_convention`.
5662 assert_cc_not_matches!(rs_api_impl, quote! { f_c_calling_convention });
5663 Ok(())
5664 }
5665
Devin Jeanpierree6e16652021-12-22 15:54:46 +00005666 /// A trivially relocatable final struct is safe to use in Rust as normal,
5667 /// and is Unpin.
5668 #[test]
5669 fn test_no_negative_impl_unpin() -> Result<()> {
5670 let ir = ir_from_cc("struct Trivial final {};")?;
Devin Jeanpierree9850a72022-06-29 19:04:48 -07005671 let rs_api = generate_bindings_tokens(ir)?.rs_api;
Devin Jeanpierrecd5ebf32022-07-08 05:31:55 -07005672 assert_rs_not_matches!(rs_api, quote! {#[::ctor::recursively_pinned]});
Devin Jeanpierree6e16652021-12-22 15:54:46 +00005673 Ok(())
5674 }
5675
5676 /// A non-final struct, even if it's trivial, is not usable by mut
5677 /// reference, and so is !Unpin.
5678 #[test]
5679 fn test_negative_impl_unpin_nonfinal() -> Result<()> {
5680 let ir = ir_from_cc("struct Nonfinal {};")?;
Devin Jeanpierree9850a72022-06-29 19:04:48 -07005681 let rs_api = generate_bindings_tokens(ir)?.rs_api;
Devin Jeanpierrecd5ebf32022-07-08 05:31:55 -07005682 assert_rs_matches!(rs_api, quote! {#[::ctor::recursively_pinned]});
Devin Jeanpierree6e16652021-12-22 15:54:46 +00005683 Ok(())
5684 }
5685
Devin Jeanpierre91de7012021-10-21 12:53:51 +00005686 /// At the least, a trivial type should have no drop impl if or until we add
5687 /// empty drop impls.
5688 #[test]
5689 fn test_no_impl_drop() -> Result<()> {
Googler7cced422021-12-06 11:58:39 +00005690 let ir = ir_from_cc("struct Trivial {};")?;
Devin Jeanpierree9850a72022-06-29 19:04:48 -07005691 let rs_api = generate_bindings_tokens(ir)?.rs_api;
Devin Jeanpierre215ee8e2022-05-16 16:09:41 -07005692 assert_rs_not_matches!(rs_api, quote! {impl Drop});
5693 assert_rs_not_matches!(rs_api, quote! {impl ::ctor::PinnedDrop});
Devin Jeanpierre91de7012021-10-21 12:53:51 +00005694 Ok(())
5695 }
5696
Devin Jeanpierre7e9a1de2021-12-03 08:04:22 +00005697 /// User-defined destructors *must* become Drop impls with ManuallyDrop
5698 /// fields
Devin Jeanpierre91de7012021-10-21 12:53:51 +00005699 #[test]
Devin Jeanpierre7e9a1de2021-12-03 08:04:22 +00005700 fn test_impl_drop_user_defined_destructor() -> Result<()> {
Googler7cced422021-12-06 11:58:39 +00005701 let ir = ir_from_cc(
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00005702 r#" struct NontrivialStruct { ~NontrivialStruct(); };
5703 struct UserDefinedDestructor {
Devin Jeanpierre91de7012021-10-21 12:53:51 +00005704 ~UserDefinedDestructor();
Devin Jeanpierre7e9a1de2021-12-03 08:04:22 +00005705 int x;
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00005706 NontrivialStruct nts;
Devin Jeanpierre91de7012021-10-21 12:53:51 +00005707 };"#,
5708 )?;
Devin Jeanpierree9850a72022-06-29 19:04:48 -07005709 let rs_api = generate_bindings_tokens(ir)?.rs_api;
Lukasz Anforowicz6d553632022-01-06 21:36:14 +00005710 assert_rs_matches!(
5711 rs_api,
5712 quote! {
Devin Jeanpierre215ee8e2022-05-16 16:09:41 -07005713 impl ::ctor::PinnedDrop for UserDefinedDestructor {
Lukasz Anforowicz6d553632022-01-06 21:36:14 +00005714 #[inline(always)]
Devin Jeanpierrecd5ebf32022-07-08 05:31:55 -07005715 unsafe fn pinned_drop<'a>(self: ::std::pin::Pin<&'a mut Self>) {
Devin Jeanpierre215ee8e2022-05-16 16:09:41 -07005716 crate::detail::__rust_thunk___ZN21UserDefinedDestructorD1Ev(self)
Lukasz Anforowicz6d553632022-01-06 21:36:14 +00005717 }
5718 }
5719 }
5720 );
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00005721 assert_rs_matches!(rs_api, quote! {pub x: i32,});
Rosica Dejanovskaaccf00b2022-04-01 13:28:04 -07005722 assert_rs_matches!(
5723 rs_api,
Devin Jeanpierrecd5ebf32022-07-08 05:31:55 -07005724 quote! {pub nts: ::std::mem::ManuallyDrop<crate::NontrivialStruct>,}
Rosica Dejanovskaaccf00b2022-04-01 13:28:04 -07005725 );
Devin Jeanpierre7e9a1de2021-12-03 08:04:22 +00005726 Ok(())
5727 }
5728
Lukasz Anforowicz6d553632022-01-06 21:36:14 +00005729 /// nontrivial types without user-defined destructors should invoke
5730 /// the C++ destructor to preserve the order of field destructions.
Devin Jeanpierre7e9a1de2021-12-03 08:04:22 +00005731 #[test]
5732 fn test_impl_drop_nontrivial_member_destructor() -> Result<()> {
5733 // TODO(jeanpierreda): This would be cleaner if the UserDefinedDestructor code were
5734 // omitted. For example, we simulate it so that UserDefinedDestructor
5735 // comes from another library.
Googler7cced422021-12-06 11:58:39 +00005736 let ir = ir_from_cc(
Devin Jeanpierre88343c72022-01-15 01:10:23 +00005737 r#"struct UserDefinedDestructor final {
Devin Jeanpierre7e9a1de2021-12-03 08:04:22 +00005738 ~UserDefinedDestructor();
5739 };
Devin Jeanpierre88343c72022-01-15 01:10:23 +00005740 struct TrivialStruct final { int i; };
5741 struct NontrivialMembers final {
Devin Jeanpierre7e9a1de2021-12-03 08:04:22 +00005742 UserDefinedDestructor udd;
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00005743 TrivialStruct ts;
Devin Jeanpierre7e9a1de2021-12-03 08:04:22 +00005744 int x;
5745 };"#,
5746 )?;
Devin Jeanpierree9850a72022-06-29 19:04:48 -07005747 let rs_api = generate_bindings_tokens(ir)?.rs_api;
Lukasz Anforowicz6d553632022-01-06 21:36:14 +00005748 assert_rs_matches!(
5749 rs_api,
5750 quote! {
Devin Jeanpierre215ee8e2022-05-16 16:09:41 -07005751 impl ::ctor::PinnedDrop for NontrivialMembers {
Lukasz Anforowicz6d553632022-01-06 21:36:14 +00005752 #[inline(always)]
Devin Jeanpierrecd5ebf32022-07-08 05:31:55 -07005753 unsafe fn pinned_drop<'a>(self: ::std::pin::Pin<&'a mut Self>) {
Devin Jeanpierre215ee8e2022-05-16 16:09:41 -07005754 crate::detail::__rust_thunk___ZN17NontrivialMembersD1Ev(self)
Lukasz Anforowicz6d553632022-01-06 21:36:14 +00005755 }
5756 }
5757 }
5758 );
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00005759 assert_rs_matches!(rs_api, quote! {pub x: i32,});
Rosica Dejanovska8283fe62022-05-09 10:04:28 -07005760 assert_rs_matches!(rs_api, quote! {pub ts: crate::TrivialStruct,});
Lukasz Anforowicz6d553632022-01-06 21:36:14 +00005761 assert_rs_matches!(
5762 rs_api,
Devin Jeanpierrecd5ebf32022-07-08 05:31:55 -07005763 quote! {pub udd: ::std::mem::ManuallyDrop<crate::UserDefinedDestructor>,}
Lukasz Anforowicz6d553632022-01-06 21:36:14 +00005764 );
Devin Jeanpierre7e9a1de2021-12-03 08:04:22 +00005765 Ok(())
5766 }
5767
5768 /// Trivial types (at least those that are mapped to Copy rust types) do not
5769 /// get a Drop impl.
5770 #[test]
5771 fn test_impl_drop_trivial() -> Result<()> {
Googler7cced422021-12-06 11:58:39 +00005772 let ir = ir_from_cc(
Devin Jeanpierre88343c72022-01-15 01:10:23 +00005773 r#"struct Trivial final {
Devin Jeanpierre7e9a1de2021-12-03 08:04:22 +00005774 ~Trivial() = default;
5775 int x;
5776 };"#,
5777 )?;
Devin Jeanpierree9850a72022-06-29 19:04:48 -07005778 let BindingsTokens { rs_api, rs_api_impl } = generate_bindings_tokens(ir)?;
Marcel Hlopko89547752021-12-10 09:39:41 +00005779 assert_rs_not_matches!(rs_api, quote! {impl Drop});
Devin Jeanpierre215ee8e2022-05-16 16:09:41 -07005780 assert_rs_not_matches!(rs_api, quote! {impl ::ctor::PinnedDrop});
Marcel Hlopko89547752021-12-10 09:39:41 +00005781 assert_rs_matches!(rs_api, quote! {pub x: i32});
Devin Jeanpierree9be70a2022-07-25 08:58:54 -07005782 assert_cc_not_matches!(rs_api_impl, quote! { std::destroy_at });
Devin Jeanpierre91de7012021-10-21 12:53:51 +00005783 Ok(())
5784 }
Devin Jeanpierre45cb1162021-10-27 10:54:28 +00005785
5786 #[test]
Lukasz Anforowicze643ec92021-12-22 15:45:15 +00005787 fn test_impl_default_explicitly_defaulted_constructor() -> Result<()> {
5788 let ir = ir_from_cc(
Lukasz Anforowicz95551272022-01-20 00:02:24 +00005789 r#"#pragma clang lifetime_elision
5790 struct DefaultedConstructor final {
Lukasz Anforowicze643ec92021-12-22 15:45:15 +00005791 DefaultedConstructor() = default;
5792 };"#,
5793 )?;
Devin Jeanpierree9850a72022-06-29 19:04:48 -07005794 let BindingsTokens { rs_api, rs_api_impl } = generate_bindings_tokens(ir)?;
Lukasz Anforowicze643ec92021-12-22 15:45:15 +00005795 assert_rs_matches!(
5796 rs_api,
5797 quote! {
5798 impl Default for DefaultedConstructor {
5799 #[inline(always)]
5800 fn default() -> Self {
Devin Jeanpierrecd5ebf32022-07-08 05:31:55 -07005801 let mut tmp = ::std::mem::MaybeUninit::<Self>::zeroed();
Lukasz Anforowicze643ec92021-12-22 15:45:15 +00005802 unsafe {
Rosica Dejanovska68c11ac2022-05-12 03:48:59 -07005803 crate::detail::__rust_thunk___ZN20DefaultedConstructorC1Ev(&mut tmp);
Lukasz Anforowicze643ec92021-12-22 15:45:15 +00005804 tmp.assume_init()
5805 }
5806 }
5807 }
5808 }
5809 );
Lukasz Anforowicze643ec92021-12-22 15:45:15 +00005810 assert_cc_matches!(
5811 rs_api_impl,
5812 quote! {
5813 extern "C" void __rust_thunk___ZN20DefaultedConstructorC1Ev(
Lukasz Anforowiczd4742ff2022-07-11 17:05:02 -07005814 struct DefaultedConstructor* __this) {
Lukasz Anforowicz07b33902022-07-13 15:27:03 -07005815 crubit::construct_at(__this);
Lukasz Anforowicze643ec92021-12-22 15:45:15 +00005816 }
5817 }
5818 );
5819 Ok(())
5820 }
5821
5822 #[test]
Lukasz Anforowicz326c4e42022-01-27 14:43:00 +00005823 fn test_impl_clone_that_propagates_lifetime() -> Result<()> {
5824 // This test covers the case where a single lifetime applies to 1)
5825 // the `__this` parameter and 2) other constructor parameters. For
5826 // example, maybe the newly constructed object needs to have the
5827 // same lifetime as the constructor's parameter. (This might require
5828 // annotating the whole C++ struct with a lifetime, so maybe the
5829 // example below is not fully realistic/accurate...).
Devin Jeanpierre8cd8ae72022-06-29 18:59:40 -07005830 let ir = ir_from_cc(&with_lifetime_macros(
Lukasz Anforowicz326c4e42022-01-27 14:43:00 +00005831 r#"#pragma clang lifetime_elision
5832 struct Foo final {
Martin Brænnee5ba6b62022-06-23 07:38:40 -07005833 Foo(const int& $a i) $a;
Lukasz Anforowicz326c4e42022-01-27 14:43:00 +00005834 };"#,
Martin Brænnee5ba6b62022-06-23 07:38:40 -07005835 ))?;
Devin Jeanpierre8cd8ae72022-06-29 18:59:40 -07005836 let ctor: &Func = ir
5837 .items()
Lukasz Anforowicz326c4e42022-01-27 14:43:00 +00005838 .filter_map(|item| match item {
Devin Jeanpierre8cd8ae72022-06-29 18:59:40 -07005839 Item::Func(func) => Some(&**func),
Lukasz Anforowicz326c4e42022-01-27 14:43:00 +00005840 _ => None,
5841 })
5842 .find(|f| {
5843 matches!(&f.name, UnqualifiedIdentifier::Constructor)
5844 && f.params.get(1).map(|p| p.identifier.identifier == "i").unwrap_or_default()
5845 })
5846 .unwrap();
5847 {
5848 // Double-check that the test scenario set up above uses the same lifetime
5849 // for both of the constructor's parameters: `__this` and `i`.
5850 assert_eq!(ctor.params.len(), 2);
5851 let this_lifetime: LifetimeId =
5852 *ctor.params[0].type_.rs_type.lifetime_args.first().unwrap();
5853 let i_lifetime: LifetimeId =
Devin Jeanpierre8cd8ae72022-06-29 18:59:40 -07005854 *ctor.params[1].type_.rs_type.lifetime_args.first().unwrap();
Lukasz Anforowicz326c4e42022-01-27 14:43:00 +00005855 assert_eq!(i_lifetime, this_lifetime);
5856 }
5857
5858 // Before cl/423346348 the generated Rust code would incorrectly look
5859 // like this (note the mismatched 'a and 'b lifetimes):
5860 // fn from<'b>(i: &'a i32) -> Self
5861 // After this CL, this scenario will result in an explicit error.
Devin Jeanpierree9850a72022-06-29 19:04:48 -07005862 let rs_api = generate_bindings_tokens(ir)?.rs_api;
Devin Jeanpierred7b48102022-03-31 04:15:03 -07005863 assert_rs_not_matches!(rs_api, quote! {impl From});
5864 let rs_api_str = tokens_to_string(rs_api)?;
5865 assert!(rs_api_str.contains(
5866 "// The lifetime of `__this` is unexpectedly also used by another parameter"
5867 ));
Lukasz Anforowicz326c4e42022-01-27 14:43:00 +00005868 Ok(())
5869 }
5870
5871 #[test]
Lukasz Anforowicz9bab8352021-12-22 17:35:31 +00005872 fn test_impl_default_non_trivial_struct() -> Result<()> {
5873 let ir = ir_from_cc(
Lukasz Anforowicz71716b72022-01-26 17:05:05 +00005874 r#"#pragma clang lifetime_elision
5875 struct NonTrivialStructWithConstructors final {
Lukasz Anforowicz9bab8352021-12-22 17:35:31 +00005876 NonTrivialStructWithConstructors();
5877 ~NonTrivialStructWithConstructors(); // Non-trivial
5878 };"#,
5879 )?;
Devin Jeanpierree9850a72022-06-29 19:04:48 -07005880 let rs_api = generate_bindings_tokens(ir)?.rs_api;
Lukasz Anforowicz9bab8352021-12-22 17:35:31 +00005881 assert_rs_not_matches!(rs_api, quote! {impl Default});
5882 Ok(())
5883 }
5884
5885 #[test]
Lukasz Anforowicz71716b72022-01-26 17:05:05 +00005886 fn test_impl_from_for_explicit_conversion_constructor() -> Result<()> {
5887 let ir = ir_from_cc(
5888 r#"#pragma clang lifetime_elision
5889 struct SomeStruct final {
5890 explicit SomeStruct(int i);
5891 };"#,
5892 )?;
Devin Jeanpierree9850a72022-06-29 19:04:48 -07005893 let rs_api = generate_bindings_tokens(ir)?.rs_api;
Lukasz Anforowicz71716b72022-01-26 17:05:05 +00005894 // As discussed in b/214020567 for now we only generate `From::from` bindings
5895 // for *implicit* C++ conversion constructors.
5896 assert_rs_not_matches!(rs_api, quote! {impl From});
5897 Ok(())
5898 }
5899
5900 #[test]
5901 fn test_impl_from_for_implicit_conversion_constructor() -> Result<()> {
5902 let ir = ir_from_cc(
5903 r#"#pragma clang lifetime_elision
5904 struct SomeStruct final {
5905 SomeStruct(int i); // implicit - no `explicit` keyword
5906 };"#,
5907 )?;
Devin Jeanpierree9850a72022-06-29 19:04:48 -07005908 let rs_api = generate_bindings_tokens(ir)?.rs_api;
Lukasz Anforowicz71716b72022-01-26 17:05:05 +00005909 // As discussed in b/214020567 we generate `From::from` bindings for
5910 // *implicit* C++ conversion constructors.
5911 assert_rs_matches!(
5912 rs_api,
5913 quote! {
5914 impl From<i32> for SomeStruct {
5915 #[inline(always)]
5916 fn from(i: i32) -> Self {
Devin Jeanpierrecd5ebf32022-07-08 05:31:55 -07005917 let mut tmp = ::std::mem::MaybeUninit::<Self>::zeroed();
Lukasz Anforowicz71716b72022-01-26 17:05:05 +00005918 unsafe {
Rosica Dejanovska68c11ac2022-05-12 03:48:59 -07005919 crate::detail::__rust_thunk___ZN10SomeStructC1Ei(&mut tmp, i);
Lukasz Anforowicz71716b72022-01-26 17:05:05 +00005920 tmp.assume_init()
5921 }
5922 }
5923 }
5924 }
5925 );
5926 Ok(())
5927 }
5928
5929 #[test]
Lukasz Anforowicz5e623782022-03-14 16:52:23 +00005930 fn test_impl_from_for_implicit_conversion_from_reference() -> Result<()> {
5931 let ir = ir_from_cc(
5932 r#"#pragma clang lifetime_elision
5933 struct SomeOtherStruct final { int i; };
5934 struct StructUnderTest final {
5935 StructUnderTest(const SomeOtherStruct& other); // implicit - no `explicit` keyword
5936 };"#,
5937 )?;
Devin Jeanpierree9850a72022-06-29 19:04:48 -07005938 let rs_api = generate_bindings_tokens(ir)?.rs_api;
Lukasz Anforowicz5e623782022-03-14 16:52:23 +00005939 // This is a regression test for b/223800038: We want to ensure that the
5940 // code says `impl<'b>` (instead of incorrectly declaring that lifetime
5941 // in `fn from<'b>`).
5942 assert_rs_matches!(
5943 rs_api,
5944 quote! {
Rosica Dejanovska8283fe62022-05-09 10:04:28 -07005945 impl<'b> From<&'b crate::SomeOtherStruct> for StructUnderTest {
Lukasz Anforowicz5e623782022-03-14 16:52:23 +00005946 #[inline(always)]
Rosica Dejanovska8283fe62022-05-09 10:04:28 -07005947 fn from(other: &'b crate::SomeOtherStruct) -> Self {
Devin Jeanpierrecd5ebf32022-07-08 05:31:55 -07005948 let mut tmp = ::std::mem::MaybeUninit::<Self>::zeroed();
Lukasz Anforowicz5e623782022-03-14 16:52:23 +00005949 unsafe {
Rosica Dejanovska68c11ac2022-05-12 03:48:59 -07005950 crate::detail::__rust_thunk___ZN15StructUnderTestC1ERK15SomeOtherStruct(
Lukasz Anforowicz5e623782022-03-14 16:52:23 +00005951 &mut tmp, other);
5952 tmp.assume_init()
5953 }
5954 }
5955 }
5956 },
5957 );
5958 Ok(())
5959 }
5960
Devin Jeanpierre5ff493e2022-06-08 12:40:23 -07005961 /// Methods with missing lifetimes for `self` should give a useful error
5962 /// message.
5963 #[test]
5964 fn test_eq_nolifetime() -> Result<()> {
5965 // Missing lifetimes currently only causes hard errors for trait impls,
5966 // not For inherent methods.
5967 let ir = ir_from_cc("struct SomeStruct{SomeStruct& operator=(const SomeStruct&);};")?;
5968
Devin Jeanpierree9850a72022-06-29 19:04:48 -07005969 let rs_api = rs_tokens_to_formatted_string_for_tests(generate_bindings_tokens(ir)?.rs_api)?;
Devin Jeanpierre5ff493e2022-06-08 12:40:23 -07005970 assert!(rs_api.contains(
5971 "// Error while generating bindings for item 'SomeStruct::operator=':\n\
5972 // `self` has no lifetime. Use lifetime annotations or \
5973 `#pragma clang lifetime_elision` to create bindings for this function."
5974 ));
5975 Ok(())
5976 }
5977
Lukasz Anforowicz5e623782022-03-14 16:52:23 +00005978 #[test]
Lukasz Anforowicz732ca642022-02-03 20:58:38 +00005979 fn test_impl_eq_for_member_function() -> Result<()> {
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +00005980 let ir = ir_from_cc(
5981 r#"#pragma clang lifetime_elision
5982 struct SomeStruct final {
5983 inline bool operator==(const SomeStruct& other) const {
5984 return i == other.i;
5985 }
5986 int i;
5987 };"#,
5988 )?;
Devin Jeanpierree9850a72022-06-29 19:04:48 -07005989 let BindingsTokens { rs_api, rs_api_impl } = generate_bindings_tokens(ir)?;
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +00005990 assert_rs_matches!(
5991 rs_api,
5992 quote! {
Rosica Dejanovska8283fe62022-05-09 10:04:28 -07005993 impl PartialEq<crate::SomeStruct> for SomeStruct {
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +00005994 #[inline(always)]
Rosica Dejanovska8283fe62022-05-09 10:04:28 -07005995 fn eq<'a, 'b>(&'a self, other: &'b crate::SomeStruct) -> bool {
Rosica Dejanovska68c11ac2022-05-12 03:48:59 -07005996 unsafe { crate::detail::__rust_thunk___ZNK10SomeStructeqERKS_(self, other) }
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +00005997 }
5998 }
5999 }
6000 );
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +00006001 assert_cc_matches!(
6002 rs_api_impl,
6003 quote! {
6004 extern "C" bool __rust_thunk___ZNK10SomeStructeqERKS_(
Lukasz Anforowicz07b33902022-07-13 15:27:03 -07006005 const struct SomeStruct* __this, const struct SomeStruct* other) {
6006 return __this->operator==(*other);
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +00006007 }
6008 }
6009 );
6010 Ok(())
6011 }
6012
6013 #[test]
Lukasz Anforowicz732ca642022-02-03 20:58:38 +00006014 fn test_impl_eq_for_free_function() -> Result<()> {
6015 let ir = ir_from_cc(
6016 r#"#pragma clang lifetime_elision
6017 struct SomeStruct final { int i; };
6018 bool operator==(const SomeStruct& lhs, const SomeStruct& rhs) {
6019 return lhs.i == rhs.i;
6020 }"#,
6021 )?;
Devin Jeanpierree9850a72022-06-29 19:04:48 -07006022 let rs_api = generate_bindings_tokens(ir)?.rs_api;
Lukasz Anforowicz732ca642022-02-03 20:58:38 +00006023 assert_rs_matches!(
6024 rs_api,
6025 quote! {
Rosica Dejanovska8283fe62022-05-09 10:04:28 -07006026 impl PartialEq<crate::SomeStruct> for SomeStruct {
Lukasz Anforowicz732ca642022-02-03 20:58:38 +00006027 #[inline(always)]
Rosica Dejanovska8283fe62022-05-09 10:04:28 -07006028 fn eq<'a, 'b>(&'a self, rhs: &'b crate::SomeStruct) -> bool {
Rosica Dejanovska68c11ac2022-05-12 03:48:59 -07006029 unsafe { crate::detail::__rust_thunk___ZeqRK10SomeStructS1_(self, rhs) }
Lukasz Anforowicz732ca642022-02-03 20:58:38 +00006030 }
6031 }
6032 }
6033 );
6034 Ok(())
6035 }
6036
6037 #[test]
Devin Jeanpierre9ced4ef2022-06-08 12:39:10 -07006038 fn test_assign() -> Result<()> {
6039 let ir = ir_from_cc(
6040 r#"
6041 #pragma clang lifetime_elision
6042 struct SomeStruct {
6043 SomeStruct& operator=(const SomeStruct& other);
6044 };"#,
6045 )?;
Devin Jeanpierree9850a72022-06-29 19:04:48 -07006046 let BindingsTokens { rs_api, .. } = generate_bindings_tokens(ir)?;
Devin Jeanpierre9ced4ef2022-06-08 12:39:10 -07006047 assert_rs_matches!(
6048 rs_api,
6049 quote! {
6050 impl<'b> ::ctor::Assign<&'b crate::SomeStruct> for SomeStruct {
6051 #[inline(always)]
Devin Jeanpierrecd5ebf32022-07-08 05:31:55 -07006052 fn assign<'a>(self: ::std::pin::Pin<&'a mut Self>, other: &'b crate::SomeStruct) {
Devin Jeanpierre9ced4ef2022-06-08 12:39:10 -07006053 unsafe {
6054 crate::detail::__rust_thunk___ZN10SomeStructaSERKS_(self, other);
6055 }
6056 }
6057 }
6058 }
6059 );
6060 Ok(())
6061 }
6062
6063 #[test]
6064 fn test_assign_nonreference_other() -> Result<()> {
6065 let ir = ir_from_cc(
6066 r#"
6067 #pragma clang lifetime_elision
6068 struct SomeStruct {
6069 SomeStruct& operator=(int other);
6070 };"#,
6071 )?;
Devin Jeanpierree9850a72022-06-29 19:04:48 -07006072 let BindingsTokens { rs_api, .. } = generate_bindings_tokens(ir)?;
Devin Jeanpierre9ced4ef2022-06-08 12:39:10 -07006073 assert_rs_matches!(
6074 rs_api,
6075 quote! {
6076 impl<'b> ::ctor::Assign<&'b crate::SomeStruct> for SomeStruct {
6077 #[inline(always)]
Devin Jeanpierrecd5ebf32022-07-08 05:31:55 -07006078 fn assign<'a>(self: ::std::pin::Pin<&'a mut Self>, __param_0: &'b crate::SomeStruct) {
Devin Jeanpierre9ced4ef2022-06-08 12:39:10 -07006079 unsafe {
6080 crate::detail::__rust_thunk___ZN10SomeStructaSERKS_(self, __param_0);
6081 }
6082 }
6083 }
6084 }
6085 );
6086 Ok(())
6087 }
6088
6089 #[test]
6090 fn test_assign_nonreference_return() -> Result<()> {
6091 let ir = ir_from_cc(
6092 r#"
6093 #pragma clang lifetime_elision
6094 struct SomeStruct {
6095 int operator=(const SomeStruct& other);
6096 };"#,
6097 )?;
Devin Jeanpierree9850a72022-06-29 19:04:48 -07006098 let BindingsTokens { rs_api, .. } = generate_bindings_tokens(ir)?;
Devin Jeanpierre9ced4ef2022-06-08 12:39:10 -07006099 assert_rs_matches!(
6100 rs_api,
6101 quote! {
6102 impl<'b> ::ctor::Assign<&'b crate::SomeStruct> for SomeStruct {
6103 #[inline(always)]
Devin Jeanpierrecd5ebf32022-07-08 05:31:55 -07006104 fn assign<'a>(self: ::std::pin::Pin<&'a mut Self>, other: &'b crate::SomeStruct) {
Devin Jeanpierre9ced4ef2022-06-08 12:39:10 -07006105 unsafe {
6106 crate::detail::__rust_thunk___ZN10SomeStructaSERKS_(self, other);
6107 }
6108 }
6109 }
6110 }
6111 );
6112 Ok(())
6113 }
6114
6115 #[test]
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +00006116 fn test_impl_eq_non_const_member_function() -> Result<()> {
6117 let ir = ir_from_cc(
6118 r#"#pragma clang lifetime_elision
6119 struct SomeStruct final {
6120 bool operator==(const SomeStruct& other) /* no `const` here */;
6121 };"#,
6122 )?;
Devin Jeanpierree9850a72022-06-29 19:04:48 -07006123 let rs_api = generate_bindings_tokens(ir)?.rs_api;
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +00006124 assert_rs_not_matches!(rs_api, quote! {impl PartialEq});
6125 Ok(())
6126 }
6127
6128 #[test]
6129 fn test_impl_eq_rhs_by_value() -> Result<()> {
6130 let ir = ir_from_cc(
6131 r#"#pragma clang lifetime_elision
6132 struct SomeStruct final {
6133 bool operator==(SomeStruct other) const;
6134 };"#,
6135 )?;
Devin Jeanpierree9850a72022-06-29 19:04:48 -07006136 let rs_api = generate_bindings_tokens(ir)?.rs_api;
Lukasz Anforowiczfae90a12022-02-03 20:58:15 +00006137 assert_rs_not_matches!(rs_api, quote! {impl PartialEq});
6138 Ok(())
6139 }
6140
6141 #[test]
Dmitri Gribenko67cbfd22022-03-24 13:39:34 +00006142 fn test_thunk_ident_function() -> Result<()> {
6143 let ir = ir_from_cc("inline int foo() {}")?;
6144 let func = retrieve_func(&ir, "foo");
Devin Jeanpierre409f6f62022-07-07 02:00:26 -07006145 assert_eq!(thunk_ident(&func), make_rs_ident("__rust_thunk___Z3foov"));
Dmitri Gribenko67cbfd22022-03-24 13:39:34 +00006146 Ok(())
Devin Jeanpierre45cb1162021-10-27 10:54:28 +00006147 }
6148
6149 #[test]
6150 fn test_thunk_ident_special_names() {
Marcel Hlopko4b13b962021-12-06 12:40:56 +00006151 let ir = ir_from_cc("struct Class {};").unwrap();
Devin Jeanpierre45cb1162021-10-27 10:54:28 +00006152
Googler45ad2752021-12-06 12:12:35 +00006153 let destructor =
6154 ir.functions().find(|f| f.name == UnqualifiedIdentifier::Destructor).unwrap();
Lukasz Anforowiczdd9ae0f2022-02-17 15:52:53 +00006155 assert_eq!(thunk_ident(destructor), make_rs_ident("__rust_thunk___ZN5ClassD1Ev"));
Devin Jeanpierre45cb1162021-10-27 10:54:28 +00006156
Lukasz Anforowicz49b5bbc2022-02-04 23:40:10 +00006157 let default_constructor = ir
6158 .functions()
6159 .find(|f| f.name == UnqualifiedIdentifier::Constructor && f.params.len() == 1)
6160 .unwrap();
Lukasz Anforowiczdd9ae0f2022-02-17 15:52:53 +00006161 assert_eq!(thunk_ident(default_constructor), make_rs_ident("__rust_thunk___ZN5ClassC1Ev"));
Devin Jeanpierre45cb1162021-10-27 10:54:28 +00006162 }
Googler7cced422021-12-06 11:58:39 +00006163
6164 #[test]
Marcel Hlopko89547752021-12-10 09:39:41 +00006165 fn test_elided_lifetimes() -> Result<()> {
Googler7cced422021-12-06 11:58:39 +00006166 let ir = ir_from_cc(
6167 r#"#pragma clang lifetime_elision
Devin Jeanpierre88343c72022-01-15 01:10:23 +00006168 struct S final {
Googler7cced422021-12-06 11:58:39 +00006169 int& f(int& i);
6170 };"#,
Marcel Hlopko89547752021-12-10 09:39:41 +00006171 )?;
Devin Jeanpierree9850a72022-06-29 19:04:48 -07006172 let rs_api = generate_bindings_tokens(ir)?.rs_api;
Marcel Hlopko89547752021-12-10 09:39:41 +00006173 assert_rs_matches!(
6174 rs_api,
6175 quote! {
Lukasz Anforowicz231a3bb2022-01-12 14:05:59 +00006176 pub fn f<'a, 'b>(&'a mut self, i: &'b mut i32) -> &'a mut i32 { ... }
Marcel Hlopko89547752021-12-10 09:39:41 +00006177 }
Googler7cced422021-12-06 11:58:39 +00006178 );
Marcel Hlopko89547752021-12-10 09:39:41 +00006179 assert_rs_matches!(
6180 rs_api,
6181 quote! {
Rosica Dejanovska8283fe62022-05-09 10:04:28 -07006182 pub(crate) fn __rust_thunk___ZN1S1fERi<'a, 'b>(__this: &'a mut crate::S, i: &'b mut i32)
Googler6804a012022-01-05 07:04:36 +00006183 -> &'a mut i32;
Marcel Hlopko89547752021-12-10 09:39:41 +00006184 }
Googler7cced422021-12-06 11:58:39 +00006185 );
Marcel Hlopko89547752021-12-10 09:39:41 +00006186 Ok(())
Googler7cced422021-12-06 11:58:39 +00006187 }
Lukasz Anforowiczdaff0402021-12-23 00:37:50 +00006188
6189 #[test]
Googler386e5942022-02-24 08:53:29 +00006190 fn test_annotated_lifetimes() -> Result<()> {
Martin Brænnee5ba6b62022-06-23 07:38:40 -07006191 let ir = ir_from_cc(&with_lifetime_macros(
6192 r#"
6193 int& $a f(int& $a i1, int& $a i2);
Googler386e5942022-02-24 08:53:29 +00006194 "#,
Martin Brænnee5ba6b62022-06-23 07:38:40 -07006195 ))?;
Devin Jeanpierree9850a72022-06-29 19:04:48 -07006196 let rs_api = generate_bindings_tokens(ir)?.rs_api;
Googler386e5942022-02-24 08:53:29 +00006197 assert_rs_matches!(
6198 rs_api,
6199 quote! {
6200 pub fn f<'a>(i1: &'a mut i32, i2: &'a mut i32) -> &'a mut i32 { ... }
6201 }
6202 );
6203 assert_rs_matches!(
6204 rs_api,
6205 quote! {
6206 pub(crate) fn __rust_thunk___Z1fRiS_<'a>(i1: &'a mut i32, i2: &'a mut i32)
6207 -> &'a mut i32;
6208 }
6209 );
6210 Ok(())
6211 }
6212
6213 #[test]
Lukasz Anforowiczdaff0402021-12-23 00:37:50 +00006214 fn test_format_generic_params() -> Result<()> {
6215 assert_rs_matches!(format_generic_params(std::iter::empty::<syn::Ident>()), quote! {});
6216
Marcel Hlopkoeaae9b72022-01-21 15:54:11 +00006217 let idents = ["T1", "T2"].iter().map(|s| make_rs_ident(s));
Lukasz Anforowiczdaff0402021-12-23 00:37:50 +00006218 assert_rs_matches!(format_generic_params(idents), quote! { < T1, T2 > });
6219
6220 let lifetimes = ["a", "b"]
6221 .iter()
6222 .map(|s| syn::Lifetime::new(&format!("'{}", s), proc_macro2::Span::call_site()));
6223 assert_rs_matches!(format_generic_params(lifetimes), quote! { < 'a, 'b > });
6224
6225 Ok(())
6226 }
Googlerd03d05b2022-01-07 10:10:57 +00006227
6228 #[test]
Devin Jeanpierread125742022-04-11 13:50:28 -07006229 fn test_format_tuple_except_singleton() {
6230 fn format(xs: &[TokenStream]) -> TokenStream {
6231 format_tuple_except_singleton(xs)
6232 }
6233 assert_rs_matches!(format(&[]), quote! {()});
6234 assert_rs_matches!(format(&[quote! {a}]), quote! {a});
6235 assert_rs_matches!(format(&[quote! {a}, quote! {b}]), quote! {(a, b)});
6236 }
6237
6238 #[test]
Googlerd03d05b2022-01-07 10:10:57 +00006239 fn test_overloaded_functions() -> Result<()> {
6240 // TODO(b/213280424): We don't support creating bindings for overloaded
6241 // functions yet, except in the case of overloaded constructors with a
6242 // single parameter.
6243 let ir = ir_from_cc(
Lukasz Anforowicz55673c92022-01-27 19:37:26 +00006244 r#" #pragma clang lifetime_elision
6245 void f();
Googlerd03d05b2022-01-07 10:10:57 +00006246 void f(int i);
Devin Jeanpierre88343c72022-01-15 01:10:23 +00006247 struct S1 final {
Googlerd03d05b2022-01-07 10:10:57 +00006248 void f();
6249 void f(int i);
6250 };
Devin Jeanpierre88343c72022-01-15 01:10:23 +00006251 struct S2 final {
Googlerd03d05b2022-01-07 10:10:57 +00006252 void f();
6253 };
Devin Jeanpierre88343c72022-01-15 01:10:23 +00006254 struct S3 final {
Googlerd03d05b2022-01-07 10:10:57 +00006255 S3(int i);
6256 S3(double d);
6257 };
Lukasz Anforowicz8d064202022-09-01 07:31:06 -07006258
6259 namespace foo { void not_overloaded(); }
6260 namespace bar { void not_overloaded(); }
Googlerd03d05b2022-01-07 10:10:57 +00006261 "#,
6262 )?;
Devin Jeanpierree9850a72022-06-29 19:04:48 -07006263 let rs_api = generate_bindings_tokens(ir)?.rs_api;
Googlerd03d05b2022-01-07 10:10:57 +00006264 let rs_api_str = tokens_to_string(rs_api.clone())?;
6265
6266 // Cannot overload free functions.
6267 assert!(rs_api_str.contains("Error while generating bindings for item 'f'"));
6268 assert_rs_not_matches!(rs_api, quote! {pub fn f()});
6269 assert_rs_not_matches!(rs_api, quote! {pub fn f(i: i32)});
6270
6271 // Cannot overload member functions.
6272 assert!(rs_api_str.contains("Error while generating bindings for item 'S1::f'"));
6273 assert_rs_not_matches!(rs_api, quote! {pub fn f(... S1 ...)});
6274
6275 // But we can import member functions that have the same name as a free
6276 // function.
Lukasz Anforowicz55673c92022-01-27 19:37:26 +00006277 assert_rs_matches!(rs_api, quote! {pub fn f<'a>(&'a mut self)});
Googlerd03d05b2022-01-07 10:10:57 +00006278
6279 // We can also import overloaded single-parameter constructors.
6280 assert_rs_matches!(rs_api, quote! {impl From<i32> for S3});
6281 assert_rs_matches!(rs_api, quote! {impl From<f64> for S3});
Lukasz Anforowicz8d064202022-09-01 07:31:06 -07006282
6283 // And we can import functions that have the same name + signature, but that are
6284 // in 2 different namespaces.
6285 assert_rs_matches!(rs_api, quote! { pub fn not_overloaded() });
Googlerd03d05b2022-01-07 10:10:57 +00006286 Ok(())
6287 }
Googlerdcca7f72022-01-10 12:30:43 +00006288
6289 #[test]
6290 fn test_type_alias() -> Result<()> {
6291 let ir = ir_from_cc(
6292 r#"
Lukasz Anforowiczc503af42022-03-04 23:18:15 +00006293 // MyTypedefDecl doc comment
Googlerdcca7f72022-01-10 12:30:43 +00006294 typedef int MyTypedefDecl;
Lukasz Anforowiczc503af42022-03-04 23:18:15 +00006295
Googlerdcca7f72022-01-10 12:30:43 +00006296 using MyTypeAliasDecl = int;
Googler6a0a5252022-01-11 14:08:09 +00006297 using MyTypeAliasDecl_Alias = MyTypeAliasDecl;
6298
Devin Jeanpierre88343c72022-01-15 01:10:23 +00006299 struct S final {};
Googler6a0a5252022-01-11 14:08:09 +00006300 using S_Alias = S;
6301 using S_Alias_Alias = S_Alias;
6302
6303 inline void f(MyTypedefDecl t) {}
Googlerdcca7f72022-01-10 12:30:43 +00006304 "#,
6305 )?;
Devin Jeanpierree9850a72022-06-29 19:04:48 -07006306 let BindingsTokens { rs_api, rs_api_impl } = generate_bindings_tokens(ir)?;
Lukasz Anforowiczc503af42022-03-04 23:18:15 +00006307 assert_rs_matches!(
6308 rs_api,
6309 quote! {
6310 #[doc = " MyTypedefDecl doc comment"]
6311 pub type MyTypedefDecl = i32;
6312 }
6313 );
Googler6a0a5252022-01-11 14:08:09 +00006314 assert_rs_matches!(rs_api, quote! { pub type MyTypeAliasDecl = i32; });
Rosica Dejanovska8283fe62022-05-09 10:04:28 -07006315 assert_rs_matches!(
6316 rs_api,
6317 quote! { pub type MyTypeAliasDecl_Alias = crate::MyTypeAliasDecl; }
6318 );
6319 assert_rs_matches!(rs_api, quote! { pub type S_Alias = crate::S; });
6320 assert_rs_matches!(rs_api, quote! { pub type S_Alias_Alias = crate::S_Alias; });
6321 assert_rs_matches!(rs_api, quote! { pub fn f(t: crate::MyTypedefDecl) });
Googler6a0a5252022-01-11 14:08:09 +00006322 assert_cc_matches!(
Devin Jeanpierre4f06f832022-04-26 15:51:30 -07006323 rs_api_impl,
Googler6a0a5252022-01-11 14:08:09 +00006324 quote! {
Lukasz Anforowicz07b33902022-07-13 15:27:03 -07006325 extern "C" void __rust_thunk___Z1fi(MyTypedefDecl t) { f(t); }
Googler6a0a5252022-01-11 14:08:09 +00006326 }
6327 );
Googlerdcca7f72022-01-10 12:30:43 +00006328 Ok(())
6329 }
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00006330
6331 #[test]
6332 fn test_rs_type_kind_implements_copy() -> Result<()> {
Lukasz Anforowicz20651e32022-02-10 14:52:15 +00006333 let template = r#" LIFETIMES
Devin Jeanpierre88343c72022-01-15 01:10:23 +00006334 struct [[clang::trivial_abi]] TrivialStruct final { int i; };
6335 struct [[clang::trivial_abi]] UserDefinedCopyConstructor final {
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00006336 UserDefinedCopyConstructor(const UserDefinedCopyConstructor&);
6337 };
6338 using IntAlias = int;
6339 using TrivialAlias = TrivialStruct;
6340 using NonTrivialAlias = UserDefinedCopyConstructor;
6341 void func(PARAM_TYPE some_param);
6342 "#;
6343 assert_impl_all!(i32: Copy);
6344 assert_impl_all!(&i32: Copy);
Lukasz Anforowiczb4d17782022-07-07 08:09:13 -07006345 assert_not_impl_any!(&mut i32: Copy);
Lukasz Anforowicz20651e32022-02-10 14:52:15 +00006346 assert_impl_all!(Option<&i32>: Copy);
Lukasz Anforowiczb4d17782022-07-07 08:09:13 -07006347 assert_not_impl_any!(Option<&mut i32>: Copy);
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00006348 assert_impl_all!(*const i32: Copy);
6349 assert_impl_all!(*mut i32: Copy);
Lukasz Anforowicz20651e32022-02-10 14:52:15 +00006350 struct Test {
6351 // Test inputs:
6352 cc: &'static str,
6353 lifetimes: bool,
6354 // Expected test outputs:
6355 rs: &'static str,
6356 is_copy: bool,
6357 }
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00006358 let tests = vec![
6359 // Validity of the next few tests is verified via
6360 // `assert_[not_]impl_all!` static assertions above.
Lukasz Anforowicz20651e32022-02-10 14:52:15 +00006361 Test { cc: "int", lifetimes: true, rs: "i32", is_copy: true },
6362 Test { cc: "const int&", lifetimes: true, rs: "&'a i32", is_copy: true },
6363 Test { cc: "int&", lifetimes: true, rs: "&'a mut i32", is_copy: false },
6364 Test { cc: "const int*", lifetimes: true, rs: "Option<&'a i32>", is_copy: true },
6365 Test { cc: "int*", lifetimes: true, rs: "Option<&'a mut i32>", is_copy: false },
6366 Test { cc: "const int*", lifetimes: false, rs: "*const i32", is_copy: true },
6367 Test { cc: "int*", lifetimes: false, rs: "*mut i32", is_copy: true },
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00006368 // Tests below have been thought-through and verified "manually".
Lukasz Anforowicz20651e32022-02-10 14:52:15 +00006369 // TrivialStruct is expected to derive Copy.
Marcel Hlopko36234892022-05-10 00:39:54 -07006370 Test {
6371 cc: "TrivialStruct",
6372 lifetimes: true,
6373 rs: "crate::TrivialStruct",
6374 is_copy: true,
6375 },
Lukasz Anforowicz20651e32022-02-10 14:52:15 +00006376 Test {
6377 cc: "UserDefinedCopyConstructor",
6378 lifetimes: true,
Rosica Dejanovska8283fe62022-05-09 10:04:28 -07006379 rs: "crate::UserDefinedCopyConstructor",
Lukasz Anforowicz20651e32022-02-10 14:52:15 +00006380 is_copy: false,
6381 },
Rosica Dejanovska8283fe62022-05-09 10:04:28 -07006382 Test { cc: "IntAlias", lifetimes: true, rs: "crate::IntAlias", is_copy: true },
6383 Test { cc: "TrivialAlias", lifetimes: true, rs: "crate::TrivialAlias", is_copy: true },
Marcel Hlopko36234892022-05-10 00:39:54 -07006384 Test {
6385 cc: "NonTrivialAlias",
6386 lifetimes: true,
6387 rs: "crate::NonTrivialAlias",
6388 is_copy: false,
6389 },
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00006390 ];
Lukasz Anforowicz20651e32022-02-10 14:52:15 +00006391 for test in tests.iter() {
6392 let test_name = format!("cc='{}', lifetimes={}", test.cc, test.lifetimes);
6393 let cc_input = template.replace("PARAM_TYPE", test.cc).replace(
6394 "LIFETIMES",
6395 if test.lifetimes { "#pragma clang lifetime_elision" } else { "" },
6396 );
Devin Jeanpierre409f6f62022-07-07 02:00:26 -07006397 let db = db_from_cc(&cc_input)?;
6398 let ir = db.ir();
6399
Lukasz Anforowicz9c663ca2022-02-09 01:33:31 +00006400 let f = retrieve_func(&ir, "func");
Devin Jeanpierre409f6f62022-07-07 02:00:26 -07006401 let t = db.rs_type_kind(f.params[0].type_.rs_type.clone())?;
Lukasz Anforowicz20651e32022-02-10 14:52:15 +00006402
Devin Jeanpierre92ca2612022-04-06 11:35:13 -07006403 let fmt = tokens_to_string(t.to_token_stream())?;
Lukasz Anforowicz20651e32022-02-10 14:52:15 +00006404 assert_eq!(test.rs, fmt, "Testing: {}", test_name);
6405
6406 assert_eq!(test.is_copy, t.implements_copy(), "Testing: {}", test_name);
Lukasz Anforowicze57215c2022-01-12 14:54:16 +00006407 }
6408 Ok(())
6409 }
Lukasz Anforowicza94ab702022-01-14 22:40:25 +00006410
6411 #[test]
6412 fn test_rs_type_kind_is_shared_ref_to_with_lifetimes() -> Result<()> {
Devin Jeanpierre409f6f62022-07-07 02:00:26 -07006413 let db = db_from_cc(
Lukasz Anforowicza94ab702022-01-14 22:40:25 +00006414 "#pragma clang lifetime_elision
6415 struct SomeStruct {};
6416 void foo(const SomeStruct& foo_param);
6417 void bar(SomeStruct& bar_param);",
6418 )?;
Devin Jeanpierre409f6f62022-07-07 02:00:26 -07006419 let ir = db.ir();
Lukasz Anforowicza94ab702022-01-14 22:40:25 +00006420 let record = ir.records().next().unwrap();
Lukasz Anforowicz9c663ca2022-02-09 01:33:31 +00006421 let foo_func = retrieve_func(&ir, "foo");
6422 let bar_func = retrieve_func(&ir, "bar");
Lukasz Anforowicza94ab702022-01-14 22:40:25 +00006423
6424 // const-ref + lifetimes in C++ ===> shared-ref in Rust
6425 assert_eq!(foo_func.params.len(), 1);
6426 let foo_param = &foo_func.params[0];
6427 assert_eq!(&foo_param.identifier.identifier, "foo_param");
Devin Jeanpierre409f6f62022-07-07 02:00:26 -07006428 let foo_type = db.rs_type_kind(foo_param.type_.rs_type.clone())?;
Lukasz Anforowicza94ab702022-01-14 22:40:25 +00006429 assert!(foo_type.is_shared_ref_to(record));
6430 assert!(matches!(foo_type, RsTypeKind::Reference { mutability: Mutability::Const, .. }));
6431
6432 // non-const-ref + lifetimes in C++ ===> mutable-ref in Rust
6433 assert_eq!(bar_func.params.len(), 1);
6434 let bar_param = &bar_func.params[0];
6435 assert_eq!(&bar_param.identifier.identifier, "bar_param");
Devin Jeanpierre409f6f62022-07-07 02:00:26 -07006436 let bar_type = db.rs_type_kind(bar_param.type_.rs_type.clone())?;
Lukasz Anforowicza94ab702022-01-14 22:40:25 +00006437 assert!(!bar_type.is_shared_ref_to(record));
6438 assert!(matches!(bar_type, RsTypeKind::Reference { mutability: Mutability::Mut, .. }));
6439
6440 Ok(())
6441 }
6442
6443 #[test]
6444 fn test_rs_type_kind_is_shared_ref_to_without_lifetimes() -> Result<()> {
Devin Jeanpierre409f6f62022-07-07 02:00:26 -07006445 let db = db_from_cc(
Lukasz Anforowicza94ab702022-01-14 22:40:25 +00006446 "struct SomeStruct {};
6447 void foo(const SomeStruct& foo_param);",
6448 )?;
Devin Jeanpierre409f6f62022-07-07 02:00:26 -07006449 let ir = db.ir();
Lukasz Anforowicza94ab702022-01-14 22:40:25 +00006450 let record = ir.records().next().unwrap();
Lukasz Anforowicz9c663ca2022-02-09 01:33:31 +00006451 let foo_func = retrieve_func(&ir, "foo");
Lukasz Anforowicza94ab702022-01-14 22:40:25 +00006452
6453 // const-ref + *no* lifetimes in C++ ===> const-pointer in Rust
6454 assert_eq!(foo_func.params.len(), 1);
6455 let foo_param = &foo_func.params[0];
6456 assert_eq!(&foo_param.identifier.identifier, "foo_param");
Devin Jeanpierre409f6f62022-07-07 02:00:26 -07006457 let foo_type = db.rs_type_kind(foo_param.type_.rs_type.clone())?;
Lukasz Anforowicza94ab702022-01-14 22:40:25 +00006458 assert!(!foo_type.is_shared_ref_to(record));
6459 assert!(matches!(foo_type, RsTypeKind::Pointer { mutability: Mutability::Const, .. }));
6460
6461 Ok(())
6462 }
Marcel Hlopkoeaae9b72022-01-21 15:54:11 +00006463
6464 #[test]
Lukasz Anforowicz90bdb962022-02-14 21:07:45 +00006465 fn test_rs_type_kind_dfs_iter_ordering() {
6466 // Set up a test input representing: A<B<C>, D<E>>.
6467 let a = {
6468 let b = {
Devin Jeanpierreb2b6cf82022-07-07 01:49:27 -07006469 let c = RsTypeKind::Other { name: "C".into(), type_args: Rc::from([]) };
6470 RsTypeKind::Other { name: "B".into(), type_args: Rc::from([c]) }
Lukasz Anforowicz90bdb962022-02-14 21:07:45 +00006471 };
6472 let d = {
Devin Jeanpierreb2b6cf82022-07-07 01:49:27 -07006473 let e = RsTypeKind::Other { name: "E".into(), type_args: Rc::from([]) };
6474 RsTypeKind::Other { name: "D".into(), type_args: Rc::from([e]) }
Lukasz Anforowicz90bdb962022-02-14 21:07:45 +00006475 };
Devin Jeanpierreb2b6cf82022-07-07 01:49:27 -07006476 RsTypeKind::Other { name: "A".into(), type_args: Rc::from([b, d]) }
Lukasz Anforowicz90bdb962022-02-14 21:07:45 +00006477 };
6478 let dfs_names = a
6479 .dfs_iter()
6480 .map(|t| match t {
Devin Jeanpierre8cd8ae72022-06-29 18:59:40 -07006481 RsTypeKind::Other { name, .. } => &**name,
Lukasz Anforowicz90bdb962022-02-14 21:07:45 +00006482 _ => unreachable!("Only 'other' types are used in this test"),
6483 })
6484 .collect_vec();
6485 assert_eq!(vec!["A", "B", "C", "D", "E"], dfs_names);
6486 }
6487
6488 #[test]
Lukasz Anforowiczcf230fd2022-02-18 19:20:39 +00006489 fn test_rs_type_kind_dfs_iter_ordering_for_func_ptr() {
6490 // Set up a test input representing: fn(A, B) -> C
6491 let f = {
Devin Jeanpierreb2b6cf82022-07-07 01:49:27 -07006492 let a = RsTypeKind::Other { name: "A".into(), type_args: Rc::from(&[][..]) };
6493 let b = RsTypeKind::Other { name: "B".into(), type_args: Rc::from(&[][..]) };
6494 let c = RsTypeKind::Other { name: "C".into(), type_args: Rc::from(&[][..]) };
Devin Jeanpierre8cd8ae72022-06-29 18:59:40 -07006495 RsTypeKind::FuncPtr {
6496 abi: "blah".into(),
Devin Jeanpierreb2b6cf82022-07-07 01:49:27 -07006497 param_types: Rc::from([a, b]),
6498 return_type: Rc::new(c),
Devin Jeanpierre8cd8ae72022-06-29 18:59:40 -07006499 }
Lukasz Anforowiczcf230fd2022-02-18 19:20:39 +00006500 };
6501 let dfs_names = f
6502 .dfs_iter()
6503 .map(|t| match t {
6504 RsTypeKind::FuncPtr { .. } => "fn",
Devin Jeanpierre8cd8ae72022-06-29 18:59:40 -07006505 RsTypeKind::Other { name, .. } => &**name,
Lukasz Anforowiczcf230fd2022-02-18 19:20:39 +00006506 _ => unreachable!("Only FuncPtr and Other kinds are used in this test"),
6507 })
6508 .collect_vec();
6509 assert_eq!(vec!["fn", "A", "B", "C"], dfs_names);
6510 }
6511
6512 #[test]
Lukasz Anforowicz90bdb962022-02-14 21:07:45 +00006513 fn test_rs_type_kind_lifetimes() -> Result<()> {
Devin Jeanpierre409f6f62022-07-07 02:00:26 -07006514 let db = db_from_cc(
Lukasz Anforowicz90bdb962022-02-14 21:07:45 +00006515 r#"
6516 #pragma clang lifetime_elision
6517 using TypeAlias = int&;
6518 struct SomeStruct {};
Devin Jeanpierre48cb5bc2022-06-02 00:50:43 -07006519 void foo(int a, int& b, int&& c, int* d, int** e, TypeAlias f, SomeStruct g); "#,
Lukasz Anforowicz90bdb962022-02-14 21:07:45 +00006520 )?;
Devin Jeanpierre409f6f62022-07-07 02:00:26 -07006521 let ir = db.ir();
Devin Jeanpierre48cb5bc2022-06-02 00:50:43 -07006522 let func = retrieve_func(&ir, "foo");
Devin Jeanpierre409f6f62022-07-07 02:00:26 -07006523 let ret = db.rs_type_kind(func.return_type.rs_type.clone())?;
6524 let a = db.rs_type_kind(func.params[0].type_.rs_type.clone())?;
6525 let b = db.rs_type_kind(func.params[1].type_.rs_type.clone())?;
6526 let c = db.rs_type_kind(func.params[2].type_.rs_type.clone())?;
6527 let d = db.rs_type_kind(func.params[3].type_.rs_type.clone())?;
6528 let e = db.rs_type_kind(func.params[4].type_.rs_type.clone())?;
6529 let f = db.rs_type_kind(func.params[5].type_.rs_type.clone())?;
6530 let g = db.rs_type_kind(func.params[6].type_.rs_type.clone())?;
Lukasz Anforowicz90bdb962022-02-14 21:07:45 +00006531
6532 assert_eq!(0, ret.lifetimes().count()); // No lifetimes on `void`.
6533 assert_eq!(0, a.lifetimes().count()); // No lifetimes on `int`.
6534 assert_eq!(1, b.lifetimes().count()); // `&'a i32` has a single lifetime.
Devin Jeanpierre48cb5bc2022-06-02 00:50:43 -07006535 assert_eq!(1, c.lifetimes().count()); // `RvalueReference<'a, i32>` has a single lifetime.
6536 assert_eq!(1, d.lifetimes().count()); // `Option<&'b i32>` has a single lifetime.
6537 assert_eq!(2, e.lifetimes().count()); // `&'c Option<&'d i32>` has two lifetimes.
6538 assert_eq!(1, f.lifetimes().count()); // Lifetime of underlying type should show through.
6539 assert_eq!(0, g.lifetimes().count()); // No lifetimes on structs (yet).
Lukasz Anforowicz90bdb962022-02-14 21:07:45 +00006540 Ok(())
6541 }
6542
6543 #[test]
6544 fn test_rs_type_kind_lifetimes_raw_ptr() -> Result<()> {
Devin Jeanpierre409f6f62022-07-07 02:00:26 -07006545 let db = db_from_cc("void foo(int* a);")?;
6546 let ir = db.ir();
Lukasz Anforowicz90bdb962022-02-14 21:07:45 +00006547 let f = retrieve_func(&ir, "foo");
Devin Jeanpierre409f6f62022-07-07 02:00:26 -07006548 let a = db.rs_type_kind(f.params[0].type_.rs_type.clone())?;
Lukasz Anforowicz90bdb962022-02-14 21:07:45 +00006549 assert_eq!(0, a.lifetimes().count()); // No lifetimes on `int*`.
6550 Ok(())
6551 }
6552
6553 #[test]
Marcel Hlopkoeaae9b72022-01-21 15:54:11 +00006554 fn test_rust_keywords_are_escaped_in_rs_api_file() -> Result<()> {
6555 let ir = ir_from_cc("struct type { int dyn; };")?;
Devin Jeanpierree9850a72022-06-29 19:04:48 -07006556 let rs_api = generate_bindings_tokens(ir)?.rs_api;
Marcel Hlopkoeaae9b72022-01-21 15:54:11 +00006557 assert_rs_matches!(rs_api, quote! { struct r#type { ... r#dyn: i32 ... } });
6558 Ok(())
6559 }
6560
6561 #[test]
6562 fn test_rust_keywords_are_not_escaped_in_rs_api_impl_file() -> Result<()> {
6563 let ir = ir_from_cc("struct type { int dyn; };")?;
Devin Jeanpierree9850a72022-06-29 19:04:48 -07006564 let rs_api_impl = generate_bindings_tokens(ir)?.rs_api_impl;
Lukasz Anforowicz4ee9c222022-04-13 09:33:36 -07006565 assert_cc_matches!(
6566 rs_api_impl,
Lukasz Anforowiczd4742ff2022-07-11 17:05:02 -07006567 quote! { static_assert(CRUBIT_OFFSET_OF(dyn, struct type) ... ) }
Lukasz Anforowicz4ee9c222022-04-13 09:33:36 -07006568 );
Marcel Hlopkoeaae9b72022-01-21 15:54:11 +00006569 Ok(())
6570 }
Marcel Hlopko14ee3c82022-02-09 09:46:23 +00006571
6572 #[test]
6573 fn test_no_aligned_attr() {
6574 let ir = ir_from_cc("struct SomeStruct {};").unwrap();
Devin Jeanpierree9850a72022-06-29 19:04:48 -07006575 let rs_api = generate_bindings_tokens(ir).unwrap().rs_api;
Marcel Hlopko14ee3c82022-02-09 09:46:23 +00006576
6577 assert_rs_matches! {rs_api, quote! {
6578 #[repr(C)]
6579 pub struct SomeStruct { ... }
6580 }};
6581 }
6582
6583 #[test]
6584 fn test_aligned_attr() {
6585 let ir = ir_from_cc("struct SomeStruct {} __attribute__((aligned(64)));").unwrap();
Devin Jeanpierree9850a72022-06-29 19:04:48 -07006586 let rs_api = generate_bindings_tokens(ir).unwrap().rs_api;
Marcel Hlopko14ee3c82022-02-09 09:46:23 +00006587
6588 assert_rs_matches! {rs_api, quote! {
6589 #[repr(C, align(64))]
6590 pub struct SomeStruct { ... }
6591 }
6592 };
6593 }
Devin Jeanpierre149950d2022-02-22 21:02:02 +00006594
6595 /// !Unpin references should not be pinned.
6596 #[test]
6597 fn test_nonunpin_ref_param() -> Result<()> {
Devin Jeanpierree9850a72022-06-29 19:04:48 -07006598 let rs_api = generate_bindings_tokens(ir_from_cc(
Devin Jeanpierre149950d2022-02-22 21:02:02 +00006599 r#"
6600 #pragma clang lifetime_elision
6601 struct S {~S();};
6602 void Function(const S& s);
6603 "#,
Devin Jeanpierre4f06f832022-04-26 15:51:30 -07006604 )?)?
6605 .rs_api;
Devin Jeanpierre149950d2022-02-22 21:02:02 +00006606 assert_rs_matches!(
Devin Jeanpierre448322b2022-04-26 15:43:40 -07006607 rs_api,
Devin Jeanpierre149950d2022-02-22 21:02:02 +00006608 quote! {
Rosica Dejanovska8283fe62022-05-09 10:04:28 -07006609 fn Function<'a>(s: &'a crate::S) { ... }
Devin Jeanpierre149950d2022-02-22 21:02:02 +00006610 }
6611 );
6612 Ok(())
6613 }
6614
6615 /// !Unpin mut references must be pinned.
6616 #[test]
6617 fn test_nonunpin_mut_param() -> Result<()> {
Devin Jeanpierree9850a72022-06-29 19:04:48 -07006618 let rs_api = generate_bindings_tokens(ir_from_cc(
Devin Jeanpierre149950d2022-02-22 21:02:02 +00006619 r#"
6620 #pragma clang lifetime_elision
6621 struct S {~S();};
6622 void Function(S& s);
6623 "#,
Devin Jeanpierre4f06f832022-04-26 15:51:30 -07006624 )?)?
6625 .rs_api;
Devin Jeanpierre149950d2022-02-22 21:02:02 +00006626 assert_rs_matches!(
Devin Jeanpierre448322b2022-04-26 15:43:40 -07006627 rs_api,
Devin Jeanpierre149950d2022-02-22 21:02:02 +00006628 quote! {
Devin Jeanpierrecd5ebf32022-07-08 05:31:55 -07006629 fn Function<'a>(s: ::std::pin::Pin<&'a mut crate::S>) { ... }
Devin Jeanpierre149950d2022-02-22 21:02:02 +00006630 }
6631 );
6632 Ok(())
6633 }
6634
6635 /// !Unpin &self should not be pinned.
6636 #[test]
6637 fn test_nonunpin_ref_self() -> Result<()> {
Devin Jeanpierree9850a72022-06-29 19:04:48 -07006638 let rs_api = generate_bindings_tokens(ir_from_cc(
Devin Jeanpierre149950d2022-02-22 21:02:02 +00006639 r#"
6640 #pragma clang lifetime_elision
6641 struct S {
6642 ~S();
6643 void Function() const;
6644 };
6645 "#,
Devin Jeanpierre4f06f832022-04-26 15:51:30 -07006646 )?)?
6647 .rs_api;
Devin Jeanpierre149950d2022-02-22 21:02:02 +00006648 assert_rs_matches!(
Devin Jeanpierre448322b2022-04-26 15:43:40 -07006649 rs_api,
Devin Jeanpierre149950d2022-02-22 21:02:02 +00006650 quote! {
6651 fn Function<'a>(&'a self) { ... }
6652 }
6653 );
6654 Ok(())
6655 }
6656
6657 /// !Unpin &mut self must be pinned.
6658 #[test]
6659 fn test_nonunpin_mut_self() -> Result<()> {
Devin Jeanpierree9850a72022-06-29 19:04:48 -07006660 let rs_api = generate_bindings_tokens(ir_from_cc(
Devin Jeanpierre149950d2022-02-22 21:02:02 +00006661 r#"
6662 #pragma clang lifetime_elision
6663 struct S {
6664 ~S();
6665 void Function();
6666 };
6667 "#,
Devin Jeanpierre4f06f832022-04-26 15:51:30 -07006668 )?)?
6669 .rs_api;
Devin Jeanpierre149950d2022-02-22 21:02:02 +00006670 assert_rs_matches!(
Devin Jeanpierre448322b2022-04-26 15:43:40 -07006671 rs_api,
Devin Jeanpierre149950d2022-02-22 21:02:02 +00006672 quote! {
Devin Jeanpierrecd5ebf32022-07-08 05:31:55 -07006673 fn Function<'a>(self: ::std::pin::Pin<&'a mut Self>) { ... }
Devin Jeanpierre149950d2022-02-22 21:02:02 +00006674 }
6675 );
6676 Ok(())
6677 }
6678
6679 /// Drop::drop must not use self : Pin<...>.
6680 #[test]
6681 fn test_nonunpin_drop() -> Result<()> {
Devin Jeanpierree9850a72022-06-29 19:04:48 -07006682 let rs_api = generate_bindings_tokens(ir_from_cc(
Devin Jeanpierre149950d2022-02-22 21:02:02 +00006683 r#"
6684 struct S {~S();};
6685 "#,
Devin Jeanpierre4f06f832022-04-26 15:51:30 -07006686 )?)?
6687 .rs_api;
Devin Jeanpierre149950d2022-02-22 21:02:02 +00006688 assert_rs_matches!(
Devin Jeanpierre448322b2022-04-26 15:43:40 -07006689 rs_api,
Devin Jeanpierre149950d2022-02-22 21:02:02 +00006690 quote! {
Devin Jeanpierrecd5ebf32022-07-08 05:31:55 -07006691 unsafe fn pinned_drop<'a>(self: ::std::pin::Pin<&'a mut Self>) { ... }
Devin Jeanpierre149950d2022-02-22 21:02:02 +00006692 }
6693 );
6694 Ok(())
6695 }
Devin Jeanpierre6f607372022-03-22 21:34:38 +00006696
6697 #[test]
Devin Jeanpierread125742022-04-11 13:50:28 -07006698 fn test_nonunpin_0_arg_constructor() -> Result<()> {
6699 let ir = ir_from_cc(
6700 r#"#pragma clang lifetime_elision
6701 // This type must be `!Unpin`.
6702 struct HasConstructor {explicit HasConstructor() {}};"#,
6703 )?;
Devin Jeanpierree9850a72022-06-29 19:04:48 -07006704 let rs_api = generate_bindings_tokens(ir)?.rs_api;
Devin Jeanpierrecd5ebf32022-07-08 05:31:55 -07006705 assert_rs_matches!(rs_api, quote! {#[::ctor::recursively_pinned]});
Devin Jeanpierread125742022-04-11 13:50:28 -07006706 assert_rs_matches!(
6707 rs_api,
6708 quote! {
Devin Jeanpierrecd5ebf32022-07-08 05:31:55 -07006709 impl ::ctor::CtorNew<()> for HasConstructor {
6710 type CtorType = impl ::ctor::Ctor<Output = Self>;
Devin Jeanpierread125742022-04-11 13:50:28 -07006711
6712 #[inline (always)]
6713 fn ctor_new(args: ()) -> Self::CtorType {
6714 let () = args;
Devin Jeanpierre93927e82022-08-01 14:05:54 -07006715 unsafe {
6716 ::ctor::FnCtor::new(move |dest: ::std::pin::Pin<&mut ::std::mem::MaybeUninit<crate::HasConstructor>>| {
Devin Jeanpierrecd5ebf32022-07-08 05:31:55 -07006717 crate::detail::__rust_thunk___ZN14HasConstructorC1Ev(::std::pin::Pin::into_inner_unchecked(dest));
Devin Jeanpierre93927e82022-08-01 14:05:54 -07006718 })
6719 }
Devin Jeanpierread125742022-04-11 13:50:28 -07006720 }
6721 }
6722 }
6723 );
6724 Ok(())
6725 }
6726
6727 #[test]
6728 fn test_nonunpin_1_arg_constructor() -> Result<()> {
Devin Jeanpierre6f607372022-03-22 21:34:38 +00006729 let ir = ir_from_cc(
6730 r#"#pragma clang lifetime_elision
6731 // This type must be `!Unpin`.
6732 struct HasConstructor {explicit HasConstructor(unsigned char input) {}};"#,
6733 )?;
Devin Jeanpierree9850a72022-06-29 19:04:48 -07006734 let rs_api = generate_bindings_tokens(ir)?.rs_api;
Devin Jeanpierrecd5ebf32022-07-08 05:31:55 -07006735 assert_rs_matches!(rs_api, quote! {#[::ctor::recursively_pinned]});
Devin Jeanpierre6f607372022-03-22 21:34:38 +00006736 assert_rs_matches!(
6737 rs_api,
6738 quote! {
Devin Jeanpierrecd5ebf32022-07-08 05:31:55 -07006739 impl ::ctor::CtorNew<u8> for HasConstructor {
6740 type CtorType = impl ::ctor::Ctor<Output = Self>;
Devin Jeanpierre6f607372022-03-22 21:34:38 +00006741
6742 #[inline (always)]
Devin Jeanpierread125742022-04-11 13:50:28 -07006743 fn ctor_new(args: u8) -> Self::CtorType {
6744 let input = args;
Devin Jeanpierre93927e82022-08-01 14:05:54 -07006745 unsafe {
6746 ::ctor::FnCtor::new(move |dest: ::std::pin::Pin<&mut ::std::mem::MaybeUninit<crate::HasConstructor>>| {
Devin Jeanpierrecd5ebf32022-07-08 05:31:55 -07006747 crate::detail::__rust_thunk___ZN14HasConstructorC1Eh(::std::pin::Pin::into_inner_unchecked(dest), input);
Devin Jeanpierre93927e82022-08-01 14:05:54 -07006748 })
6749 }
Devin Jeanpierre6f607372022-03-22 21:34:38 +00006750 }
6751 }
6752 }
6753 );
6754 Ok(())
6755 }
Devin Jeanpierread125742022-04-11 13:50:28 -07006756
6757 #[test]
6758 fn test_nonunpin_2_arg_constructor() -> Result<()> {
6759 let ir = ir_from_cc(
6760 r#"#pragma clang lifetime_elision
6761 // This type must be `!Unpin`.
6762 struct HasConstructor {explicit HasConstructor(unsigned char input1, signed char input2) {}};"#,
6763 )?;
Devin Jeanpierree9850a72022-06-29 19:04:48 -07006764 let rs_api = generate_bindings_tokens(ir)?.rs_api;
Devin Jeanpierrecd5ebf32022-07-08 05:31:55 -07006765 assert_rs_matches!(rs_api, quote! {#[::ctor::recursively_pinned]});
Devin Jeanpierread125742022-04-11 13:50:28 -07006766 assert_rs_matches!(
6767 rs_api,
6768 quote! {
Devin Jeanpierrecd5ebf32022-07-08 05:31:55 -07006769 impl ::ctor::CtorNew<(u8, i8)> for HasConstructor {
6770 type CtorType = impl ::ctor::Ctor<Output = Self>;
Devin Jeanpierread125742022-04-11 13:50:28 -07006771
6772 #[inline (always)]
6773 fn ctor_new(args: (u8, i8)) -> Self::CtorType {
6774 let (input1, input2) = args;
Devin Jeanpierre93927e82022-08-01 14:05:54 -07006775 unsafe {
6776 ::ctor::FnCtor::new(move |dest: ::std::pin::Pin<&mut ::std::mem::MaybeUninit<crate::HasConstructor>>| {
Devin Jeanpierrecd5ebf32022-07-08 05:31:55 -07006777 crate::detail::__rust_thunk___ZN14HasConstructorC1Eha(::std::pin::Pin::into_inner_unchecked(dest), input1, input2);
Devin Jeanpierre93927e82022-08-01 14:05:54 -07006778 })
6779 }
Devin Jeanpierread125742022-04-11 13:50:28 -07006780 }
6781 }
6782 }
6783 );
6784 Ok(())
6785 }
Rosica Dejanovskadd9a9032022-04-12 07:34:41 -07006786
Devin Jeanpierre6bb81802022-08-10 02:08:47 -07006787 /// Traits which monomorphize the `Ctor` parameter into the caller must
6788 /// synthesize an RvalueReference parameter, with an appropriate
6789 /// lifetime parameter.
6790 #[test]
6791 fn test_nonunpin_by_value_params() -> Result<()> {
6792 let ir = ir_from_cc(
6793 r#"#pragma clang lifetime_elision
6794 // This type must be `!Unpin`.
6795 struct HasConstructor {
6796 // int& x is here to create a 'b lifetime, which collides with a synthesized
6797 // lifetime name. But that's OK! We handle collisions!
6798 // (`a` would also work, but that's just because the left hand doesn't know what
6799 // the right is doing: the `a` lifetime is present in some places, but eventually
6800 // removed from the public interface.)
6801 explicit HasConstructor(const int& x, HasConstructor y, HasConstructor b) {}
6802 };"#,
6803 )?;
6804 let rs_api = generate_bindings_tokens(ir)?.rs_api;
6805 assert_rs_matches!(rs_api, quote! {#[::ctor::recursively_pinned]});
6806 assert_rs_matches!(
6807 rs_api,
6808 quote! {
6809 impl <'b, 'y, 'b_2> ::ctor::CtorNew<(
6810 &'b i32,
6811 ::ctor::RvalueReference<'y, crate::HasConstructor>,
6812 ::ctor::RvalueReference<'b_2, crate::HasConstructor>)
6813 > for HasConstructor {
6814 // The captures are why we need explicit lifetimes for the two rvalue reference
6815 // parameters.
6816 type CtorType = impl ::ctor::Ctor<Output = Self>
6817 + ::ctor::Captures<'b>
6818 + ::ctor::Captures<'y>
6819 + ::ctor::Captures<'b_2>;
6820
6821 #[inline (always)]
6822 fn ctor_new(args: (
6823 &'b i32,
6824 ::ctor::RvalueReference<'y, crate::HasConstructor>,
6825 ::ctor::RvalueReference<'b_2, crate::HasConstructor>)
6826 ) -> Self::CtorType {
6827 let (x, y, b) = args;
6828 unsafe {
6829 ::ctor::FnCtor::new(move |dest: ::std::pin::Pin<&mut ::std::mem::MaybeUninit<crate::HasConstructor>>| {
6830 crate::detail::__rust_thunk___ZN14HasConstructorC1ERKiS_S_(::std::pin::Pin::into_inner_unchecked(dest), x, y, b);
6831 })
6832 }
6833 }
6834 }
6835 }
6836 );
6837 Ok(())
6838 }
6839
Rosica Dejanovskadd9a9032022-04-12 07:34:41 -07006840 #[test]
Devin Jeanpierreb8ce2c12022-07-13 10:34:01 -07006841 fn test_nonunpin_return() -> Result<()> {
6842 let ir = ir_from_cc(
6843 r#"#pragma clang lifetime_elision
6844 // This type must be `!Unpin`.
6845 struct Nontrivial {~Nontrivial();};
Lukasz Anforowicz07b33902022-07-13 15:27:03 -07006846
Devin Jeanpierreb8ce2c12022-07-13 10:34:01 -07006847 Nontrivial ReturnsByValue(const int& x, const int& y);
6848 "#,
6849 )?;
6850 let BindingsTokens { rs_api, rs_api_impl } = generate_bindings_tokens(ir)?;
6851 assert_rs_matches!(
6852 rs_api,
6853 quote! {
6854 pub fn ReturnsByValue<'a, 'b>(x: &'a i32, y: &'b i32)
Devin Jeanpierre83cb2dc2022-07-15 01:50:28 -07006855 -> impl ::ctor::Ctor<Output=crate::Nontrivial>
6856 + ::ctor::Captures<'a>
6857 + ::ctor::Captures<'b> {
Devin Jeanpierreb8ce2c12022-07-13 10:34:01 -07006858 unsafe {
6859 ::ctor::FnCtor::new(move |dest: ::std::pin::Pin<&mut ::std::mem::MaybeUninit<crate::Nontrivial>>| {
6860 crate::detail::__rust_thunk___Z14ReturnsByValueRKiS0_(::std::pin::Pin::into_inner_unchecked(dest), x, y);
6861 })
6862 }
6863
6864 }
6865 }
6866 );
6867
6868 assert_cc_matches!(
6869 rs_api_impl,
6870 quote! {
Lukasz Anforowicz07b33902022-07-13 15:27:03 -07006871 extern "C" void __rust_thunk___Z14ReturnsByValueRKiS0_(
6872 struct Nontrivial* __return, int const* x, int const* y) {
Devin Jeanpierre521b4ee2022-07-27 07:19:50 -07006873 new(__return) auto(ReturnsByValue(*x, *y));
Devin Jeanpierreb8ce2c12022-07-13 10:34:01 -07006874 }
6875 }
6876 );
6877 Ok(())
6878 }
6879
6880 /// Assignment is special in that it discards the return type.
6881 /// So if the return type is !Unpin, it needs to emplace!() it.
6882 #[test]
Devin Jeanpierre11ce2b92022-07-27 07:54:21 -07006883 fn test_nonunpin_return_assign() -> Result<()> {
Devin Jeanpierreb8ce2c12022-07-13 10:34:01 -07006884 let ir = ir_from_cc(
6885 r#"#pragma clang lifetime_elision
6886 // This type must be `!Unpin`.
6887 struct Nontrivial {
6888 ~Nontrivial();
6889 Nontrivial operator=(const Nontrivial& other);
6890 };
6891 "#,
6892 )?;
6893 let BindingsTokens { rs_api, rs_api_impl } = generate_bindings_tokens(ir)?;
6894 assert_rs_matches!(
6895 rs_api,
6896 quote! {
6897 impl<'b> ::ctor::Assign<&'b crate::Nontrivial> for Nontrivial {
6898 #[inline(always)]
6899 fn assign<'a>(self: ::std::pin::Pin<&'a mut Self>, other: &'b crate::Nontrivial) {
6900 unsafe {
6901 let _ = ::ctor::emplace!(::ctor::FnCtor::new(
6902 move |dest: ::std::pin::Pin<&mut ::std::mem::MaybeUninit<crate::Nontrivial>>| {
6903 crate::detail::__rust_thunk___ZN10NontrivialaSERKS_(
6904 ::std::pin::Pin::into_inner_unchecked(dest),
6905 self,
6906 other
6907 );
6908 }
6909 ));
6910 }
6911 }
6912 }
6913 }
6914 );
6915
6916 assert_cc_matches!(
6917 rs_api_impl,
6918 quote! {
6919 extern "C" void __rust_thunk___ZN10NontrivialaSERKS_(
6920 struct Nontrivial* __return, struct Nontrivial* __this,
Lukasz Anforowicz07b33902022-07-13 15:27:03 -07006921 const struct Nontrivial* other
Devin Jeanpierreb8ce2c12022-07-13 10:34:01 -07006922 ) {
Devin Jeanpierre521b4ee2022-07-27 07:19:50 -07006923 new(__return) auto(__this->operator=(*other));
Devin Jeanpierreb8ce2c12022-07-13 10:34:01 -07006924 }
6925 }
6926 );
6927 Ok(())
6928 }
6929
6930 #[test]
Devin Jeanpierre11ce2b92022-07-27 07:54:21 -07006931 fn test_nonunpin_param() -> Result<()> {
6932 let ir = ir_from_cc(
6933 r#"#pragma clang lifetime_elision
6934 // This type must be `!Unpin`.
6935 struct Nontrivial {
6936 Nontrivial(Nontrivial&&);
6937 ~Nontrivial();
6938 };
6939
6940 void TakesByValue(Nontrivial x);
6941 "#,
6942 )?;
6943 let BindingsTokens { rs_api, rs_api_impl } = generate_bindings_tokens(ir)?;
6944 assert_rs_matches!(
6945 rs_api,
6946 quote! {
6947 pub fn TakesByValue(x: impl ::ctor::Ctor<Output=crate::Nontrivial>) {
6948 unsafe {
6949 crate::detail::__rust_thunk___Z12TakesByValue10Nontrivial(::std::pin::Pin::into_inner_unchecked(::ctor::emplace!(x)))
6950 }
6951 }
6952 }
6953 );
6954
6955 assert_cc_matches!(
6956 rs_api_impl,
6957 quote! {
6958 extern "C" void __rust_thunk___Z12TakesByValue10Nontrivial(struct Nontrivial*x) {
6959 TakesByValue(std::move(*x));
6960 }
6961 }
6962 );
6963 Ok(())
6964 }
6965
6966 #[test]
6967 fn test_nonunpin_trait_param() -> Result<()> {
6968 let ir = ir_from_cc(
6969 r#"#pragma clang lifetime_elision
6970 // This type must be `!Unpin`.
6971 struct Nontrivial {
6972 Nontrivial(Nontrivial&&);
6973 Nontrivial& operator=(Nontrivial) {}
6974 ~Nontrivial();
6975 };
Devin Jeanpierre2b1e46d2022-08-01 13:42:12 -07006976
6977 struct Trivial final {
6978 /*implicit*/ Trivial(Nontrivial) {}
6979 };
Devin Jeanpierre11ce2b92022-07-27 07:54:21 -07006980 "#,
6981 )?;
6982 let rs_api = generate_bindings_tokens(ir)?.rs_api;
Devin Jeanpierre2b1e46d2022-08-01 13:42:12 -07006983 assert_rs_matches!(
6984 rs_api,
6985 quote! {
Devin Jeanpierre6bb81802022-08-10 02:08:47 -07006986 impl<'__param_0> From<::ctor::RvalueReference<'__param_0, crate::Nontrivial>> for Trivial {
Devin Jeanpierre2b1e46d2022-08-01 13:42:12 -07006987 #[inline(always)]
Devin Jeanpierre6bb81802022-08-10 02:08:47 -07006988 fn from(__param_0: ::ctor::RvalueReference<'__param_0, crate::Nontrivial>) -> Self {
Devin Jeanpierre2b1e46d2022-08-01 13:42:12 -07006989 let mut tmp = ::std::mem::MaybeUninit::<Self>::zeroed();
6990 unsafe {
6991 crate::detail::__rust_thunk___ZN7TrivialC1E10Nontrivial(
6992 &mut tmp,
6993 __param_0
6994 );
6995 tmp.assume_init()
6996 }
6997 }
6998 }
6999 }
7000 );
Devin Jeanpierre11ce2b92022-07-27 07:54:21 -07007001 Ok(())
7002 }
7003
7004 #[test]
7005 fn test_nonmovable_param() -> Result<()> {
7006 let ir = ir_from_cc(
7007 r#"#pragma clang lifetime_elision
7008 // This type must be `!Unpin` and non-move constructible.
7009 struct Nonmovable {
7010 Nonmovable(Nonmovable&&) = delete;
7011 };
7012
7013 void TakesByValue(Nonmovable) {}
7014 "#,
7015 )?;
7016 let BindingsTokens { rs_api, rs_api_impl } = generate_bindings_tokens(ir)?;
7017 // Bindings for TakesByValue cannot be generated.
7018 assert_rs_not_matches!(rs_api, quote! {TakesByValue});
7019 assert_cc_not_matches!(rs_api_impl, quote! {TakesByValue});
7020 Ok(())
7021 }
7022
7023 #[test]
Lukasz Anforowicz07b33902022-07-13 15:27:03 -07007024 fn test_function_returning_rvalue_reference() -> Result<()> {
7025 let ir = ir_from_cc(
7026 r#"#pragma clang lifetime_elision
7027 struct SomeStruct final {
7028 // Inline to force generation (and test coverage) of C++ thunks.
7029 inline SomeStruct&& GetRValueReference() {
7030 return static_cast<SomeStruct&&>(*this);
7031 }
7032 int field;
7033 };
7034 "#,
7035 )?;
7036 let BindingsTokens { rs_api, rs_api_impl } = generate_bindings_tokens(ir)?;
7037 assert_rs_matches!(
7038 rs_api,
7039 quote! {
7040 impl SomeStruct {
7041 ...
7042 #[inline(always)]
7043 pub fn GetRValueReference<'a>(&'a mut self)
7044 -> ::ctor::RvalueReference<'a, crate::SomeStruct> {
7045 unsafe {
7046 crate::detail::__rust_thunk___ZN10SomeStruct18GetRValueReferenceEv(self)
7047 }
7048 }
7049 }
7050 }
7051 );
7052 assert_rs_matches!(
7053 rs_api,
7054 quote! {
7055 extern "C" {
7056 ...
7057 pub(crate) fn __rust_thunk___ZN10SomeStruct18GetRValueReferenceEv<'a>(
7058 __this: &'a mut crate::SomeStruct
7059 ) -> ::ctor::RvalueReference<'a, crate::SomeStruct>;
7060 ...
7061 }
7062 }
7063 );
7064
7065 // Note that you can't just convert directly from xvalue to lvalue:
7066 //
7067 // return &static_cast<SomeStruct&>(__this->GetRValueReference());
7068 //
7069 // For the above, Clang will emit an error that "non-const lvalue reference to
7070 // type 'struct SomeStruct' cannot bind to a temporary of type
7071 // 'SomeStruct'" (This is somewhat misleading, because there are no
7072 // temporaries here). We must first bind the return value to a name
7073 // (`lvalue` below), so that it becomes an lvalue. Only then can it be
7074 // converted to a pointer.
7075 assert_cc_matches!(
7076 rs_api_impl,
7077 quote! {
7078 extern "C" struct SomeStruct*
7079 __rust_thunk___ZN10SomeStruct18GetRValueReferenceEv(struct SomeStruct* __this) {
7080 struct SomeStruct&& lvalue = __this->GetRValueReference();
7081 return &lvalue;
7082 }
7083 }
7084 );
7085
7086 Ok(())
7087 }
7088
7089 #[test]
Devin Jeanpierrec0543eb2022-04-20 16:00:34 -07007090 fn test_forward_declared() -> Result<()> {
7091 let ir = ir_from_cc(
7092 r#"#pragma clang lifetime_elision
7093 struct ForwardDeclared;"#,
7094 )?;
Devin Jeanpierree9850a72022-06-29 19:04:48 -07007095 let rs_api = generate_bindings_tokens(ir)?.rs_api;
Devin Jeanpierrec0543eb2022-04-20 16:00:34 -07007096 assert_rs_matches!(
7097 rs_api,
7098 quote! {
7099 forward_declare::forward_declare!(pub ForwardDeclared = forward_declare::symbol!("ForwardDeclared"));
7100 }
7101 );
7102 assert_rs_not_matches!(rs_api, quote! {struct ForwardDeclared});
7103 Ok(())
7104 }
7105
7106 #[test]
Rosica Dejanovskadd9a9032022-04-12 07:34:41 -07007107 fn test_namespace_module_items() -> Result<()> {
Devin Jeanpierree9850a72022-06-29 19:04:48 -07007108 let rs_api = generate_bindings_tokens(ir_from_cc(
Rosica Dejanovskadd9a9032022-04-12 07:34:41 -07007109 r#"
7110 namespace test_namespace_bindings {
7111 int func();
7112 struct S {};
7113 namespace inner {
7114 int inner_func();
7115 struct InnerS {};
7116 }
7117 }
7118 "#,
Devin Jeanpierre4f06f832022-04-26 15:51:30 -07007119 )?)?
7120 .rs_api;
Rosica Dejanovskadd9a9032022-04-12 07:34:41 -07007121 assert_rs_matches!(
Devin Jeanpierre448322b2022-04-26 15:43:40 -07007122 rs_api,
Rosica Dejanovskadd9a9032022-04-12 07:34:41 -07007123 quote! {
7124 pub mod test_namespace_bindings {
7125 ...
7126 pub fn func() -> i32 { ... }
7127 ...
7128 pub struct S { ... }
7129 ...
7130 pub mod inner {
7131 ...
7132 pub fn inner_func() -> i32 { ... }
7133 ...
7134 pub struct InnerS { ... }
7135 ...
7136 }
7137 ...
7138 }
7139 }
7140 );
7141 Ok(())
7142 }
7143
7144 #[test]
Rosica Dejanovska5d9faaf2022-05-11 04:30:04 -07007145 fn test_detail_outside_of_namespace_module() -> Result<()> {
Devin Jeanpierree9850a72022-06-29 19:04:48 -07007146 let rs_api = generate_bindings_tokens(ir_from_cc(
Rosica Dejanovskadd9a9032022-04-12 07:34:41 -07007147 r#"
7148 namespace test_namespace_bindings {
7149 int f();
7150 }
7151 "#,
Devin Jeanpierre4f06f832022-04-26 15:51:30 -07007152 )?)?
7153 .rs_api;
Rosica Dejanovskadd9a9032022-04-12 07:34:41 -07007154 assert_rs_matches!(
Devin Jeanpierre448322b2022-04-26 15:43:40 -07007155 rs_api,
Rosica Dejanovskadd9a9032022-04-12 07:34:41 -07007156 quote! {
7157 pub mod test_namespace_bindings {
7158 ...
Rosica Dejanovskadd9a9032022-04-12 07:34:41 -07007159 }
Rosica Dejanovska5d9faaf2022-05-11 04:30:04 -07007160 ...
7161 mod detail {
7162 #[allow(unused_imports)]
7163 use super::*;
7164 extern "C" {
7165 #[link_name = "_ZN23test_namespace_bindings1fEv"]
7166 pub(crate) fn __rust_thunk___ZN23test_namespace_bindings1fEv() -> i32;
7167 }
7168 }
7169 ...
Rosica Dejanovskadd9a9032022-04-12 07:34:41 -07007170 }
7171 );
7172 Ok(())
7173 }
7174
7175 #[test]
Rosica Dejanovska5d9faaf2022-05-11 04:30:04 -07007176 fn test_assertions_outside_of_namespace_module() -> Result<()> {
Devin Jeanpierree9850a72022-06-29 19:04:48 -07007177 let rs_api = generate_bindings_tokens(ir_from_cc(
Rosica Dejanovskadd9a9032022-04-12 07:34:41 -07007178 r#"
7179 namespace test_namespace_bindings {
7180 struct S {
7181 int i;
7182 };
7183 }
7184 "#,
Devin Jeanpierre4f06f832022-04-26 15:51:30 -07007185 )?)?
7186 .rs_api;
Rosica Dejanovskadd9a9032022-04-12 07:34:41 -07007187 assert_rs_matches!(
Devin Jeanpierre448322b2022-04-26 15:43:40 -07007188 rs_api,
Rosica Dejanovskadd9a9032022-04-12 07:34:41 -07007189 quote! {
7190 pub mod test_namespace_bindings {
7191 ...
Rosica Dejanovskadd9a9032022-04-12 07:34:41 -07007192 }
Rosica Dejanovska5d9faaf2022-05-11 04:30:04 -07007193 ...
Devin Jeanpierrecd5ebf32022-07-08 05:31:55 -07007194 const _: () = assert!(::std::mem::size_of::<crate::test_namespace_bindings::S>() == 4);
7195 const _: () = assert!(::std::mem::align_of::<crate::test_namespace_bindings::S>() == 4);
Rosica Dejanovska5d9faaf2022-05-11 04:30:04 -07007196 ...
Lukasz Anforowicz30360ba2022-05-23 12:15:12 -07007197 const _: () = assert!(memoffset_unstable_const::offset_of!(crate::test_namespace_bindings::S, i) == 0);
Rosica Dejanovskadd9a9032022-04-12 07:34:41 -07007198 }
7199 );
7200 Ok(())
7201 }
Rosica Dejanovska93aeafb2022-06-01 07:05:31 -07007202
7203 #[test]
7204 fn test_reopened_namespaces() -> Result<()> {
Devin Jeanpierree9850a72022-06-29 19:04:48 -07007205 let rs_api = generate_bindings_tokens(ir_from_cc(
Rosica Dejanovska93aeafb2022-06-01 07:05:31 -07007206 r#"
7207 namespace test_namespace_bindings {
7208 namespace inner {}
7209 } // namespace test_namespace_bindings
7210
7211 namespace test_namespace_bindings {
7212 namespace inner {}
7213 } // namespace test_namespace_bindings"#,
7214 )?)?
7215 .rs_api;
7216
7217 assert_rs_matches!(
7218 rs_api,
7219 quote! {
7220 ...
7221 pub mod test_namespace_bindings_0 {
7222 pub mod inner_0 {} ...
7223 }
7224 ...
7225 pub mod test_namespace_bindings {
7226 pub use super::test_namespace_bindings_0::*;
7227 ...
7228 pub mod inner {
7229 pub use super::inner_0::*;
7230 ...
7231 }
7232 }
7233 ...
7234 }
7235 );
7236 Ok(())
7237 }
Rosica Dejanovskaa08b3862022-06-02 08:22:49 -07007238
7239 #[test]
7240 fn test_qualified_identifiers_in_impl_file() -> Result<()> {
Devin Jeanpierree9850a72022-06-29 19:04:48 -07007241 let rs_api_impl = generate_bindings_tokens(ir_from_cc(
Rosica Dejanovskaa08b3862022-06-02 08:22:49 -07007242 r#"
7243 namespace test_namespace_bindings {
7244 inline void f() {};
Devin Jeanpierrecc61dad2022-07-19 01:40:09 -07007245 struct S final {};
Rosica Dejanovskaa08b3862022-06-02 08:22:49 -07007246 }
7247 inline void useS(test_namespace_bindings::S s) {};"#,
7248 )?)?
7249 .rs_api_impl;
7250
7251 assert_cc_matches!(
7252 rs_api_impl,
7253 quote! {
7254 extern "C" void __rust_thunk___ZN23test_namespace_bindings1fEv() {
7255 test_namespace_bindings::f();
7256 }
7257 ...
Rosica Dejanovskaa08b3862022-06-02 08:22:49 -07007258 extern "C" void __rust_thunk___Z4useSN23test_namespace_bindings1SE(
Lukasz Anforowicz07b33902022-07-13 15:27:03 -07007259 struct test_namespace_bindings::S s) { useS(s); }
Rosica Dejanovskaa08b3862022-06-02 08:22:49 -07007260 ...
7261 }
7262 );
7263 Ok(())
7264 }
Rosica Dejanovska4180ffe2022-06-09 06:01:05 -07007265
7266 #[test]
7267 fn test_inline_namespace() -> Result<()> {
Devin Jeanpierree9850a72022-06-29 19:04:48 -07007268 let rs_api = generate_bindings_tokens(ir_from_cc(
Rosica Dejanovska4180ffe2022-06-09 06:01:05 -07007269 r#"
7270 namespace test_namespace_bindings {
7271 inline namespace inner {
Devin Jeanpierrecc61dad2022-07-19 01:40:09 -07007272 struct MyStruct final {};
Rosica Dejanovska4180ffe2022-06-09 06:01:05 -07007273 }
7274 void processMyStruct(MyStruct s);
7275 }
7276 void processMyStructOutsideNamespace(test_namespace_bindings::inner::MyStruct s);
7277 void processMyStructSkipInlineNamespaceQualifier(test_namespace_bindings::MyStruct s);
7278 "#,
7279 )?)?
7280 .rs_api;
7281
7282 assert_rs_matches!(
7283 rs_api,
7284 quote! {
7285 ...
7286 pub mod test_namespace_bindings {
7287 ...
7288 pub mod inner {
7289 ...
7290 pub struct MyStruct {...} ...
7291 }
7292 ...
7293 pub fn processMyStruct(s: crate::test_namespace_bindings::inner::MyStruct)
7294 ...
7295 }
7296 ...
7297 pub fn processMyStructOutsideNamespace(s: crate::test_namespace_bindings::inner::MyStruct)
7298 ...
7299 pub fn processMyStructSkipInlineNamespaceQualifier(s: crate::test_namespace_bindings::inner::MyStruct)
7300 ...
7301 }
7302 );
7303 Ok(())
7304 }
Rosica Dejanovska2708a5f2022-06-22 06:54:59 -07007305
7306 #[test]
7307 fn test_implicit_template_specializations_are_sorted_by_mangled_name() -> Result<()> {
Devin Jeanpierree9850a72022-06-29 19:04:48 -07007308 let bindings = generate_bindings_tokens(ir_from_cc(
Rosica Dejanovska2708a5f2022-06-22 06:54:59 -07007309 r#"
7310 template <typename T>
7311 struct MyStruct {
7312 T getT();
7313 };
7314
7315 using Alias1 = MyStruct<int>;
7316 using Alias2 = MyStruct<double>;
7317
7318 namespace test_namespace_bindings {
7319 using Alias3 = MyStruct<bool>;
7320 }
7321 "#,
7322 )?)?;
7323
7324 // Mangled name order: bool < double < int
7325 let my_struct_bool = make_rs_ident("__CcTemplateInst8MyStructIbE");
7326 let my_struct_double = make_rs_ident("__CcTemplateInst8MyStructIdE");
7327 let my_struct_int = make_rs_ident("__CcTemplateInst8MyStructIiE");
7328
7329 assert_rs_matches!(
7330 &bindings.rs_api,
7331 quote! {
7332 ...
7333 pub struct #my_struct_bool {...}
7334 ...
7335 pub struct #my_struct_double {...}
7336 ...
7337 pub struct #my_struct_int {...}
7338 ...
Devin Jeanpierrecd5ebf32022-07-08 05:31:55 -07007339 const _: () = assert!(::std::mem::size_of::<crate::#my_struct_bool>() == 1);
Rosica Dejanovska2708a5f2022-06-22 06:54:59 -07007340 ...
Devin Jeanpierrecd5ebf32022-07-08 05:31:55 -07007341 const _: () = assert!(::std::mem::size_of::<crate::#my_struct_double>() == 1);
Rosica Dejanovska2708a5f2022-06-22 06:54:59 -07007342 ...
Devin Jeanpierrecd5ebf32022-07-08 05:31:55 -07007343 const _: () = assert!(::std::mem::size_of::<crate::#my_struct_int>() == 1);
Rosica Dejanovska2708a5f2022-06-22 06:54:59 -07007344 ...
7345 }
7346 );
7347
Rosica Dejanovska2708a5f2022-06-22 06:54:59 -07007348 // User defined methods in mangled name order
7349 let my_struct_bool_method =
7350 make_rs_ident("__rust_thunk___ZN8MyStructIbE4getTEv__2f_2ftest_3atesting_5ftarget");
7351 let my_struct_double_method =
7352 make_rs_ident("__rust_thunk___ZN8MyStructIdE4getTEv__2f_2ftest_3atesting_5ftarget");
7353 let my_struct_int_method =
7354 make_rs_ident("__rust_thunk___ZN8MyStructIiE4getTEv__2f_2ftest_3atesting_5ftarget");
7355
7356 assert_cc_matches!(
7357 &bindings.rs_api_impl,
7358 quote! {
7359 ...
Lukasz Anforowiczd4742ff2022-07-11 17:05:02 -07007360 extern "C" bool #my_struct_bool_method(struct MyStruct<bool>*__this) {...} ...
7361 extern "C" double #my_struct_double_method(struct MyStruct<double>*__this) {...} ...
7362 extern "C" int #my_struct_int_method(struct MyStruct<int>*__this) {...} ...
Rosica Dejanovska2708a5f2022-06-22 06:54:59 -07007363 }
7364 );
7365 Ok(())
7366 }
Rosica Dejanovskaf9787c92022-06-22 12:14:57 -07007367
7368 #[test]
7369 fn test_implicit_template_specialization_namespace_qualifier() -> Result<()> {
Devin Jeanpierree9850a72022-06-29 19:04:48 -07007370 let rs_api = generate_bindings_tokens(ir_from_cc(
Rosica Dejanovskaf9787c92022-06-22 12:14:57 -07007371 r#" #pragma clang lifetime_elision
7372 namespace test_namespace_bindings {
7373 template <typename T>
7374 struct MyTemplate final {
7375 T value_;
7376 };
7377
7378 using MyTypeAlias = MyTemplate<int>;
7379 }"#,
7380 )?)?
7381 .rs_api;
7382
7383 assert_rs_matches!(
7384 rs_api,
7385 quote! {
7386 ...
7387 pub mod test_namespace_bindings {
7388 ...
7389 pub type MyTypeAlias = crate::__CcTemplateInstN23test_namespace_bindings10MyTemplateIiEE;
7390 ...
7391 }
7392 ...
7393 pub struct __CcTemplateInstN23test_namespace_bindings10MyTemplateIiEE {
7394 pub value_: i32,
7395 }
7396 ...
7397 }
7398 );
7399 Ok(())
7400 }
Rosica Dejanovskae12d7172022-06-22 12:20:17 -07007401
7402 #[test]
7403 fn test_forward_declared_class_template_specialization_symbol() -> Result<()> {
Devin Jeanpierree9850a72022-06-29 19:04:48 -07007404 let rs_api = generate_bindings_tokens(ir_from_cc(
Rosica Dejanovskae12d7172022-06-22 12:20:17 -07007405 r#"
7406 namespace test_namespace_bindings {
7407 template <typename T>
7408 struct MyTemplate {
7409 void processT(T t);
7410 };
7411
7412 struct Param {};
7413
7414 template<> struct MyTemplate<Param>;
7415 }"#,
7416 )?)?
7417 .rs_api;
7418
7419 assert_rs_matches!(
7420 rs_api,
7421 quote! {
7422 ...
7423 pub mod test_namespace_bindings {
7424 ...
7425 forward_declare::forward_declare!(pub __CcTemplateInstN23test_namespace_bindings10MyTemplateINS_5ParamEEE = forward_declare::symbol!("__CcTemplateInstN23test_namespace_bindings10MyTemplateINS_5ParamEEE"));
7426 ...
7427 }
7428 ...
7429 }
7430 );
7431 Ok(())
7432 }
Devin Jeanpierre7ed8c6f2022-08-01 13:41:02 -07007433
7434 #[test]
7435 fn test_lifetime_elision_for_references() {
7436 let type_args: &[RsTypeKind] = &[];
7437 let referent = Rc::new(RsTypeKind::Other { name: "T".into(), type_args: type_args.into() });
7438 let reference = RsTypeKind::Reference {
7439 referent: referent,
7440 mutability: Mutability::Const,
Devin Jeanpierre4e94a082022-08-10 01:58:35 -07007441 lifetime: Lifetime::new("_"),
Devin Jeanpierre7ed8c6f2022-08-01 13:41:02 -07007442 };
7443 assert_rs_matches!(quote! {#reference}, quote! {&T});
7444 }
7445
7446 #[test]
7447 fn test_lifetime_elision_for_rvalue_references() {
7448 let type_args: &[RsTypeKind] = &[];
7449 let referent = Rc::new(RsTypeKind::Other { name: "T".into(), type_args: type_args.into() });
7450 let reference = RsTypeKind::RvalueReference {
7451 referent: referent,
7452 mutability: Mutability::Mut,
Devin Jeanpierre4e94a082022-08-10 01:58:35 -07007453 lifetime: Lifetime::new("_"),
Devin Jeanpierre7ed8c6f2022-08-01 13:41:02 -07007454 };
7455 assert_rs_matches!(quote! {#reference}, quote! {RvalueReference<'_, T>});
7456 }
Marcel Hlopko42abfc82021-08-09 07:03:17 +00007457}