blob: e4f7ec09aa43cf846080617b24d07bd55e509cb5 [file] [log] [blame]
Lukasz Anforowiczbda1cfe2022-09-20 06:25:43 -07001// Part of the Crubit project, under the Apache License v2.0 with LLVM
2// Exceptions. See /LICENSE for license information.
3// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
4
Lukasz Anforowicz75894832022-11-22 18:25:28 -08005use anyhow::{anyhow, bail, ensure, Context, Result};
Lukasz Anforowicz8b475a42022-11-29 09:33:02 -08006use code_gen_utils::{
Lukasz Anforowicz9924c432023-04-04 12:51:22 -07007 escape_non_identifier_chars, format_cc_ident, format_cc_includes,
8 format_namespace_bound_cc_tokens, make_rs_ident, CcInclude, NamespaceQualifier,
Lukasz Anforowicz8b475a42022-11-29 09:33:02 -08009};
Lukasz Anforowicz903fc632022-10-25 08:55:33 -070010use itertools::Itertools;
Lukasz Anforowicza691cf52023-03-08 12:24:33 -080011use proc_macro2::{Ident, Literal, TokenStream};
Lukasz Anforowicz2eb24292023-03-14 09:38:21 -070012use quote::{format_ident, quote, ToTokens};
Lukasz Anforowiczae1ae172023-07-05 12:11:24 -070013use rustc_hir::{AssocItemKind, Item, ItemKind, Node, Unsafety};
Lukasz Anforowicz36de5722023-06-16 08:17:44 -070014use rustc_infer::infer::TyCtxtInferExt;
Lukasz Anforowiczed1c4f02022-09-29 11:11:20 -070015use rustc_middle::dep_graph::DepContext;
Lukasz Anforowiczeb58a492023-01-07 08:25:48 -080016use rustc_middle::mir::Mutability;
Lukasz Anforowicz4f9f5522022-10-17 15:03:37 -070017use rustc_middle::ty::{self, Ty, TyCtxt}; // See <internal link>/ty.html#import-conventions
Lukasz Anforowicz903fc632022-10-25 08:55:33 -070018use rustc_span::def_id::{DefId, LocalDefId, LOCAL_CRATE};
Lukasz Anforowicz1b665a42023-06-20 13:04:54 -070019use rustc_span::symbol::{kw, sym, Symbol};
Lukasz Anforowicz735bfb82023-03-15 16:18:44 -070020use rustc_target::abi::{Abi, FieldsShape, Integer, Layout, Primitive, Scalar};
Lukasz Anforowicz24160d52022-10-19 06:45:45 -070021use rustc_target::spec::PanicStrategy;
Lukasz Anforowicz36de5722023-06-16 08:17:44 -070022use rustc_trait_selection::infer::InferCtxtExt;
Lukasz Anforowiczaa2de7e2023-06-15 11:32:05 -070023use rustc_type_ir::sty::RegionKind;
Lukasz Anforowicz816cbaa2022-12-07 09:31:30 -080024use std::collections::{BTreeSet, HashMap, HashSet};
Googler47fd9572023-01-20 09:57:32 -080025use std::iter::once;
Lukasz Anforowicz7f31f802022-12-16 08:24:13 -080026use std::ops::AddAssign;
Lukasz Anforowicz8e5042c2023-01-03 11:19:07 -080027use std::rc::Rc;
Lukasz Anforowiczbda1cfe2022-09-20 06:25:43 -070028
Lukasz Anforowiczbe25c762023-01-17 12:43:52 -080029pub struct Input<'tcx> {
30 /// Compilation context for the crate that the bindings should be generated
31 /// for.
32 pub tcx: TyCtxt<'tcx>,
33
Lukasz Anforowicza782bda2023-01-17 14:04:50 -080034 /// Path to a the `crubit/support` directory in a format that should be used
35 /// in the `#include` directives inside the generated C++ files.
36 /// Example: "crubit/support".
37 pub crubit_support_path: Rc<str>,
Lukasz Anforowiczbe25c762023-01-17 12:43:52 -080038
Lukasz Anforowiczcfe84552023-04-20 13:38:54 -070039 /// A map from a crate name to the include path with the corresponding C++
40 /// bindings. This is used when formatting a type exported from another
41 /// crate.
42 // TODO(b/271857814): A crate name might not be globally unique - the key needs to also cover
43 // a "hash" of the crate version and compilation flags.
44 pub crate_name_to_include_path: HashMap<Rc<str>, CcInclude>,
45
Lukasz Anforowiczbe25c762023-01-17 12:43:52 -080046 // TODO(b/262878759): Provide a set of enabled/disabled Crubit features.
47 pub _features: (),
Lukasz Anforowiczbe25c762023-01-17 12:43:52 -080048}
49
Lukasz Anforowicza3b7db02023-03-09 17:34:05 -080050impl<'tcx> Input<'tcx> {
51 // TODO(b/259724276): This function's results should be memoized. It may be
52 // easier if separate functions are provided for each support header - e.g.
53 // `rs_char()`, `return_value_slot()`, etc.
54 fn support_header(&self, suffix: &str) -> CcInclude {
55 let support_path = &*self.crubit_support_path;
56 let full_path = format!("{support_path}/{suffix}");
57 CcInclude::user_header(full_path.into())
58 }
59}
60
Lukasz Anforowiczbe25c762023-01-17 12:43:52 -080061pub struct Output {
Lukasz Anforowicz2b38d272022-09-23 08:08:18 -070062 pub h_body: TokenStream,
Lukasz Anforowicze7a25002022-11-10 06:21:42 -080063 pub rs_body: TokenStream,
Lukasz Anforowicz581fd752022-09-21 11:30:15 -070064}
65
Lukasz Anforowiczbe25c762023-01-17 12:43:52 -080066pub fn generate_bindings(input: &Input) -> Result<Output> {
67 match input.tcx.sess().panic_strategy() {
68 PanicStrategy::Unwind => bail!("No support for panic=unwind strategy (b/254049425)"),
69 PanicStrategy::Abort => (),
70 };
Lukasz Anforowicz24160d52022-10-19 06:45:45 -070071
Lukasz Anforowiczbe25c762023-01-17 12:43:52 -080072 let top_comment = {
73 let crate_name = input.tcx.crate_name(LOCAL_CRATE);
74 let txt = format!(
75 "Automatically @generated C++ bindings for the following Rust crate:\n\
76 {crate_name}"
77 );
78 quote! { __COMMENT__ #txt __NEWLINE__ }
79 };
Lukasz Anforowiczd9ff4ab2022-09-23 08:11:18 -070080
Lukasz Anforowiczbe25c762023-01-17 12:43:52 -080081 let Output { h_body, rs_body } = format_crate(input).unwrap_or_else(|err| {
82 let txt = format!("Failed to generate bindings for the crate: {err}");
83 let src = quote! { __COMMENT__ #txt };
84 Output { h_body: src.clone(), rs_body: src }
85 });
Lukasz Anforowicz2f849cf2022-11-17 15:52:39 -080086
Lukasz Anforowiczbe25c762023-01-17 12:43:52 -080087 let h_body = quote! {
88 #top_comment
Lukasz Anforowicz2f849cf2022-11-17 15:52:39 -080089
Lukasz Anforowiczbe25c762023-01-17 12:43:52 -080090 // TODO(b/251445877): Replace `#pragma once` with include guards.
91 __HASH_TOKEN__ pragma once __NEWLINE__
92 __NEWLINE__
Lukasz Anforowicz2f849cf2022-11-17 15:52:39 -080093
Lukasz Anforowiczbe25c762023-01-17 12:43:52 -080094 #h_body
95 };
Lukasz Anforowicz581fd752022-09-21 11:30:15 -070096
Lukasz Anforowiczbe25c762023-01-17 12:43:52 -080097 let rs_body = quote! {
98 #top_comment
Lukasz Anforowiczd16b6bf2022-11-22 18:35:08 -080099
Lukasz Anforowicz843d1612023-03-02 16:52:53 -0800100 // `rust_builtin_type_abi_assumptions.md` documents why the generated
101 // bindings need to relax the `improper_ctypes_definitions` warning
102 // for `char` (and possibly for other built-in types in the future).
Lukasz Anforowiczbe25c762023-01-17 12:43:52 -0800103 #![allow(improper_ctypes_definitions)] __NEWLINE__
104 __NEWLINE__
Lukasz Anforowiczd16b6bf2022-11-22 18:35:08 -0800105
Lukasz Anforowiczbe25c762023-01-17 12:43:52 -0800106 #rs_body
107 };
Lukasz Anforowicze7a25002022-11-10 06:21:42 -0800108
Lukasz Anforowiczbe25c762023-01-17 12:43:52 -0800109 Ok(Output { h_body, rs_body })
Lukasz Anforowicz581fd752022-09-21 11:30:15 -0700110}
111
Lukasz Anforowicz0b8a02f2023-02-10 15:55:40 -0800112#[derive(Clone, Debug, Default)]
Lukasz Anforowicz3744e502022-12-02 08:40:38 -0800113struct CcPrerequisites {
114 /// Set of `#include`s that a `CcSnippet` depends on. For example if
115 /// `CcSnippet::tokens` expands to `std::int32_t`, then `includes`
116 /// need to cover the `#include <cstdint>`.
Lukasz Anforowiczed17d052022-11-02 12:07:28 -0700117 includes: BTreeSet<CcInclude>,
Lukasz Anforowicz5c9c00d2022-12-02 08:41:39 -0800118
119 /// Set of local definitions that a `CcSnippet` depends on. For example if
Lukasz Anforowicz73a2c0d2023-03-22 14:07:00 -0700120 /// `CcSnippet::tokens` expands to `void foo(S s) { ... }` then the
121 /// definition of `S` should have appeared earlier - in this case `defs`
122 /// will include the `LocalDefId` corresponding to `S`. Note that the
123 /// definition of `S` is covered by `ApiSnippets::main_api` (i.e. the
124 /// predecessor of a toposort edge is `ApiSnippets::main_api` - it is not
125 /// possible to depend on `ApiSnippets::cc_details`).
Lukasz Anforowicz5c9c00d2022-12-02 08:41:39 -0800126 defs: HashSet<LocalDefId>,
127
128 /// Set of forward declarations that a `CcSnippet` depends on. For example
129 /// if `CcSnippet::tokens` expands to `void foo(S* s)` then a forward
130 /// declaration of `S` should have appeared earlier - in this case
131 /// `fwd_decls` will include the `LocalDefId` corresponding to `S`.
132 /// Note that in this particular example the *definition* of `S` does
133 /// *not* need to appear earlier (and therefore `defs` will *not*
134 /// contain `LocalDefId` corresponding to `S`).
Lukasz Anforowiczf4501b42023-01-07 09:41:29 -0800135 fwd_decls: HashSet<LocalDefId>,
Lukasz Anforowicz3744e502022-12-02 08:40:38 -0800136}
137
138impl CcPrerequisites {
139 #[cfg(test)]
140 fn is_empty(&self) -> bool {
Lukasz Anforowiczf4501b42023-01-07 09:41:29 -0800141 let &Self { ref includes, ref defs, ref fwd_decls } = self;
142 includes.is_empty() && defs.is_empty() && fwd_decls.is_empty()
Lukasz Anforowicz3744e502022-12-02 08:40:38 -0800143 }
Lukasz Anforowicz7a7b81f2023-01-12 15:50:46 -0800144
145 /// Weakens all dependencies to only require a forward declaration. Example
146 /// usage scenarios:
147 /// - Computing prerequisites of pointer types (the pointee type can just be
148 /// forward-declared),
149 /// - Computing prerequisites of function declarations (parameter types and
150 /// return type can just be forward-declared).
151 fn move_defs_to_fwd_decls(&mut self) {
152 self.fwd_decls.extend(std::mem::take(&mut self.defs))
153 }
Lukasz Anforowicz3744e502022-12-02 08:40:38 -0800154}
155
156impl AddAssign for CcPrerequisites {
Lukasz Anforowiczf4501b42023-01-07 09:41:29 -0800157 fn add_assign(&mut self, rhs: Self) {
158 let Self { mut includes, defs, fwd_decls } = rhs;
159
Lukasz Anforowicz5c9c00d2022-12-02 08:41:39 -0800160 // `BTreeSet::append` is used because it _seems_ to be more efficient than
161 // calling `extend`. This is because `extend` takes an iterator
162 // (processing each `rhs` include one-at-a-time) while `append` steals
163 // the whole backing data store from `rhs.includes`. OTOH, this is a bit
164 // speculative, since the (expected / guessed) performance difference is
Lukasz Anforowiczdf363ee2022-12-16 14:56:38 -0800165 // not documented at
166 // https://doc.rust-lang.org/std/collections/struct.BTreeSet.html#method.append
Lukasz Anforowiczf4501b42023-01-07 09:41:29 -0800167 self.includes.append(&mut includes);
Lukasz Anforowicz5c9c00d2022-12-02 08:41:39 -0800168
Lukasz Anforowiczf4501b42023-01-07 09:41:29 -0800169 self.defs.extend(defs);
170 self.fwd_decls.extend(fwd_decls);
Lukasz Anforowicz3744e502022-12-02 08:40:38 -0800171 }
172}
173
174#[derive(Debug, Default)]
175struct CcSnippet {
176 tokens: TokenStream,
177 prereqs: CcPrerequisites,
Lukasz Anforowiczed17d052022-11-02 12:07:28 -0700178}
179
180impl CcSnippet {
Lukasz Anforowicz3744e502022-12-02 08:40:38 -0800181 /// Consumes `self` and returns its `tokens`, while preserving
Lukasz Anforowiczdf363ee2022-12-16 14:56:38 -0800182 /// its `prereqs` into `prereqs_accumulator`.
Lukasz Anforowicz3744e502022-12-02 08:40:38 -0800183 fn into_tokens(self, prereqs_accumulator: &mut CcPrerequisites) -> TokenStream {
184 let Self { tokens, prereqs } = self;
185 *prereqs_accumulator += prereqs;
186 tokens
Lukasz Anforowiczed17d052022-11-02 12:07:28 -0700187 }
Lukasz Anforowicza2f1cae2022-12-15 10:42:25 -0800188
189 /// Creates a new CcSnippet (with no `CcPrerequisites`).
190 fn new(tokens: TokenStream) -> Self {
191 Self { tokens, ..Default::default() }
192 }
193
194 /// Creates a CcSnippet that depends on a single `CcInclude`.
195 fn with_include(tokens: TokenStream, include: CcInclude) -> Self {
196 let mut prereqs = CcPrerequisites::default();
197 prereqs.includes.insert(include);
198 Self { tokens, prereqs }
199 }
Lukasz Anforowiczed17d052022-11-02 12:07:28 -0700200}
201
Lukasz Anforowicz73a2c0d2023-03-22 14:07:00 -0700202impl AddAssign for CcSnippet {
203 fn add_assign(&mut self, rhs: Self) {
204 self.tokens.extend(rhs.into_tokens(&mut self.prereqs));
205 }
206}
207
Lukasz Anforowiczdf363ee2022-12-16 14:56:38 -0800208/// Represents the fully qualified name of a Rust item (e.g. of a `struct` or a
Lukasz Anforowicz8b475a42022-11-29 09:33:02 -0800209/// function).
210struct FullyQualifiedName {
211 /// Name of the crate that defines the item.
212 /// For example, this would be `std` for `std::cmp::Ordering`.
213 krate: Symbol,
214
215 /// Path to the module where the item is located.
216 /// For example, this would be `cmp` for `std::cmp::Ordering`.
Lukasz Anforowiczdf363ee2022-12-16 14:56:38 -0800217 /// The path may contain multiple modules - e.g. `foo::bar::baz`.
Lukasz Anforowicz8b475a42022-11-29 09:33:02 -0800218 mod_path: NamespaceQualifier,
219
220 /// Name of the item.
Lukasz Anforowiczce17f3f2023-02-27 11:32:14 -0800221 /// For example, this would be:
222 /// * `Some("Ordering")` for `std::cmp::Ordering`.
223 /// * `None` for `ItemKind::Use` - e.g.: `use submodule::*`
224 name: Option<Symbol>,
Lukasz Anforowicz8b475a42022-11-29 09:33:02 -0800225}
226
227impl FullyQualifiedName {
Lukasz Anforowicz43f06c62023-02-10 17:14:19 -0800228 /// Computes a `FullyQualifiedName` for `def_id`.
229 ///
Lukasz Anforowiczce17f3f2023-02-27 11:32:14 -0800230 /// May panic if `def_id` is an invalid id.
Lukasz Anforowicz1ab5e872022-12-05 10:07:00 -0800231 // TODO(b/259724276): This function's results should be memoized.
Lukasz Anforowicz8b475a42022-11-29 09:33:02 -0800232 fn new(tcx: TyCtxt, def_id: DefId) -> Self {
Lukasz Anforowicz8b475a42022-11-29 09:33:02 -0800233 let krate = tcx.crate_name(def_id.krate);
234
235 let mut full_path = tcx.def_path(def_id).data; // mod_path + name
236 let name = full_path.pop().expect("At least the item's name should be present");
Lukasz Anforowiczce17f3f2023-02-27 11:32:14 -0800237 let name = name.data.get_opt_name();
Lukasz Anforowicz8b475a42022-11-29 09:33:02 -0800238
Lukasz Anforowicz8e5042c2023-01-03 11:19:07 -0800239 let mod_path = NamespaceQualifier::new(
Lukasz Anforowiczce17f3f2023-02-27 11:32:14 -0800240 full_path
241 .into_iter()
242 .filter_map(|p| p.data.get_opt_name())
243 .map(|s| Rc::<str>::from(s.as_str())),
Lukasz Anforowicz8e5042c2023-01-03 11:19:07 -0800244 );
Lukasz Anforowicz8b475a42022-11-29 09:33:02 -0800245
246 Self { krate, mod_path, name }
247 }
248
249 fn format_for_cc(&self) -> Result<TokenStream> {
Lukasz Anforowiczce17f3f2023-02-27 11:32:14 -0800250 let name =
251 self.name.as_ref().expect("`format_for_cc` can't be called on name-less item kinds");
252
Lukasz Anforowicz8b475a42022-11-29 09:33:02 -0800253 let top_level_ns = format_cc_ident(self.krate.as_str())?;
254 let ns_path = self.mod_path.format_for_cc()?;
Lukasz Anforowiczce17f3f2023-02-27 11:32:14 -0800255 let name = format_cc_ident(name.as_str())?;
Lukasz Anforowicz8b475a42022-11-29 09:33:02 -0800256 Ok(quote! { :: #top_level_ns :: #ns_path #name })
257 }
258
259 fn format_for_rs(&self) -> TokenStream {
Lukasz Anforowiczce17f3f2023-02-27 11:32:14 -0800260 let name =
261 self.name.as_ref().expect("`format_for_cc` can't be called on name-less item kinds");
262
Lukasz Anforowiczdf363ee2022-12-16 14:56:38 -0800263 let krate = make_rs_ident(self.krate.as_str());
264 let mod_path = self.mod_path.format_for_rs();
Lukasz Anforowiczce17f3f2023-02-27 11:32:14 -0800265 let name = make_rs_ident(name.as_str());
Lukasz Anforowiczdf363ee2022-12-16 14:56:38 -0800266 quote! { :: #krate :: #mod_path #name }
Lukasz Anforowicz8b475a42022-11-29 09:33:02 -0800267 }
268}
269
Lukasz Anforowicza3b7db02023-03-09 17:34:05 -0800270/// Whether functions using `extern "C"` ABI can safely handle values of type
271/// `ty` (e.g. when passing by value arguments or return values of such type).
272fn is_c_abi_compatible_by_value(ty: Ty) -> bool {
273 match ty.kind() {
274 // `improper_ctypes_definitions` warning doesn't complain about the following types:
275 ty::TyKind::Bool |
276 ty::TyKind::Float{..} |
277 ty::TyKind::Int{..} |
278 ty::TyKind::Uint{..} |
279 ty::TyKind::Never |
280 ty::TyKind::RawPtr{..} |
Lukasz Anforowiczaa2de7e2023-06-15 11:32:05 -0700281 ty::TyKind::Ref{..} |
Lukasz Anforowicza3b7db02023-03-09 17:34:05 -0800282 ty::TyKind::FnPtr{..} => true,
283 ty::TyKind::Tuple(types) if types.len() == 0 => true,
284
285 // Crubit assumes that `char` is compatible with a certain `extern "C"` ABI.
286 // See `rust_builtin_type_abi_assumptions.md` for more details.
287 ty::TyKind::Char => true,
288
289 // Crubit's C++ bindings for tuples, structs, and other ADTs may not preserve
290 // their ABI (even if they *do* preserve their memory layout). For example:
291 // - In System V ABI replacing a field with a fixed-length array of bytes may affect
292 // whether the whole struct is classified as an integer and passed in general purpose
293 // registers VS classified as SSE2 and passed in floating-point registers like xmm0).
294 // See also b/270454629.
295 // - To replicate field offsets, Crubit may insert explicit padding fields. These
296 // extra fields may also impact the ABI of the generated bindings.
297 //
298 // TODO(lukasza): In the future, some additional performance gains may be realized by
299 // returning `true` in a few limited cases (this may require additional complexity to
300 // ensure that `format_adt` never injects explicit padding into such structs):
301 // - `#[repr(C)]` structs and unions,
302 // - `#[repr(transparent)]` struct that wraps an ABI-safe type,
303 // - Discriminant-only enums (b/259984090).
304 ty::TyKind::Tuple{..} | // An empty tuple (`()` - the unit type) is handled above.
305 ty::TyKind::Adt{..} => false,
306
307 // These kinds of reference-related types are not implemented yet - `is_c_abi_compatible_by_value`
308 // should never need to handle them, because `format_ty_for_cc` fails for such types.
309 //
310 // TODO(b/258235219): When implementing support for references we should
311 // consider returning `true` for `TyKind::Ref` and document the rationale
312 // for such decision - maybe something like this will be sufficient:
313 // - In general `TyKind::Ref` should have the same ABI as `TyKind::RawPtr`
314 // - References to slices (`&[T]`) or strings (`&str`) rely on assumptions
315 // spelled out in `rust_builtin_type_abi_assumptions.md`..
Lukasz Anforowicza3b7db02023-03-09 17:34:05 -0800316 ty::TyKind::Str |
317 ty::TyKind::Array{..} |
318 ty::TyKind::Slice{..} =>
319 unimplemented!(),
320
321 // `format_ty_for_cc` is expected to fail for other kinds of types
322 // and therefore `is_c_abi_compatible_by_value` should never be called for
323 // these other types
324 _ => unimplemented!(),
325 }
326}
327
Lukasz Anforowicz8574c9d2023-04-13 15:11:20 -0700328/// Location where a type is used.
329enum TypeLocation {
330 /// The top-level return type.
331 ///
332 /// The "top-level" part can be explained by looking at an example of `fn
333 /// foo() -> *const T`:
334 /// - The top-level return type `*const T` is in the `FnReturn` location
335 /// - The nested pointee type `T` is in the `Other` location
336 FnReturn,
337
Lukasz Anforowicz5c1b3ad2023-04-13 17:05:00 -0700338 /// The top-level parameter type.
339 ///
340 /// The "top-level" part can be explained by looking at an example of:
341 /// `fn foo(param: *const T)`:
342 /// - The top-level parameter type `*const T` is in the `FnParam` location
343 /// - The nested pointee type `T` is in the `Other` location
344 // TODO(b/278141494, b/278141418): Once `const` and `static` items are supported,
345 // we may want to apply parameter-like formatting to their types (e.g. have
346 // `format_ty_for_cc` emit `T&` rather than `T*`).
347 FnParam,
348
349 /// Other location (e.g. pointee type, field type, etc.).
Lukasz Anforowicz8574c9d2023-04-13 15:11:20 -0700350 Other,
351}
352
Lukasz Anforowiczaa2de7e2023-06-15 11:32:05 -0700353fn format_pointer_or_reference_ty_for_cc<'tcx>(
354 input: &Input<'tcx>,
355 pointee: Ty<'tcx>,
356 mutability: rustc_middle::mir::Mutability,
357 pointer_sigil: TokenStream,
358) -> Result<CcSnippet> {
359 let const_qualifier = match mutability {
360 Mutability::Mut => quote! {},
361 Mutability::Not => quote! { const },
362 };
363 let CcSnippet { tokens, mut prereqs } = format_ty_for_cc(input, pointee, TypeLocation::Other)?;
364 prereqs.move_defs_to_fwd_decls();
365 Ok(CcSnippet { prereqs, tokens: quote! { #tokens #const_qualifier #pointer_sigil } })
366}
367
Lukasz Anforowiczed17d052022-11-02 12:07:28 -0700368/// Formats `ty` into a `CcSnippet` that represents how the type should be
Lukasz Anforowiczdf363ee2022-12-16 14:56:38 -0800369/// spelled in a C++ declaration of a function parameter or field.
Lukasz Anforowicz1ab5e872022-12-05 10:07:00 -0800370//
371// TODO(b/259724276): This function's results should be memoized.
Lukasz Anforowicz8574c9d2023-04-13 15:11:20 -0700372fn format_ty_for_cc<'tcx>(
373 input: &Input<'tcx>,
374 ty: Ty<'tcx>,
375 location: TypeLocation,
376) -> Result<CcSnippet> {
Lukasz Anforowicz3744e502022-12-02 08:40:38 -0800377 fn cstdint(tokens: TokenStream) -> CcSnippet {
Lukasz Anforowicza2f1cae2022-12-15 10:42:25 -0800378 CcSnippet::with_include(tokens, CcInclude::cstdint())
Lukasz Anforowiczed17d052022-11-02 12:07:28 -0700379 }
Lukasz Anforowicz3744e502022-12-02 08:40:38 -0800380 fn keyword(tokens: TokenStream) -> CcSnippet {
Lukasz Anforowicza2f1cae2022-12-15 10:42:25 -0800381 CcSnippet::new(tokens)
Lukasz Anforowiczed17d052022-11-02 12:07:28 -0700382 }
Lukasz Anforowicz4f9f5522022-10-17 15:03:37 -0700383 Ok(match ty.kind() {
Lukasz Anforowicz8574c9d2023-04-13 15:11:20 -0700384 ty::TyKind::Never => match location {
385 TypeLocation::FnReturn => keyword(quote! { void }),
Googler7a77a802023-04-21 08:32:50 -0700386 _ => {
Lukasz Anforowicz8574c9d2023-04-13 15:11:20 -0700387 // TODO(b/254507801): Maybe translate into `crubit::Never`?
388 bail!("The never type `!` is only supported as a return type (b/254507801)");
Googler7a77a802023-04-21 08:32:50 -0700389 }
390 },
Lukasz Anforowicz4f9f5522022-10-17 15:03:37 -0700391 ty::TyKind::Tuple(types) => {
392 if types.len() == 0 {
Lukasz Anforowicz8574c9d2023-04-13 15:11:20 -0700393 match location {
394 TypeLocation::FnReturn => keyword(quote! { void }),
Googler7a77a802023-04-21 08:32:50 -0700395 _ => {
Lukasz Anforowicz8574c9d2023-04-13 15:11:20 -0700396 // TODO(b/254507801): Maybe translate into `crubit::Unit`?
397 bail!("`()` / `void` is only supported as a return type (b/254507801)");
Googler7a77a802023-04-21 08:32:50 -0700398 }
Lukasz Anforowicz8574c9d2023-04-13 15:11:20 -0700399 }
Lukasz Anforowicz4f9f5522022-10-17 15:03:37 -0700400 } else {
Lukasz Anforowicz13794df2022-10-21 07:56:34 -0700401 // TODO(b/254099023): Add support for tuples.
402 bail!("Tuples are not supported yet: {} (b/254099023)", ty);
Lukasz Anforowicz4f9f5522022-10-17 15:03:37 -0700403 }
404 }
Lukasz Anforowicz4f9f5522022-10-17 15:03:37 -0700405
Lukasz Anforowicze3c39472023-01-12 15:07:38 -0800406 // https://rust-lang.github.io/unsafe-code-guidelines/layout/scalars.html#bool documents
407 // that "Rust's bool has the same layout as C17's _Bool". The details (e.g. size, valid
408 // bit patterns) are implementation-defined, but this is okay, because `bool` in the
409 // `extern "C"` functions in the generated `..._cc_api.h` will also be the C17's _Bool.
Lukasz Anforowiczed17d052022-11-02 12:07:28 -0700410 ty::TyKind::Bool => keyword(quote! { bool }),
411
412 // https://rust-lang.github.io/unsafe-code-guidelines/layout/scalars.html#fixed-width-floating-point-types
413 // documents that "When the platforms' "math.h" header defines the __STDC_IEC_559__ macro,
414 // Rust's floating-point types are safe to use directly in C FFI where the appropriate C
415 // types are expected (f32 for float, f64 for double)."
416 //
417 // TODO(b/255768062): Generated bindings should explicitly check `__STDC_IEC_559__`
418 ty::TyKind::Float(ty::FloatTy::F32) => keyword(quote! { float }),
419 ty::TyKind::Float(ty::FloatTy::F64) => keyword(quote! { double }),
420
Lukasz Anforowicza782bda2023-01-17 14:04:50 -0800421 // ABI compatibility and other details are described in the doc comments in
Lukasz Anforowiczec0b64e2023-02-17 14:31:12 -0800422 // `crubit/support/rs_std/rs_char.h` and `crubit/support/rs_std/char_test.cc` (search for
Lukasz Anforowiczd71686c2023-02-17 14:29:55 -0800423 // "Layout tests").
Lukasz Anforowiczed17d052022-11-02 12:07:28 -0700424 ty::TyKind::Char => {
Lukasz Anforowiczb06e0812023-03-02 15:54:32 -0800425 // Asserting that the target architecture meets the assumption from Crubit's
Devin Jeanpierre81aec502023-04-04 15:33:39 -0700426 // `rust_builtin_type_abi_assumptions.md` - we assume that Rust's `char` has the
427 // same ABI as `u32`.
Lukasz Anforowiczb06e0812023-03-02 15:54:32 -0800428 let layout = input
429 .tcx
Lukasz Anforowicz2aab0192023-06-15 12:05:39 -0700430 .layout_of(ty::ParamEnv::empty().and(ty))
Lukasz Anforowiczb06e0812023-03-02 15:54:32 -0800431 .expect("`layout_of` is expected to succeed for the builtin `char` type")
432 .layout;
433 assert_eq!(4, layout.align().abi.bytes());
434 assert_eq!(4, layout.size().bytes());
Devin Jeanpierre81aec502023-04-04 15:33:39 -0700435 assert!(matches!(
436 layout.abi(),
437 Abi::Scalar(Scalar::Initialized {
438 value: Primitive::Int(Integer::I32, /* signedness = */ false),
439 ..
440 })
441 ));
Lukasz Anforowiczb06e0812023-03-02 15:54:32 -0800442
Lukasz Anforowicza782bda2023-01-17 14:04:50 -0800443 CcSnippet::with_include(
Lukasz Anforowiczec0b64e2023-02-17 14:31:12 -0800444 quote! { rs_std::rs_char },
Lukasz Anforowicza3b7db02023-03-09 17:34:05 -0800445 input.support_header("rs_std/rs_char.h"),
Lukasz Anforowicza782bda2023-01-17 14:04:50 -0800446 )
Devin Jeanpierre81aec502023-04-04 15:33:39 -0700447 }
Lukasz Anforowiczed17d052022-11-02 12:07:28 -0700448
449 // https://rust-lang.github.io/unsafe-code-guidelines/layout/scalars.html#isize-and-usize
450 // documents that "Rust's signed and unsigned fixed-width integer types {i,u}{8,16,32,64}
451 // have the same layout the C fixed-width integer types from the <stdint.h> header
452 // {u,}int{8,16,32,64}_t. These fixed-width integer types are therefore safe to use
453 // directly in C FFI where the corresponding C fixed-width integer types are expected.
454 //
455 // https://rust-lang.github.io/unsafe-code-guidelines/layout/scalars.html#layout-compatibility-with-c-native-integer-types
456 // documents that "Rust does not support C platforms on which the C native integer type are
457 // not compatible with any of Rust's fixed-width integer type (e.g. because of
458 // padding-bits, lack of 2's complement, etc.)."
Devin Jeanpierre81aec502023-04-04 15:33:39 -0700459 ty::TyKind::Int(ty::IntTy::I8) => cstdint(quote! { std::int8_t }),
460 ty::TyKind::Int(ty::IntTy::I16) => cstdint(quote! { std::int16_t }),
461 ty::TyKind::Int(ty::IntTy::I32) => cstdint(quote! { std::int32_t }),
462 ty::TyKind::Int(ty::IntTy::I64) => cstdint(quote! { std::int64_t }),
463 ty::TyKind::Uint(ty::UintTy::U8) => cstdint(quote! { std::uint8_t }),
464 ty::TyKind::Uint(ty::UintTy::U16) => cstdint(quote! { std::uint16_t }),
465 ty::TyKind::Uint(ty::UintTy::U32) => cstdint(quote! { std::uint32_t }),
466 ty::TyKind::Uint(ty::UintTy::U64) => cstdint(quote! { std::uint64_t }),
Lukasz Anforowiczed17d052022-11-02 12:07:28 -0700467
468 // https://rust-lang.github.io/unsafe-code-guidelines/layout/scalars.html#isize-and-usize
469 // documents that "The isize and usize types are [...] layout compatible with C's uintptr_t
470 // and intptr_t types.".
Devin Jeanpierre81aec502023-04-04 15:33:39 -0700471 ty::TyKind::Int(ty::IntTy::Isize) => cstdint(quote! { std::intptr_t }),
472 ty::TyKind::Uint(ty::UintTy::Usize) => cstdint(quote! { std::uintptr_t }),
Lukasz Anforowicz4f9f5522022-10-17 15:03:37 -0700473
474 ty::TyKind::Int(ty::IntTy::I128) | ty::TyKind::Uint(ty::UintTy::U128) => {
Lukasz Anforowiczed17d052022-11-02 12:07:28 -0700475 // Note that "the alignment of Rust's {i,u}128 is unspecified and allowed to
476 // change" according to
477 // https://rust-lang.github.io/unsafe-code-guidelines/layout/scalars.html#fixed-width-integer-types
478 //
Lukasz Anforowicz4f9f5522022-10-17 15:03:37 -0700479 // TODO(b/254094650): Consider mapping this to Clang's (and GCC's) `__int128`
480 // or to `absl::in128`.
481 bail!("C++ doesn't have a standard equivalent of `{ty}` (b/254094650)");
482 }
483
Lukasz Anforowicz75894832022-11-22 18:25:28 -0800484 ty::TyKind::Adt(adt, substs) => {
Lukasz Anforowiczdf363ee2022-12-16 14:56:38 -0800485 ensure!(substs.len() == 0, "Generic types are not supported yet (b/259749095)");
Lukasz Anforowiczf36762a2023-03-02 18:43:07 -0800486 ensure!(
487 is_directly_public(input.tcx, adt.did()),
Devin Jeanpierre81aec502023-04-04 15:33:39 -0700488 "Not directly public type (re-exports are not supported yet - b/262052635)"
489 );
Lukasz Anforowicz75894832022-11-22 18:25:28 -0800490
Lukasz Anforowicz75894832022-11-22 18:25:28 -0800491 let def_id = adt.did();
Lukasz Anforowicz5c9c00d2022-12-02 08:41:39 -0800492 let mut prereqs = CcPrerequisites::default();
493 if def_id.krate == LOCAL_CRATE {
494 prereqs.defs.insert(def_id.expect_local());
Lukasz Anforowicz75894832022-11-22 18:25:28 -0800495 } else {
Lukasz Anforowiczcfe84552023-04-20 13:38:54 -0700496 let other_crate_name = input.tcx.crate_name(def_id.krate);
497 let include = input
498 .crate_name_to_include_path
499 .get(other_crate_name.as_str())
Googler7a77a802023-04-21 08:32:50 -0700500 .ok_or_else(|| {
501 anyhow!(
Lukasz Anforowiczcfe84552023-04-20 13:38:54 -0700502 "Type `{ty}` comes from the `{other_crate_name}` crate, \
Googler7a77a802023-04-21 08:32:50 -0700503 but no `--other-crate-bindings` were specified for this crate"
504 )
505 })?;
Lukasz Anforowiczcfe84552023-04-20 13:38:54 -0700506 prereqs.includes.insert(include.clone());
507 }
Lukasz Anforowicz75894832022-11-22 18:25:28 -0800508
Lukasz Anforowiczcc7a76b2023-02-28 14:19:42 -0800509 // Verify if definition of `ty` can be succesfully imported and bail otherwise.
Devin Jeanpierre81aec502023-04-04 15:33:39 -0700510 format_adt_core(input.tcx, def_id).with_context(|| {
511 format!("Failed to generate bindings for the definition of `{ty}`")
512 })?;
Lukasz Anforowiczcc7a76b2023-02-28 14:19:42 -0800513
Lukasz Anforowicz75894832022-11-22 18:25:28 -0800514 CcSnippet {
Lukasz Anforowiczbe25c762023-01-17 12:43:52 -0800515 tokens: FullyQualifiedName::new(input.tcx, def_id).format_for_cc()?,
Lukasz Anforowiczeb58a492023-01-07 08:25:48 -0800516 prereqs,
Lukasz Anforowiczeb58a492023-01-07 08:25:48 -0800517 }
Devin Jeanpierre81aec502023-04-04 15:33:39 -0700518 }
519
Lukasz Anforowiczaa2de7e2023-06-15 11:32:05 -0700520 ty::TyKind::RawPtr(ty::TypeAndMut { ty: pointee_ty, mutbl }) => {
521 format_pointer_or_reference_ty_for_cc(input, *pointee_ty, *mutbl, quote! { * })
522 .with_context(|| {
Devin Jeanpierre81aec502023-04-04 15:33:39 -0700523 format!("Failed to format the pointee of the pointer type `{ty}`")
Lukasz Anforowiczaa2de7e2023-06-15 11:32:05 -0700524 })?
525 }
526
527 ty::TyKind::Ref(region, referent_ty, mutability) => {
528 match location {
529 TypeLocation::FnReturn | TypeLocation::FnParam => (),
530 TypeLocation::Other => bail!(
531 "Can't format `{ty}`, because references are only supported in \
532 function parameter types and return types (b/286256327)",
533 ),
534 };
535 let lifetime = format_region_as_cc_lifetime(region);
536 format_pointer_or_reference_ty_for_cc(
537 input,
538 *referent_ty,
539 *mutability,
540 quote! { & #lifetime },
541 )
542 .with_context(|| {
543 format!("Failed to format the referent of the reference type `{ty}`")
544 })?
Devin Jeanpierre81aec502023-04-04 15:33:39 -0700545 }
Lukasz Anforowiczeb58a492023-01-07 08:25:48 -0800546
Lukasz Anforowicz5c1b3ad2023-04-13 17:05:00 -0700547 ty::TyKind::FnPtr(sig) => {
548 let sig = match sig.no_bound_vars() {
549 None => bail!("Generic functions are not supported yet (b/259749023)"),
550 Some(sig) => sig,
551 };
552 check_fn_sig(&sig)?;
553 is_thunk_required(&sig).context("Function pointers can't have a thunk")?;
554
555 // `is_thunk_required` check above implies `extern "C"` (or `"C-unwind"`).
556 // This assertion reinforces that the generated C++ code doesn't need
557 // to use calling convention attributes like `_stdcall`, etc.
558 assert!(matches!(sig.abi, rustc_target::spec::abi::Abi::C { .. }));
559
Googler7a77a802023-04-21 08:32:50 -0700560 // C++ references are not rebindable and therefore can't be used to replicate
561 // semantics of Rust field types (or, say, element types of Rust
562 // arrays). Because of this, C++ references are only used for
563 // top-level return types and parameter types (and pointers are used
564 // in other locations).
Lukasz Anforowicz5c1b3ad2023-04-13 17:05:00 -0700565 let ptr_or_ref_sigil = match location {
Googler7a77a802023-04-21 08:32:50 -0700566 TypeLocation::FnReturn | TypeLocation::FnParam => quote! { & },
567 TypeLocation::Other => quote! { * },
Lukasz Anforowicz5c1b3ad2023-04-13 17:05:00 -0700568 };
569
570 let mut prereqs = CcPrerequisites::default();
571 prereqs.includes.insert(input.support_header("internal/cxx20_backports.h"));
572 let ret_type = format_ret_ty_for_cc(input, &sig)?.into_tokens(&mut prereqs);
573 let param_types = format_param_types_for_cc(input, &sig)?
574 .into_iter()
575 .map(|snippet| snippet.into_tokens(&mut prereqs));
576 let tokens = quote! {
577 crubit::type_identity_t<
578 #ret_type( #( #param_types ),* )
579 > #ptr_or_ref_sigil
580 };
581
582 CcSnippet { tokens, prereqs }
Googler7a77a802023-04-21 08:32:50 -0700583 }
Lukasz Anforowicz5c1b3ad2023-04-13 17:05:00 -0700584
Lukasz Anforowiczdf363ee2022-12-16 14:56:38 -0800585 // TODO(b/260268230, b/260729464): When recursively processing nested types (e.g. an
Lukasz Anforowicz7a7b81f2023-01-12 15:50:46 -0800586 // element type of an Array, a referent of a Ref, a parameter type of an FnPtr, etc), one
587 // should also 1) propagate `CcPrerequisites::defs`, 2) cover `CcPrerequisites::defs` in
588 // `test_format_ty_for_cc...`. For ptr/ref it might be possible to use
589 // `CcPrerequisites::move_defs_to_fwd_decls`.
Lukasz Anforowicz087dff72023-02-17 12:13:32 -0800590 _ => bail!("The following Rust type is not supported yet: {ty}"),
Lukasz Anforowicz4f9f5522022-10-17 15:03:37 -0700591 })
Lukasz Anforowiczed1c4f02022-09-29 11:11:20 -0700592}
593
Lukasz Anforowicz8574c9d2023-04-13 15:11:20 -0700594fn format_ret_ty_for_cc<'tcx>(input: &Input<'tcx>, sig: &ty::FnSig<'tcx>) -> Result<CcSnippet> {
595 format_ty_for_cc(input, sig.output(), TypeLocation::FnReturn)
596 .context("Error formatting function return type")
597}
598
Lukasz Anforowiczc6ff4532023-04-13 15:16:30 -0700599fn format_param_types_for_cc<'tcx>(
600 input: &Input<'tcx>,
601 sig: &ty::FnSig<'tcx>,
602) -> Result<Vec<CcSnippet>> {
603 sig.inputs()
604 .iter()
605 .enumerate()
606 .map(|(i, &ty)| {
Lukasz Anforowicze7212082023-07-05 13:50:10 -0700607 format_ty_for_cc(input, ty, TypeLocation::FnParam)
608 .with_context(|| format!("Error handling parameter #{i}"))
Lukasz Anforowiczc6ff4532023-04-13 15:16:30 -0700609 })
610 .collect()
611}
612
Lukasz Anforowicz75894832022-11-22 18:25:28 -0800613/// Formats `ty` for Rust - to be used in `..._cc_api_impl.rs` (e.g. as a type
614/// of a parameter in a Rust thunk). Because `..._cc_api_impl.rs` is a
615/// distinct, separate crate, the returned `TokenStream` uses crate-qualified
616/// names whenever necessary - for example: `target_crate::SomeStruct` rather
617/// than just `SomeStruct`.
Lukasz Anforowicz1ab5e872022-12-05 10:07:00 -0800618//
619// TODO(b/259724276): This function's results should be memoized.
Lukasz Anforowicz75894832022-11-22 18:25:28 -0800620fn format_ty_for_rs(tcx: TyCtxt, ty: Ty) -> Result<TokenStream> {
Lukasz Anforowicz7860d0e2022-11-15 08:47:56 -0800621 Ok(match ty.kind() {
622 ty::TyKind::Bool
623 | ty::TyKind::Float(_)
624 | ty::TyKind::Char
625 | ty::TyKind::Int(_)
626 | ty::TyKind::Uint(_)
Lukasz Anforowicz5c1b3ad2023-04-13 17:05:00 -0700627 | ty::TyKind::FnPtr(_)
Lukasz Anforowicz7860d0e2022-11-15 08:47:56 -0800628 | ty::TyKind::Never => ty
629 .to_string()
630 .parse()
631 .expect("rustc_middle::ty::Ty::to_string() should produce no parsing errors"),
632 ty::TyKind::Tuple(types) => {
633 if types.len() == 0 {
634 quote! { () }
635 } else {
636 // TODO(b/254099023): Add support for tuples.
637 bail!("Tuples are not supported yet: {} (b/254099023)", ty);
638 }
639 }
Lukasz Anforowicz8b475a42022-11-29 09:33:02 -0800640 ty::TyKind::Adt(adt, substs) => {
Lukasz Anforowiczdf363ee2022-12-16 14:56:38 -0800641 ensure!(substs.len() == 0, "Generic types are not supported yet (b/259749095)");
Lukasz Anforowicz8b475a42022-11-29 09:33:02 -0800642 FullyQualifiedName::new(tcx, adt.did()).format_for_rs()
Devin Jeanpierre81aec502023-04-04 15:33:39 -0700643 }
Lukasz Anforowiczaa2de7e2023-06-15 11:32:05 -0700644 ty::TyKind::RawPtr(ty::TypeAndMut { ty: pointee_ty, mutbl }) => {
Lukasz Anforowiczeb58a492023-01-07 08:25:48 -0800645 let qualifier = match mutbl {
Devin Jeanpierre81aec502023-04-04 15:33:39 -0700646 Mutability::Mut => quote! { mut },
647 Mutability::Not => quote! { const },
Lukasz Anforowiczeb58a492023-01-07 08:25:48 -0800648 };
Lukasz Anforowiczaa2de7e2023-06-15 11:32:05 -0700649 let ty = format_ty_for_rs(tcx, *pointee_ty).with_context(|| {
Devin Jeanpierre81aec502023-04-04 15:33:39 -0700650 format!("Failed to format the pointee of the pointer type `{ty}`")
651 })?;
652 quote! { * #qualifier #ty }
653 }
Lukasz Anforowiczaa2de7e2023-06-15 11:32:05 -0700654 ty::TyKind::Ref(region, referent_ty, mutability) => {
655 let mutability = match mutability {
656 Mutability::Mut => quote! { mut },
657 Mutability::Not => quote! {},
658 };
659 let ty = format_ty_for_rs(tcx, *referent_ty).with_context(|| {
660 format!("Failed to format the referent of the reference type `{ty}`")
661 })?;
662 let lifetime = format_region_as_rs_lifetime(region);
663 quote! { & #lifetime #mutability #ty }
664 }
Lukasz Anforowicz087dff72023-02-17 12:13:32 -0800665 _ => bail!("The following Rust type is not supported yet: {ty}"),
Lukasz Anforowicz7860d0e2022-11-15 08:47:56 -0800666 })
667}
668
Lukasz Anforowiczaa2de7e2023-06-15 11:32:05 -0700669fn format_region_as_cc_lifetime(region: &ty::Region) -> TokenStream {
Lukasz Anforowicz36de5722023-06-16 08:17:44 -0700670 let name =
671 region.get_name().expect("Caller should use `liberate_and_deanonymize_late_bound_regions`");
Lukasz Anforowiczaa2de7e2023-06-15 11:32:05 -0700672 let name = name
673 .as_str()
674 .strip_prefix('\'')
675 .expect("All Rust lifetimes are expected to begin with the \"'\" character");
676
677 // TODO(b/286299326): Use `$a` or `$(foo)` or `$static` syntax below.
678 quote! { [[clang::annotate_type("lifetime", #name)]] }
679}
680
681fn format_region_as_rs_lifetime(region: &ty::Region) -> TokenStream {
Lukasz Anforowicz36de5722023-06-16 08:17:44 -0700682 let name =
683 region.get_name().expect("Caller should use `liberate_and_deanonymize_late_bound_regions`");
Lukasz Anforowiczaa2de7e2023-06-15 11:32:05 -0700684 let lifetime = syn::Lifetime::new(name.as_str(), proc_macro2::Span::call_site());
685 quote! { #lifetime }
686}
687
Lukasz Anforowicz73a2c0d2023-03-22 14:07:00 -0700688#[derive(Debug, Default)]
689struct ApiSnippets {
Lukasz Anforowicz33f46302023-02-10 15:38:05 -0800690 /// Main API - for example:
691 /// - A C++ declaration of a function (with a doc comment),
692 /// - A C++ definition of a struct (with a doc comment).
Lukasz Anforowicz73a2c0d2023-03-22 14:07:00 -0700693 main_api: CcSnippet,
Lukasz Anforowicz33f46302023-02-10 15:38:05 -0800694
Lukasz Anforowicz73a2c0d2023-03-22 14:07:00 -0700695 /// C++ implementation details - for example:
Lukasz Anforowicz33f46302023-02-10 15:38:05 -0800696 /// - A C++ declaration of an `extern "C"` thunk,
Lukasz Anforowicz73a2c0d2023-03-22 14:07:00 -0700697 /// - C++ `static_assert`s about struct size, aligment, and field offsets.
698 cc_details: CcSnippet,
699
700 /// Rust implementation details - for exmaple:
Lukasz Anforowicz33f46302023-02-10 15:38:05 -0800701 /// - A Rust implementation of an `extern "C"` thunk,
Lukasz Anforowicz73a2c0d2023-03-22 14:07:00 -0700702 /// - Rust `assert!`s about struct size, aligment, and field offsets.
703 rs_details: TokenStream,
Lukasz Anforowicz33f46302023-02-10 15:38:05 -0800704}
705
Lukasz Anforowicz73a2c0d2023-03-22 14:07:00 -0700706impl FromIterator<ApiSnippets> for ApiSnippets {
707 fn from_iter<I: IntoIterator<Item = ApiSnippets>>(iter: I) -> Self {
708 let mut result = ApiSnippets::default();
709 for ApiSnippets { main_api, cc_details, rs_details } in iter.into_iter() {
710 result.main_api += main_api;
711 result.cc_details += cc_details;
712 result.rs_details.extend(rs_details);
713 }
714 result
Lukasz Anforowicz0b8a02f2023-02-10 15:55:40 -0800715 }
716}
717
Lukasz Anforowicz36de5722023-06-16 08:17:44 -0700718/// Similar to `TyCtxt::liberate_and_name_late_bound_regions` but also replaces
719/// anonymous regions with new names.
720fn liberate_and_deanonymize_late_bound_regions<'tcx>(
721 tcx: TyCtxt<'tcx>,
722 sig: ty::PolyFnSig<'tcx>,
723 fn_def_id: DefId,
724) -> ty::FnSig<'tcx> {
Lukasz Anforowiczaa2de7e2023-06-15 11:32:05 -0700725 let mut anon_count: u32 = 0;
726 let mut translated_kinds: HashMap<ty::BoundVar, ty::BoundRegionKind> = HashMap::new();
727 tcx.replace_late_bound_regions_uncached(sig, |br: ty::BoundRegion| {
728 let new_kind: &ty::BoundRegionKind = translated_kinds.entry(br.var).or_insert_with(|| {
729 let name = br.kind.get_name().unwrap_or_else(|| {
730 anon_count += 1;
731 Symbol::intern(&format!("'__anon{anon_count}"))
732 });
733 let id = br.kind.get_id().unwrap_or(fn_def_id);
734 ty::BoundRegionKind::BrNamed(id, name)
735 });
736 ty::Region::new_free(tcx, fn_def_id, *new_kind)
737 })
Lukasz Anforowicz6b1ee8c2023-04-04 11:58:29 -0700738}
739
Lukasz Anforowicz36de5722023-06-16 08:17:44 -0700740fn get_fn_sig(tcx: TyCtxt, fn_def_id: LocalDefId) -> ty::FnSig {
741 let fn_def_id = fn_def_id.to_def_id(); // LocalDefId => DefId
742 let sig = tcx.fn_sig(fn_def_id).subst_identity();
743 liberate_and_deanonymize_late_bound_regions(tcx, sig, fn_def_id)
744}
745
Lukasz Anforowicz73edf152023-04-04 12:05:00 -0700746/// Formats a C++ function declaration of a thunk that wraps a Rust function
747/// identified by `fn_def_id`. `format_thunk_impl` may panic if `fn_def_id`
748/// doesn't identify a function.
Lukasz Anforowicz73a79ab2023-06-15 13:42:20 -0700749fn format_thunk_decl<'tcx>(
750 input: &Input<'tcx>,
751 fn_def_id: DefId,
752 sig: &ty::FnSig<'tcx>,
Lukasz Anforowicz9924c432023-04-04 12:51:22 -0700753 thunk_name: &TokenStream,
754) -> Result<CcSnippet> {
Lukasz Anforowicz73edf152023-04-04 12:05:00 -0700755 let tcx = input.tcx;
Lukasz Anforowicz73edf152023-04-04 12:05:00 -0700756
757 let mut prereqs = CcPrerequisites::default();
Lukasz Anforowicze7212082023-07-05 13:50:10 -0700758 let main_api_ret_type = format_ret_ty_for_cc(input, sig)?.into_tokens(&mut prereqs);
Lukasz Anforowicz73edf152023-04-04 12:05:00 -0700759
Lukasz Anforowiczc6ff4532023-04-13 15:16:30 -0700760 let mut thunk_params = {
Lukasz Anforowicze7212082023-07-05 13:50:10 -0700761 let cc_types = format_param_types_for_cc(input, sig)?;
Lukasz Anforowiczc6ff4532023-04-13 15:16:30 -0700762 sig.inputs()
763 .iter()
764 .zip(cc_types.into_iter())
765 .map(|(&ty, cc_type)| -> Result<TokenStream> {
766 let cc_type = cc_type.into_tokens(&mut prereqs);
767 if is_c_abi_compatible_by_value(ty) {
768 Ok(quote! { #cc_type })
769 } else {
770 // Rust thunk will move a value via memcpy - we need to `ensure` that
771 // invoking the C++ destructor (on the moved-away value) is safe.
Lukasz Anforowiczc6ff4532023-04-13 15:16:30 -0700772 ensure!(
Lukasz Anforowicz73a79ab2023-06-15 13:42:20 -0700773 !ty.needs_drop(tcx, tcx.param_env(fn_def_id)),
Lukasz Anforowiczc6ff4532023-04-13 15:16:30 -0700774 "Only trivially-movable and trivially-destructible types \
775 may be passed by value over the FFI boundary"
776 );
777 Ok(quote! { #cc_type* })
778 }
779 })
780 .collect::<Result<Vec<_>>>()?
781 };
Lukasz Anforowicz73edf152023-04-04 12:05:00 -0700782
783 let thunk_ret_type: TokenStream;
784 if is_c_abi_compatible_by_value(sig.output()) {
Lukasz Anforowicze7212082023-07-05 13:50:10 -0700785 thunk_ret_type = main_api_ret_type;
Lukasz Anforowicz73edf152023-04-04 12:05:00 -0700786 } else {
787 thunk_ret_type = quote! { void };
788 thunk_params.push(quote! { #main_api_ret_type* __ret_ptr });
789 prereqs.includes.insert(CcInclude::utility());
790 prereqs.includes.insert(input.support_header("internal/return_value_slot.h"));
791 };
792 Ok(CcSnippet {
793 prereqs,
794 tokens: quote! {
795 namespace __crubit_internal {
796 extern "C" #thunk_ret_type #thunk_name ( #( #thunk_params ),* );
797 }
798 },
799 })
800}
801
Lukasz Anforowicz6b1ee8c2023-04-04 11:58:29 -0700802/// Formats a thunk implementation in Rust that provides an `extern "C"` ABI for
803/// calling a Rust function identified by `fn_def_id`. `format_thunk_impl` may
804/// panic if `fn_def_id` doesn't identify a function.
805///
806/// `fully_qualified_fn_name` specifies how the thunk can identify the function
807/// to call. Examples of valid arguments:
808/// - `::crate_name::some_module::free_function`
809/// - `::crate_name::some_module::SomeStruct::method`
810/// - `<::create_name::some_module::SomeStruct as
811/// ::core::default::Default>::default`
Lukasz Anforowicz73a79ab2023-06-15 13:42:20 -0700812fn format_thunk_impl<'tcx>(
813 tcx: TyCtxt<'tcx>,
814 fn_def_id: DefId,
815 sig: &ty::FnSig<'tcx>,
Lukasz Anforowicz6b1ee8c2023-04-04 11:58:29 -0700816 thunk_name: &str,
817 fully_qualified_fn_name: TokenStream,
818) -> Result<TokenStream> {
Lukasz Anforowicz6b1ee8c2023-04-04 11:58:29 -0700819 let param_names_and_types: Vec<(Ident, Ty)> = {
Lukasz Anforowicz1b665a42023-06-20 13:04:54 -0700820 let param_names = tcx.fn_arg_names(fn_def_id).iter().enumerate().map(|(i, ident)| {
821 if ident.as_str().is_empty() {
Lukasz Anforowicz6b1ee8c2023-04-04 11:58:29 -0700822 format_ident!("__param_{i}")
Lukasz Anforowicz1b665a42023-06-20 13:04:54 -0700823 } else if ident.name == kw::SelfLower {
824 format_ident!("__self")
Lukasz Anforowicz6b1ee8c2023-04-04 11:58:29 -0700825 } else {
Lukasz Anforowicz1b665a42023-06-20 13:04:54 -0700826 make_rs_ident(ident.as_str())
Lukasz Anforowicz6b1ee8c2023-04-04 11:58:29 -0700827 }
828 });
829 let param_types = sig.inputs().iter().copied();
830 param_names.zip(param_types).collect_vec()
831 };
832
833 let mut thunk_params = param_names_and_types
834 .iter()
835 .map(|(param_name, ty)| {
836 let rs_type = format_ty_for_rs(tcx, *ty)
837 .with_context(|| format!("Error handling parameter `{param_name}`"))?;
838 Ok(if is_c_abi_compatible_by_value(*ty) {
839 quote! { #param_name: #rs_type }
840 } else {
841 quote! { #param_name: &mut ::core::mem::MaybeUninit<#rs_type> }
842 })
843 })
844 .collect::<Result<Vec<_>>>()?;
845
846 let mut thunk_ret_type = format_ty_for_rs(tcx, sig.output())?;
847 let mut thunk_body = {
848 let fn_args = param_names_and_types.iter().map(|(rs_name, ty)| {
849 if is_c_abi_compatible_by_value(*ty) {
850 quote! { #rs_name }
851 } else {
852 quote! { unsafe { #rs_name.assume_init_read() } }
853 }
854 });
855 quote! {
856 #fully_qualified_fn_name( #( #fn_args ),* )
857 }
858 };
859 if !is_c_abi_compatible_by_value(sig.output()) {
860 thunk_params.push(quote! {
861 __ret_slot: &mut ::core::mem::MaybeUninit<#thunk_ret_type>
862 });
863 thunk_ret_type = quote! { () };
864 thunk_body = quote! { __ret_slot.write(#thunk_body); };
865 };
866
Lukasz Anforowiczaa2de7e2023-06-15 11:32:05 -0700867 let generic_params = {
868 let regions = sig
869 .inputs()
870 .iter()
871 .copied()
872 .chain(std::iter::once(sig.output()))
873 .flat_map(|ty| {
874 ty.walk().filter_map(|generic_arg| match generic_arg.unpack() {
875 ty::GenericArgKind::Const(_) | ty::GenericArgKind::Type(_) => None,
876 ty::GenericArgKind::Lifetime(region) => Some(region),
877 })
878 })
879 .filter(|region| match region.kind() {
880 RegionKind::ReStatic => false,
881 RegionKind::ReFree(_) => true,
882 _ => panic!("Unexpected region kind: {region}"),
883 })
884 .sorted_by_key(|region| {
Lukasz Anforowicz36de5722023-06-16 08:17:44 -0700885 region
886 .get_name()
887 .expect("Caller should use `liberate_and_deanonymize_late_bound_regions`")
Lukasz Anforowiczaa2de7e2023-06-15 11:32:05 -0700888 })
889 .dedup()
890 .collect_vec();
891 if regions.is_empty() {
892 quote! {}
893 } else {
894 let lifetimes = regions.into_iter().map(|region| format_region_as_rs_lifetime(&region));
895 quote! { < #( #lifetimes ),* > }
896 }
897 };
898
Lukasz Anforowicz6b1ee8c2023-04-04 11:58:29 -0700899 let thunk_name = make_rs_ident(thunk_name);
900 Ok(quote! {
901 #[no_mangle]
Lukasz Anforowiczaa2de7e2023-06-15 11:32:05 -0700902 extern "C" fn #thunk_name #generic_params ( #( #thunk_params ),* ) -> #thunk_ret_type {
Lukasz Anforowicz6b1ee8c2023-04-04 11:58:29 -0700903 #thunk_body
904 }
905 })
906}
907
Lukasz Anforowicz5c1b3ad2023-04-13 17:05:00 -0700908fn check_fn_sig(sig: &ty::FnSig) -> Result<()> {
909 if sig.c_variadic {
910 // TODO(b/254097223): Add support for variadic functions.
911 bail!("C variadic functions are not supported (b/254097223)");
912 }
913
914 match sig.unsafety {
915 Unsafety::Normal => (),
916 Unsafety::Unsafe => {
917 // TODO(b/254095482): Figure out how to handle `unsafe` functions.
918 bail!("Bindings for `unsafe` functions are not fully designed yet (b/254095482)");
919 }
920 }
921
922 Ok(())
923}
924
925/// Returns `Ok(())` if no thunk is required.
926/// Otherwise returns an error the describes why the thunk is needed.
927fn is_thunk_required(sig: &ty::FnSig) -> Result<()> {
928 match sig.abi {
929 // "C" ABI is okay: Before https://rust-lang.github.io/rfcs/2945-c-unwind-abi.html a
930 // Rust panic that "escapes" a "C" ABI function leads to Undefined Behavior. This is
931 // unfortunate, but Crubit's `panics_and_exceptions.md` documents that `-Cpanic=abort`
932 // is the only supported configuration.
933 //
934 // After https://rust-lang.github.io/rfcs/2945-c-unwind-abi.html a Rust panic that
935 // tries to "escape" a "C" ABI function will terminate the program. This is okay.
936 rustc_target::spec::abi::Abi::C { unwind: false } => (),
937
938 // "C-unwind" ABI is okay: After
939 // https://rust-lang.github.io/rfcs/2945-c-unwind-abi.html a new "C-unwind" ABI may be
940 // used by Rust functions that want to safely propagate Rust panics through frames that
941 // may belong to another language.
942 rustc_target::spec::abi::Abi::C { unwind: true } => (),
943
944 // All other ABIs trigger thunk generation. This covers Rust ABI functions, but also
945 // ABIs that theoretically are understood both by C++ and Rust (e.g. see
946 // `format_cc_call_conv_as_clang_attribute` in `rs_bindings_from_cc/src_code_gen.rs`).
947 _ => bail!("Calling convention other than `extern \"C\"` requires a thunk"),
948 };
949
950 ensure!(is_c_abi_compatible_by_value(sig.output()), "Return type requires a thunk");
951 for (i, param_ty) in sig.inputs().iter().enumerate() {
952 ensure!(is_c_abi_compatible_by_value(*param_ty), "Type of parameter #{i} requires a thunk",);
953 }
954
955 Ok(())
956}
957
Lukasz Anforowiczae1ae172023-07-05 12:11:24 -0700958#[derive(Debug, Eq, PartialEq)]
959enum FunctionKind {
960 /// Free function (i.e. not a method).
961 Free,
962
963 /// Static method (i.e. the first parameter is not named `self`).
964 StaticMethod,
965
966 /// Instance method taking `self` by value (i.e. `self: Self`).
967 MethodTakingSelfByValue,
Lukasz Anforowiczec4c8c72023-07-05 12:30:57 -0700968
969 /// Instance method taking `self` by reference (i.e. `&self` or `&mut
970 /// self`).
971 MethodTakingSelfByRef,
Lukasz Anforowiczae1ae172023-07-05 12:11:24 -0700972}
973
974impl FunctionKind {
975 fn has_self_param(&self) -> bool {
976 match self {
Lukasz Anforowiczec4c8c72023-07-05 12:30:57 -0700977 FunctionKind::MethodTakingSelfByValue | FunctionKind::MethodTakingSelfByRef => true,
Lukasz Anforowiczae1ae172023-07-05 12:11:24 -0700978 FunctionKind::Free | FunctionKind::StaticMethod => false,
979 }
980 }
981}
982
Googlerfb204272022-12-02 00:52:05 -0800983/// Formats a function with the given `local_def_id`.
Lukasz Anforowicze4333062022-10-17 14:47:53 -0700984///
Googlerfb204272022-12-02 00:52:05 -0800985/// Will panic if `local_def_id`
Lukasz Anforowicz7123f212022-10-20 08:51:04 -0700986/// - is invalid
987/// - doesn't identify a function,
Lukasz Anforowicz73a2c0d2023-03-22 14:07:00 -0700988fn format_fn(input: &Input, local_def_id: LocalDefId) -> Result<ApiSnippets> {
Lukasz Anforowiczbe25c762023-01-17 12:43:52 -0800989 let tcx = input.tcx;
Googler624580b2022-12-01 01:23:42 -0800990 let def_id: DefId = local_def_id.to_def_id(); // Convert LocalDefId to DefId.
Lukasz Anforowicz903fc632022-10-25 08:55:33 -0700991
Lukasz Anforowicz56d94fc2023-02-17 12:23:20 -0800992 ensure!(
993 tcx.generics_of(def_id).count() == 0,
994 "Generic functions are not supported yet (b/259749023)"
995 );
Lukasz Anforowicz56d94fc2023-02-17 12:23:20 -0800996
Lukasz Anforowiczaa2de7e2023-06-15 11:32:05 -0700997 let sig = get_fn_sig(tcx, local_def_id);
Lukasz Anforowicz5c1b3ad2023-04-13 17:05:00 -0700998 check_fn_sig(&sig)?;
999 let needs_thunk = is_thunk_required(&sig).is_err();
Lukasz Anforowicz9924c432023-04-04 12:51:22 -07001000 let thunk_name = {
Lukasz Anforowicz36de5722023-06-16 08:17:44 -07001001 let symbol_name = {
1002 // Call to `mono` is ok - `generics_of` have been checked above.
1003 let instance = ty::Instance::mono(tcx, def_id);
1004 tcx.symbol_name(instance).name
1005 };
1006 if needs_thunk {
1007 format!("__crubit_thunk_{}", &escape_non_identifier_chars(symbol_name))
1008 } else {
1009 symbol_name.to_string()
1010 }
Lukasz Anforowicz9924c432023-04-04 12:51:22 -07001011 };
Googler624580b2022-12-01 01:23:42 -08001012
Lukasz Anforowicz954696e2023-05-10 12:18:48 -07001013 let fully_qualified_fn_name = FullyQualifiedName::new(tcx, def_id);
1014 let short_fn_name =
1015 fully_qualified_fn_name.name.expect("Functions are assumed to always have a name");
Lukasz Anforowicza691cf52023-03-08 12:24:33 -08001016 let main_api_fn_name =
Lukasz Anforowicz954696e2023-05-10 12:18:48 -07001017 format_cc_ident(short_fn_name.as_str()).context("Error formatting function name")?;
Lukasz Anforowicza577d822022-12-12 15:00:46 -08001018
Lukasz Anforowicza691cf52023-03-08 12:24:33 -08001019 let mut main_api_prereqs = CcPrerequisites::default();
Lukasz Anforowicz8574c9d2023-04-13 15:11:20 -07001020 let main_api_ret_type = format_ret_ty_for_cc(input, &sig)?.into_tokens(&mut main_api_prereqs);
Lukasz Anforowicza691cf52023-03-08 12:24:33 -08001021
1022 struct Param<'tcx> {
1023 cc_name: TokenStream,
1024 cc_type: TokenStream,
Lukasz Anforowicza691cf52023-03-08 12:24:33 -08001025 ty: Ty<'tcx>,
1026 }
1027 let params = {
1028 let names = tcx.fn_arg_names(def_id).iter();
Lukasz Anforowiczc6ff4532023-04-13 15:16:30 -07001029 let cc_types = format_param_types_for_cc(input, &sig)?;
Lukasz Anforowicza691cf52023-03-08 12:24:33 -08001030 names
Lukasz Anforowicza691cf52023-03-08 12:24:33 -08001031 .enumerate()
Lukasz Anforowiczc6ff4532023-04-13 15:16:30 -07001032 .zip(sig.inputs().iter())
1033 .zip(cc_types.into_iter())
1034 .map(|(((i, name), &ty), cc_type)| {
Lukasz Anforowicza691cf52023-03-08 12:24:33 -08001035 let cc_name = format_cc_ident(name.as_str())
1036 .unwrap_or_else(|_err| format_cc_ident(&format!("__param_{i}")).unwrap());
Lukasz Anforowiczc6ff4532023-04-13 15:16:30 -07001037 let cc_type = cc_type.into_tokens(&mut main_api_prereqs);
1038 Param { cc_name, cc_type, ty }
Lukasz Anforowicza691cf52023-03-08 12:24:33 -08001039 })
Lukasz Anforowiczc6ff4532023-04-13 15:16:30 -07001040 .collect_vec()
Lukasz Anforowicza691cf52023-03-08 12:24:33 -08001041 };
Lukasz Anforowicz6753d402023-02-10 16:41:23 -08001042
Lukasz Anforowiczae1ae172023-07-05 12:11:24 -07001043 let self_ty: Option<Ty> = match tcx.impl_of_method(def_id) {
Googlere1585652023-04-24 06:02:39 -07001044 Some(impl_id) => match tcx.impl_subject(impl_id).subst_identity() {
Lukasz Anforowiczae1ae172023-07-05 12:11:24 -07001045 ty::ImplSubject::Inherent(ty) => Some(ty),
Lukasz Anforowicz43f06c62023-02-10 17:14:19 -08001046 ty::ImplSubject::Trait(_) => panic!("Trait methods should be filtered by caller"),
1047 },
1048 None => None,
1049 };
Lukasz Anforowiczae1ae172023-07-05 12:11:24 -07001050
1051 let method_kind = match tcx.hir().get_by_def_id(local_def_id) {
1052 Node::Item(_) => FunctionKind::Free,
1053 Node::ImplItem(_) => match tcx.fn_arg_names(def_id).get(0) {
1054 Some(arg_name) if arg_name.name == kw::SelfLower => {
1055 let self_ty = self_ty.expect("ImplItem => non-None `self_ty`");
1056 if params[0].ty == self_ty {
1057 FunctionKind::MethodTakingSelfByValue
1058 } else {
Lukasz Anforowiczec4c8c72023-07-05 12:30:57 -07001059 match params[0].ty.kind() {
1060 ty::TyKind::Ref(_, referent_ty, _) if *referent_ty == self_ty => {
1061 FunctionKind::MethodTakingSelfByRef
1062 }
1063 _ => bail!("Unsupported `self` type"),
1064 }
Lukasz Anforowiczae1ae172023-07-05 12:11:24 -07001065 }
1066 }
1067 _ => FunctionKind::StaticMethod,
1068 },
1069 other => panic!("Unexpected HIR node kind: {other:?}"),
1070 };
Lukasz Anforowiczec4c8c72023-07-05 12:30:57 -07001071 let method_qualifiers = match method_kind {
1072 FunctionKind::Free | FunctionKind::StaticMethod => quote! {},
1073 FunctionKind::MethodTakingSelfByValue => quote! { && },
1074 FunctionKind::MethodTakingSelfByRef => match params[0].ty.kind() {
1075 ty::TyKind::Ref(region, _, mutability) => {
1076 let lifetime_annotation = format_region_as_cc_lifetime(region);
1077 let mutability = match mutability {
1078 Mutability::Mut => quote! {},
1079 Mutability::Not => quote! { const },
1080 };
1081 quote! { #mutability #lifetime_annotation }
1082 }
1083 _ => panic!("Expecting TyKind::Ref for MethodKind...Self...Ref"),
1084 },
Lukasz Anforowiczae1ae172023-07-05 12:11:24 -07001085 };
1086
1087 let struct_name = match self_ty {
1088 Some(ty) => match ty.kind() {
1089 ty::TyKind::Adt(adt, substs) => {
1090 assert_eq!(0, substs.len(), "Callers should filter out generics");
1091 Some(FullyQualifiedName::new(tcx, adt.did()))
1092 }
1093 _ => panic!("Non-ADT `impl`s should be filtered by caller"),
1094 },
1095 None => None,
1096 };
Lukasz Anforowicz954696e2023-05-10 12:18:48 -07001097 let needs_definition = short_fn_name.as_str() != thunk_name;
Lukasz Anforowiczae1ae172023-07-05 12:11:24 -07001098 let main_api_params = params
1099 .iter()
1100 .skip(if method_kind.has_self_param() { 1 } else { 0 })
1101 .map(|Param { cc_name, cc_type, .. }| quote! { #cc_type #cc_name })
1102 .collect_vec();
Lukasz Anforowicz6753d402023-02-10 16:41:23 -08001103 let main_api = {
1104 let doc_comment = {
1105 let doc_comment = format_doc_comment(tcx, local_def_id);
1106 quote! { __NEWLINE__ #doc_comment }
1107 };
1108
Lukasz Anforowicza691cf52023-03-08 12:24:33 -08001109 let mut prereqs = main_api_prereqs.clone();
Lukasz Anforowicz6753d402023-02-10 16:41:23 -08001110 prereqs.move_defs_to_fwd_decls();
Lukasz Anforowicza691cf52023-03-08 12:24:33 -08001111
Lukasz Anforowiczae1ae172023-07-05 12:11:24 -07001112 let static_ = if method_kind == FunctionKind::StaticMethod {
Lukasz Anforowicza691cf52023-03-08 12:24:33 -08001113 quote! { static }
1114 } else {
1115 quote! {}
1116 };
Lukasz Anforowiczb9e515a2023-07-05 12:48:22 -07001117 let extern_c = if !needs_definition {
Lukasz Anforowicz6753d402023-02-10 16:41:23 -08001118 quote! { extern "C" }
Lukasz Anforowicz2f849cf2022-11-17 15:52:39 -08001119 } else {
Lukasz Anforowiczb9e515a2023-07-05 12:48:22 -07001120 quote! {}
Lukasz Anforowicz6753d402023-02-10 16:41:23 -08001121 };
1122 CcSnippet {
1123 prereqs,
1124 tokens: quote! {
Lukasz Anforowicza0502fb2023-02-13 15:33:18 -08001125 __NEWLINE__
Lukasz Anforowicz6753d402023-02-10 16:41:23 -08001126 #doc_comment
Lukasz Anforowiczb9e515a2023-07-05 12:48:22 -07001127 #static_ #extern_c
Lukasz Anforowiczae1ae172023-07-05 12:11:24 -07001128 #main_api_ret_type #main_api_fn_name (
1129 #( #main_api_params ),*
1130 ) #method_qualifiers;
Lukasz Anforowicza0502fb2023-02-13 15:33:18 -08001131 __NEWLINE__
Lukasz Anforowicz6753d402023-02-10 16:41:23 -08001132 },
1133 }
1134 };
Lukasz Anforowicz73a2c0d2023-03-22 14:07:00 -07001135 let cc_details = if !needs_definition {
1136 CcSnippet::default()
Lukasz Anforowicz6753d402023-02-10 16:41:23 -08001137 } else {
Devin Jeanpierre81aec502023-04-04 15:33:39 -07001138 let thunk_name = format_cc_ident(&thunk_name).context("Error formatting thunk name")?;
Lukasz Anforowicz73a2c0d2023-03-22 14:07:00 -07001139 let struct_name = match struct_name.as_ref() {
1140 None => quote! {},
Lukasz Anforowicz954696e2023-05-10 12:18:48 -07001141 Some(fully_qualified_name) => {
1142 let name = fully_qualified_name.name.expect("Structs always have a name");
1143 let name = format_cc_ident(name.as_str())
Lukasz Anforowicz73a2c0d2023-03-22 14:07:00 -07001144 .expect("Caller of format_fn should verify struct via format_adt_core");
1145 quote! { #name :: }
1146 }
1147 };
1148
1149 let mut prereqs = main_api_prereqs;
Devin Jeanpierre81aec502023-04-04 15:33:39 -07001150 let thunk_decl =
Lukasz Anforowicz73a79ab2023-06-15 13:42:20 -07001151 format_thunk_decl(input, def_id, &sig, &thunk_name)?.into_tokens(&mut prereqs);
Lukasz Anforowicz73edf152023-04-04 12:05:00 -07001152
Lukasz Anforowicz73a2c0d2023-03-22 14:07:00 -07001153 let mut thunk_args = params
1154 .iter()
Lukasz Anforowiczae1ae172023-07-05 12:11:24 -07001155 .enumerate()
1156 .map(|(i, Param { cc_name, ty, .. })| {
1157 if i == 0 && method_kind.has_self_param() {
Lukasz Anforowiczec4c8c72023-07-05 12:30:57 -07001158 if method_kind == FunctionKind::MethodTakingSelfByValue {
1159 quote! { this }
1160 } else {
1161 quote! { *this }
1162 }
Lukasz Anforowiczae1ae172023-07-05 12:11:24 -07001163 } else if is_c_abi_compatible_by_value(*ty) {
Lukasz Anforowicz73a2c0d2023-03-22 14:07:00 -07001164 quote! { #cc_name }
1165 } else {
1166 quote! { & #cc_name }
1167 }
1168 })
1169 .collect_vec();
Lukasz Anforowicz73a2c0d2023-03-22 14:07:00 -07001170 let impl_body: TokenStream;
1171 if is_c_abi_compatible_by_value(sig.output()) {
Lukasz Anforowicz73a2c0d2023-03-22 14:07:00 -07001172 impl_body = quote! {
1173 return __crubit_internal :: #thunk_name( #( #thunk_args ),* );
1174 };
1175 } else {
Lukasz Anforowicz73a2c0d2023-03-22 14:07:00 -07001176 thunk_args.push(quote! { __ret_slot.Get() });
1177 impl_body = quote! {
1178 crubit::ReturnValueSlot<#main_api_ret_type> __ret_slot;
1179 __crubit_internal :: #thunk_name( #( #thunk_args ),* );
1180 return std::move(__ret_slot).AssumeInitAndTakeValue();
1181 };
1182 prereqs.includes.insert(CcInclude::utility());
1183 prereqs.includes.insert(input.support_header("internal/return_value_slot.h"));
1184 };
1185 CcSnippet {
1186 prereqs,
1187 tokens: quote! {
1188 __NEWLINE__
Lukasz Anforowicz73edf152023-04-04 12:05:00 -07001189 #thunk_decl
Lukasz Anforowicz73a2c0d2023-03-22 14:07:00 -07001190 inline #main_api_ret_type #struct_name #main_api_fn_name (
Lukasz Anforowiczae1ae172023-07-05 12:11:24 -07001191 #( #main_api_params ),* ) #method_qualifiers {
Lukasz Anforowicz73a2c0d2023-03-22 14:07:00 -07001192 #impl_body
1193 }
1194 __NEWLINE__
1195 },
1196 }
1197 };
1198
1199 let rs_details = if !needs_thunk {
1200 quote! {}
1201 } else {
Lukasz Anforowicz954696e2023-05-10 12:18:48 -07001202 let fully_qualified_fn_name = match struct_name.as_ref() {
1203 None => fully_qualified_fn_name.format_for_rs(),
1204 Some(struct_name) => {
1205 let fn_name = make_rs_ident(short_fn_name.as_str());
1206 let struct_name = struct_name.format_for_rs();
1207 quote! { #struct_name :: #fn_name }
Lukasz Anforowiczbad99632022-11-18 16:39:13 -08001208 }
Lukasz Anforowicz6753d402023-02-10 16:41:23 -08001209 };
Lukasz Anforowicz73a79ab2023-06-15 13:42:20 -07001210 format_thunk_impl(tcx, def_id, &sig, &thunk_name, fully_qualified_fn_name)?
Lukasz Anforowiczbad99632022-11-18 16:39:13 -08001211 };
Lukasz Anforowicz73a2c0d2023-03-22 14:07:00 -07001212 Ok(ApiSnippets { main_api, cc_details, rs_details })
Lukasz Anforowicze4333062022-10-17 14:47:53 -07001213}
1214
Lukasz Anforowicz75894832022-11-22 18:25:28 -08001215/// Represents bindings for the "core" part of an algebraic data type (an ADT -
1216/// a struct, an enum, or a union) in a way that supports later injecting the
1217/// other parts like so:
1218///
1219/// ```
1220/// quote! {
Lukasz Anforowiczf4501b42023-01-07 09:41:29 -08001221/// #keyword #alignment #name final {
Lukasz Anforowicz75894832022-11-22 18:25:28 -08001222/// #core
Lukasz Anforowicz3b579542023-02-06 11:23:49 -08001223/// #decls_of_other_parts // (e.g. struct fields, methods, etc.)
Lukasz Anforowicz75894832022-11-22 18:25:28 -08001224/// }
1225/// }
1226/// ```
Lukasz Anforowiczf4501b42023-01-07 09:41:29 -08001227///
1228/// `keyword`, `name` are stored separately, to support formatting them as a
1229/// forward declaration - e.g. `struct SomeStruct`.
Lukasz Anforowicz15a60022023-07-07 09:05:49 -07001230struct AdtCoreBindings<'tcx> {
Lukasz Anforowicz3b579542023-02-06 11:23:49 -08001231 /// DefId of the ADT.
1232 def_id: DefId,
1233
Lukasz Anforowiczf4501b42023-01-07 09:41:29 -08001234 /// C++ tag - e.g. `struct`, `class`, `enum`, or `union`. This isn't always
1235 /// a direct mapping from Rust (e.g. a Rust `enum` might end up being
1236 /// represented as an opaque C++ `struct`).
1237 keyword: TokenStream,
1238
Lukasz Anforowiczf4501b42023-01-07 09:41:29 -08001239 /// C++ translation of the ADT identifier - e.g. `SomeStruct`.
Lukasz Anforowiczce56a802023-04-04 12:18:19 -07001240 ///
1241 /// A _short_ name is sufficient (i.e. there is no need to use a
1242 /// namespace-qualified name), for `CcSnippet`s that are emitted into
1243 /// the same namespace as the ADT. (This seems to be all the snippets
1244 /// today.)
1245 cc_short_name: TokenStream,
Lukasz Anforowicz75894832022-11-22 18:25:28 -08001246
Lukasz Anforowicz3b579542023-02-06 11:23:49 -08001247 /// Rust spelling of the ADT type - e.g.
Lukasz Anforowiczce56a802023-04-04 12:18:19 -07001248 /// `::some_crate::some_module::SomeStruct`.
1249 rs_fully_qualified_name: TokenStream,
Lukasz Anforowicz3b579542023-02-06 11:23:49 -08001250
Lukasz Anforowicz15a60022023-07-07 09:05:49 -07001251 self_ty: Ty<'tcx>,
Lukasz Anforowicz3b579542023-02-06 11:23:49 -08001252 alignment_in_bytes: u64,
1253 size_in_bytes: u64,
Lukasz Anforowicz75894832022-11-22 18:25:28 -08001254}
1255
Lukasz Anforowiczf36762a2023-03-02 18:43:07 -08001256/// Like `TyCtxt::is_directly_public`, but works not only with `LocalDefId`, but
1257/// also with `DefId`.
1258fn is_directly_public(tcx: TyCtxt, def_id: DefId) -> bool {
1259 match def_id.as_local() {
1260 None => {
1261 // This mimics the checks in `try_print_visible_def_path_recur` in
1262 // `compiler/rustc_middle/src/ty/print/pretty.rs`.
1263 let actual_parent = tcx.opt_parent(def_id);
1264 let visible_parent = tcx.visible_parent_map(()).get(&def_id).copied();
1265 actual_parent == visible_parent
1266 }
1267 Some(local_def_id) => tcx.effective_visibilities(()).is_directly_public(local_def_id),
1268 }
1269}
1270
Lukasz Anforowicz735bfb82023-03-15 16:18:44 -07001271fn get_layout<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Result<Layout<'tcx>> {
Lukasz Anforowicz2aab0192023-06-15 12:05:39 -07001272 let param_env = match ty.ty_adt_def() {
1273 None => ty::ParamEnv::empty(),
1274 Some(adt_def) => tcx.param_env(adt_def.did()),
1275 };
Lukasz Anforowicz735bfb82023-03-15 16:18:44 -07001276
1277 tcx.layout_of(param_env.and(ty)).map(|ty_and_layout| ty_and_layout.layout).map_err(
1278 |layout_err| {
1279 // Have to use `.map_err`, because `LayoutError` doesn't satisfy the
1280 // `anyhow::context::ext::StdError` trait bound.
1281 anyhow!("Error computing the layout: {layout_err}")
1282 },
1283 )
1284}
1285
Lukasz Anforowicz75894832022-11-22 18:25:28 -08001286/// Formats the core of an algebraic data type (an ADT - a struct, an enum, or a
1287/// union) represented by `def_id`.
1288///
1289/// The "core" means things that are necessary for a succesful binding (e.g.
1290/// inability to generate a correct C++ destructor means that the ADT cannot
1291/// have any bindings). "core" excludes things that are A) infallible (e.g.
1292/// struct or union fields which can always be translated into private, opaque
1293/// blobs of bytes) or B) optional (e.g. a problematic instance method
1294/// can just be ignored, unlike a problematic destructor). The split between
1295/// fallible "core" and non-fallible "rest" is motivated by the need to avoid
1296/// cycles / infinite recursion (e.g. when processing fields that refer back to
1297/// the struct type, possible with an indirection of a pointer).
1298///
1299/// `format_adt_core` is used both to 1) format bindings for the core of an ADT,
1300/// and 2) check if formatting would have succeeded (e.g. when called from
1301/// `format_ty`). The 2nd case is needed for ADTs defined in any crate - this
1302/// is why the `def_id` parameter is a DefId rather than LocalDefId.
1303//
Lukasz Anforowicz1ab5e872022-12-05 10:07:00 -08001304// TODO(b/259724276): This function's results should be memoized.
Lukasz Anforowicz15a60022023-07-07 09:05:49 -07001305fn format_adt_core(tcx: TyCtxt<'_>, def_id: DefId) -> Result<AdtCoreBindings<'_>> {
1306 let self_ty = tcx.type_of(def_id).subst_identity();
1307 assert!(self_ty.is_adt());
Lukasz Anforowiczf36762a2023-03-02 18:43:07 -08001308 assert!(is_directly_public(tcx, def_id), "Caller should verify");
1309
Lukasz Anforowicz15a60022023-07-07 09:05:49 -07001310 if self_ty.needs_drop(tcx, tcx.param_env(def_id)) {
Lukasz Anforowicz75894832022-11-22 18:25:28 -08001311 // TODO(b/258251148): Support custom `Drop` impls.
1312 bail!("`Drop` trait and \"drop glue\" are not supported yet (b/258251148)");
1313 }
1314
Lukasz Anforowicz15a60022023-07-07 09:05:49 -07001315 let adt_def = self_ty.ty_adt_def().expect("`def_id` needs to identify an ADT");
Lukasz Anforowiczcc7a76b2023-02-28 14:19:42 -08001316 let keyword = match adt_def.adt_kind() {
Lukasz Anforowicz32ad9562023-03-10 14:48:25 -08001317 ty::AdtKind::Struct | ty::AdtKind::Enum => quote! { struct },
Lukasz Anforowiczcc7a76b2023-02-28 14:19:42 -08001318 ty::AdtKind::Union => quote! { union },
Lukasz Anforowiczcc7a76b2023-02-28 14:19:42 -08001319 };
1320
Lukasz Anforowicz735bfb82023-03-15 16:18:44 -07001321 let item_name = tcx.item_name(def_id);
Lukasz Anforowicz15a60022023-07-07 09:05:49 -07001322 let rs_fully_qualified_name = format_ty_for_rs(tcx, self_ty)?;
Lukasz Anforowiczce56a802023-04-04 12:18:19 -07001323 let cc_short_name =
1324 format_cc_ident(item_name.as_str()).context("Error formatting item name")?;
Lukasz Anforowicz3b579542023-02-06 11:23:49 -08001325
Lukasz Anforowicz15a60022023-07-07 09:05:49 -07001326 let layout = get_layout(tcx, self_ty)
Lukasz Anforowicz735bfb82023-03-15 16:18:44 -07001327 .with_context(|| format!("Error computing the layout of #{item_name}"))?;
Lukasz Anforowicz74e090d2023-04-25 15:14:14 -07001328 ensure!(layout.abi().is_sized(), "Bindings for dynamically sized types are not supported.");
Lukasz Anforowicz3b579542023-02-06 11:23:49 -08001329 let alignment_in_bytes = {
Lukasz Anforowicz75894832022-11-22 18:25:28 -08001330 // Only the ABI-mandated alignment is considered (i.e. `AbiAndPrefAlign::pref`
1331 // is ignored), because 1) Rust's `std::mem::align_of` returns the
1332 // ABI-mandated alignment and 2) the generated C++'s `alignas(...)`
1333 // should specify the minimal/mandatory alignment.
Lukasz Anforowicz3b579542023-02-06 11:23:49 -08001334 layout.align().abi.bytes()
Lukasz Anforowicz75894832022-11-22 18:25:28 -08001335 };
Lukasz Anforowicz3b579542023-02-06 11:23:49 -08001336 let size_in_bytes = layout.size().bytes();
1337 ensure!(size_in_bytes != 0, "Zero-sized types (ZSTs) are not supported (b/258259459)");
Lukasz Anforowicz75894832022-11-22 18:25:28 -08001338
Lukasz Anforowiczf4501b42023-01-07 09:41:29 -08001339 Ok(AdtCoreBindings {
Lukasz Anforowicz3b579542023-02-06 11:23:49 -08001340 def_id,
Lukasz Anforowiczcc7a76b2023-02-28 14:19:42 -08001341 keyword,
Lukasz Anforowiczce56a802023-04-04 12:18:19 -07001342 cc_short_name,
1343 rs_fully_qualified_name,
Lukasz Anforowicz15a60022023-07-07 09:05:49 -07001344 self_ty,
Lukasz Anforowicz3b579542023-02-06 11:23:49 -08001345 alignment_in_bytes,
1346 size_in_bytes,
Lukasz Anforowiczf4501b42023-01-07 09:41:29 -08001347 })
Lukasz Anforowicz75894832022-11-22 18:25:28 -08001348}
1349
Lukasz Anforowicz15a60022023-07-07 09:05:49 -07001350fn format_fields<'tcx>(input: &Input<'tcx>, core: &AdtCoreBindings<'tcx>) -> ApiSnippets {
Lukasz Anforowicz43f06c62023-02-10 17:14:19 -08001351 let tcx = input.tcx;
1352
Lukasz Anforowicz2eb24292023-03-14 09:38:21 -07001353 // TODO(b/259749095): Support non-empty set of generic parameters.
Googlera1e72282023-04-25 08:06:40 -07001354 let substs_ref = ty::List::empty();
Lukasz Anforowicz2eb24292023-03-14 09:38:21 -07001355
Lukasz Anforowicz672c5152023-03-15 16:23:00 -07001356 struct FieldTypeInfo {
1357 size: u64,
Lukasz Anforowicz2eb24292023-03-14 09:38:21 -07001358 cc_type: CcSnippet,
Lukasz Anforowicz672c5152023-03-15 16:23:00 -07001359 }
1360 struct Field {
1361 type_info: Result<FieldTypeInfo>,
1362 cc_name: TokenStream,
Lukasz Anforowiczcf60f522023-03-14 10:03:55 -07001363 rs_name: TokenStream,
1364 is_public: bool,
Lukasz Anforowicz735bfb82023-03-15 16:18:44 -07001365 index: usize,
Lukasz Anforowicz2eb24292023-03-14 09:38:21 -07001366 offset: u64,
Lukasz Anforowicz735bfb82023-03-15 16:18:44 -07001367 offset_of_next_field: u64,
Lukasz Anforowiczf4b37602023-06-01 16:16:34 -07001368 doc_comment: TokenStream,
Lukasz Anforowicz2eb24292023-03-14 09:38:21 -07001369 }
Lukasz Anforowicz15a60022023-07-07 09:05:49 -07001370 let layout = get_layout(tcx, core.self_ty)
1371 .expect("Layout should be already verified by `format_adt_core`");
1372 let fields: Vec<Field> = if core.self_ty.is_enum() || core.self_ty.is_union() {
Lukasz Anforowicz2eb24292023-03-14 09:38:21 -07001373 // Note that `#[repr(Rust)]` unions don't guarantee that all their fields
1374 // have offset 0.
Lukasz Anforowicz672c5152023-03-15 16:23:00 -07001375 vec![Field {
1376 type_info: Err(anyhow!(
1377 "No support for bindings of individual fields of \
1378 `union` (b/272801632) or `enum`"
1379 )),
1380 cc_name: quote! { __opaque_blob_of_bytes },
1381 rs_name: quote! { __opaque_blob_of_bytes },
1382 is_public: false,
1383 index: 0,
1384 offset: 0,
1385 offset_of_next_field: core.size_in_bytes,
Lukasz Anforowiczf4b37602023-06-01 16:16:34 -07001386 doc_comment: quote! {},
Lukasz Anforowicz672c5152023-03-15 16:23:00 -07001387 }]
Lukasz Anforowicz2eb24292023-03-14 09:38:21 -07001388 } else {
Lukasz Anforowicz15a60022023-07-07 09:05:49 -07001389 let mut fields = core
1390 .self_ty
Lukasz Anforowicz2eb24292023-03-14 09:38:21 -07001391 .ty_adt_def()
1392 .expect("`core.def_id` needs to identify an ADT")
1393 .all_fields()
Lukasz Anforowicz672c5152023-03-15 16:23:00 -07001394 .sorted_by_key(|f| tcx.def_span(f.did))
Lukasz Anforowicz2eb24292023-03-14 09:38:21 -07001395 .enumerate()
Lukasz Anforowicz672c5152023-03-15 16:23:00 -07001396 .map(|(index, field_def)| {
Lukasz Anforowicz735bfb82023-03-15 16:18:44 -07001397 let field_ty = field_def.ty(tcx, substs_ref);
Lukasz Anforowicz672c5152023-03-15 16:23:00 -07001398 let size = get_layout(tcx, field_ty).map(|layout| layout.size().bytes());
1399 let type_info = size.and_then(|size| {
Googler7a77a802023-04-21 08:32:50 -07001400 Ok(FieldTypeInfo {
1401 size,
1402 cc_type: format_ty_for_cc(input, field_ty, TypeLocation::Other)?,
1403 })
Lukasz Anforowicz672c5152023-03-15 16:23:00 -07001404 });
1405 let name = field_def.ident(tcx);
Devin Jeanpierre81aec502023-04-04 15:33:39 -07001406 let cc_name = format_cc_ident(name.as_str())
1407 .unwrap_or_else(|_err| format_ident!("__field{index}").into_token_stream());
Lukasz Anforowiczcf60f522023-03-14 10:03:55 -07001408 let rs_name = {
1409 let name_starts_with_digit = name
1410 .as_str()
1411 .chars()
1412 .next()
1413 .expect("Empty names are unexpected (here and in general)")
1414 .is_ascii_digit();
1415 if name_starts_with_digit {
1416 let index = Literal::usize_unsuffixed(index);
1417 quote! { #index }
1418 } else {
1419 let name = make_rs_ident(name.as_str());
1420 quote! { #name }
1421 }
1422 };
Lukasz Anforowicz735bfb82023-03-15 16:18:44 -07001423
1424 // `offset` and `offset_of_next_field` will be fixed by FieldsShape::Arbitrary
1425 // branch below.
1426 let offset = 0;
1427 let offset_of_next_field = 0;
1428
Lukasz Anforowicz672c5152023-03-15 16:23:00 -07001429 Field {
1430 type_info,
Lukasz Anforowicz735bfb82023-03-15 16:18:44 -07001431 cc_name,
Lukasz Anforowicz735bfb82023-03-15 16:18:44 -07001432 rs_name,
Lukasz Anforowiczf4b37602023-06-01 16:16:34 -07001433 is_public: field_def.vis == ty::Visibility::Public,
Lukasz Anforowicz735bfb82023-03-15 16:18:44 -07001434 index,
Lukasz Anforowicz735bfb82023-03-15 16:18:44 -07001435 offset,
1436 offset_of_next_field,
Lukasz Anforowiczf4b37602023-06-01 16:16:34 -07001437 doc_comment: format_doc_comment(tcx, field_def.did.expect_local()),
Lukasz Anforowicz2eb24292023-03-14 09:38:21 -07001438 }
1439 })
Lukasz Anforowicz672c5152023-03-15 16:23:00 -07001440 .collect_vec();
1441 match layout.fields() {
1442 FieldsShape::Arbitrary { offsets, .. } => {
1443 for (index, offset) in offsets.iter().enumerate() {
1444 // Documentation of `FieldsShape::Arbitrary says that the offsets are "ordered
1445 // to match the source definition order". We can coorelate them with elements
1446 // of the `fields` vector because we've explicitly `sorted_by_key` using
1447 // `def_span`.
1448 fields[index].offset = offset.bytes();
1449 }
Devin Jeanpierre9e15d0b2023-04-06 13:18:22 -07001450 // Sort by offset first; ZSTs in the same offset are sorted by source order.
1451 fields.sort_by_key(|field| (field.offset, field.index));
Lukasz Anforowicz672c5152023-03-15 16:23:00 -07001452 let next_offsets = fields
1453 .iter()
1454 .map(|Field { offset, .. }| *offset)
1455 .skip(1)
1456 .chain(once(core.size_in_bytes))
1457 .collect_vec();
1458 for (field, next_offset) in fields.iter_mut().zip(next_offsets) {
1459 field.offset_of_next_field = next_offset;
1460 }
1461 fields
1462 }
1463 unexpected => panic!("Unexpected FieldsShape: {unexpected:?}"),
1464 }
Lukasz Anforowicz2eb24292023-03-14 09:38:21 -07001465 };
1466
Lukasz Anforowiczb7145e62023-03-23 09:00:37 -07001467 let cc_details = if fields.is_empty() {
1468 CcSnippet::default()
1469 } else {
Lukasz Anforowiczce56a802023-04-04 12:18:19 -07001470 let adt_cc_name = &core.cc_short_name;
Lukasz Anforowiczb7145e62023-03-23 09:00:37 -07001471 let cc_assertions: TokenStream = fields
1472 .iter()
1473 .map(|Field { cc_name, offset, .. }| {
1474 let offset = Literal::u64_unsuffixed(*offset);
1475 quote! { static_assert(#offset == offsetof(#adt_cc_name, #cc_name)); }
1476 })
1477 .collect();
Devin Jeanpierreb9b05fc2023-05-15 12:23:27 -07001478 CcSnippet::with_include(
1479 quote! {
1480 inline void #adt_cc_name::__crubit_field_offset_assertions() {
1481 #cc_assertions
1482 }
1483 },
1484 CcInclude::cstddef(),
1485 )
Lukasz Anforowiczb7145e62023-03-23 09:00:37 -07001486 };
1487 let rs_details: TokenStream = {
Lukasz Anforowiczce56a802023-04-04 12:18:19 -07001488 let adt_rs_name = &core.rs_fully_qualified_name;
Lukasz Anforowiczb7145e62023-03-23 09:00:37 -07001489 fields
1490 .iter()
1491 .filter(|Field { is_public, .. }| *is_public)
1492 .map(|Field { rs_name, offset, .. }| {
1493 let expected_offset = Literal::u64_unsuffixed(*offset);
1494 let actual_offset = quote! { memoffset::offset_of!(#adt_rs_name, #rs_name) };
1495 quote! { const _: () = assert!(#actual_offset == #expected_offset); }
1496 })
1497 .collect()
1498 };
Lukasz Anforowicz0b8a02f2023-02-10 15:55:40 -08001499 let main_api = {
Lukasz Anforowiczb7145e62023-03-23 09:00:37 -07001500 let assertions_method_decl = if fields.is_empty() {
1501 quote! {}
1502 } else {
1503 // We put the assertions in a method so that they can read private member
1504 // variables.
Lukasz Anforowiczb9e515a2023-07-05 12:48:22 -07001505 quote! { private: static void __crubit_field_offset_assertions(); }
Lukasz Anforowicz2eb24292023-03-14 09:38:21 -07001506 };
1507
Lukasz Anforowicz14229762023-02-10 15:28:33 -08001508 let mut prereqs = CcPrerequisites::default();
Lukasz Anforowicz672c5152023-03-15 16:23:00 -07001509 let fields: TokenStream = fields
1510 .into_iter()
1511 .map(|field| {
1512 let cc_name = field.cc_name;
1513 match field.type_info {
1514 Err(err) => {
Devin Jeanpierre9e15d0b2023-04-06 13:18:22 -07001515 let size = field.offset_of_next_field - field.offset;
Lukasz Anforowicz672c5152023-03-15 16:23:00 -07001516 let msg =
1517 format!("Field type has been replaced with a blob of bytes: {err:#}");
Devin Jeanpierre9e15d0b2023-04-06 13:18:22 -07001518
1519 // Empty arrays are ill-formed, but also unnecessary for padding.
1520 if size > 0 {
1521 let size = Literal::u64_unsuffixed(size);
1522 quote! {
Lukasz Anforowicz52bc49d2023-06-09 03:12:57 -07001523 private: __NEWLINE__
Lukasz Anforowicz68c88822023-06-01 16:06:15 -07001524 __COMMENT__ #msg
1525 unsigned char #cc_name[#size];
Devin Jeanpierre9e15d0b2023-04-06 13:18:22 -07001526 }
1527 } else {
1528 // TODO(b/258259459): finalize the approach here.
1529 // Possibly we should, rather than using no_unique_address, drop the
1530 // field entirely. This also requires removing the field's assertions,
1531 // added above.
1532 quote! {
Lukasz Anforowicz52bc49d2023-06-09 03:12:57 -07001533 private: __NEWLINE__
Lukasz Anforowicz68c88822023-06-01 16:06:15 -07001534 __COMMENT__ #msg
1535 [[no_unique_address]] struct{} #cc_name;
Devin Jeanpierre9e15d0b2023-04-06 13:18:22 -07001536 }
Lukasz Anforowicz672c5152023-03-15 16:23:00 -07001537 }
1538 }
1539 Ok(FieldTypeInfo { cc_type, size }) => {
1540 let padding = field.offset_of_next_field - field.offset - size;
1541 let padding = if padding == 0 {
1542 quote! {}
1543 } else {
1544 let padding = Literal::u64_unsuffixed(padding);
1545 let ident = format_ident!("__padding{}", field.index);
Lukasz Anforowicz68c88822023-06-01 16:06:15 -07001546 quote! { private: unsigned char #ident[#padding]; }
1547 };
1548 let visibility = if field.is_public {
1549 quote! { public: }
1550 } else {
1551 quote! { private: }
Lukasz Anforowicz672c5152023-03-15 16:23:00 -07001552 };
1553 let cc_type = cc_type.into_tokens(&mut prereqs);
Lukasz Anforowiczf4b37602023-06-01 16:16:34 -07001554 let doc_comment = field.doc_comment;
Lukasz Anforowicz68c88822023-06-01 16:06:15 -07001555 quote! {
Lukasz Anforowicz52bc49d2023-06-09 03:12:57 -07001556 #visibility __NEWLINE__
Lukasz Anforowicz82502222023-06-23 09:27:14 -07001557 // The anonymous union gives more control over when exactly
1558 // the field constructors and destructors run. See also
1559 // b/288138612.
1560 union { __NEWLINE__
1561 #doc_comment
1562 #cc_type #cc_name;
1563 };
Lukasz Anforowicz68c88822023-06-01 16:06:15 -07001564 #padding
1565 }
Lukasz Anforowicz672c5152023-03-15 16:23:00 -07001566 }
Lukasz Anforowicz2eb24292023-03-14 09:38:21 -07001567 }
Lukasz Anforowicz672c5152023-03-15 16:23:00 -07001568 })
1569 .collect();
Lukasz Anforowiczb7145e62023-03-23 09:00:37 -07001570
1571 CcSnippet {
1572 prereqs,
1573 tokens: quote! {
Lukasz Anforowicz68c88822023-06-01 16:06:15 -07001574 #fields
1575 #assertions_method_decl
Lukasz Anforowiczb7145e62023-03-23 09:00:37 -07001576 },
1577 }
1578 };
1579
1580 ApiSnippets { main_api, cc_details, rs_details }
1581}
1582
Lukasz Anforowicz15a60022023-07-07 09:05:49 -07001583fn does_type_implement_trait<'tcx>(tcx: TyCtxt<'tcx>, self_ty: Ty<'tcx>, trait_id: DefId) -> bool {
Lukasz Anforowicz92b66052023-07-06 09:26:54 -07001584 assert!(tcx.is_trait(trait_id));
1585
1586 let generics = tcx.generics_of(trait_id);
1587 assert!(generics.has_self);
1588 assert_eq!(
1589 generics.count(),
1590 1, // Only `Self`
1591 "Generic traits are not supported yet (b/286941486)",
1592 );
Lukasz Anforowicz92b66052023-07-06 09:26:54 -07001593 let substs = [self_ty];
1594
1595 tcx.infer_ctxt()
1596 .build()
1597 .type_implements_trait(trait_id, substs, tcx.param_env(trait_id))
1598 .must_apply_modulo_regions()
1599}
1600
Lukasz Anforowicz36de5722023-06-16 08:17:44 -07001601struct TraitThunks {
1602 method_name_to_cc_thunk_name: HashMap<Symbol, TokenStream>,
1603 cc_thunk_decls: CcSnippet,
1604 rs_thunk_impls: TokenStream,
1605}
1606
Lukasz Anforowicz15a60022023-07-07 09:05:49 -07001607fn format_trait_thunks<'tcx>(
1608 input: &Input<'tcx>,
Lukasz Anforowicz36de5722023-06-16 08:17:44 -07001609 trait_id: DefId,
Lukasz Anforowicz15a60022023-07-07 09:05:49 -07001610 adt: &AdtCoreBindings<'tcx>,
Lukasz Anforowicz36de5722023-06-16 08:17:44 -07001611) -> Result<TraitThunks> {
1612 let tcx = input.tcx;
Lukasz Anforowicz92b66052023-07-06 09:26:54 -07001613 assert!(tcx.is_trait(trait_id));
1614
Lukasz Anforowicz15a60022023-07-07 09:05:49 -07001615 let self_ty = adt.self_ty;
1616 if !does_type_implement_trait(tcx, self_ty, trait_id) {
Lukasz Anforowicz36de5722023-06-16 08:17:44 -07001617 let trait_name = tcx.item_name(trait_id);
1618 bail!("`{self_ty}` doesn't implement the `{trait_name}` trait");
Lukasz Anforowicz9924c432023-04-04 12:51:22 -07001619 }
Lukasz Anforowicz36de5722023-06-16 08:17:44 -07001620
1621 let mut method_name_to_cc_thunk_name = HashMap::new();
1622 let mut cc_thunk_decls = CcSnippet::default();
1623 let mut rs_thunk_impls = quote! {};
1624 let methods = tcx
1625 .associated_items(trait_id)
1626 .in_definition_order()
1627 .filter(|item| item.kind == ty::AssocKind::Fn);
1628 for method in methods {
1629 let substs = {
1630 let generics = tcx.generics_of(method.def_id);
1631 if generics.params.iter().any(|p| p.kind.is_ty_or_const()) {
1632 // Note that lifetime-generic methods are ok:
1633 // * they are handled by `format_thunk_decl` and `format_thunk_impl`
1634 // * the lifetimes are erased by `ty::Instance::mono` and *seem* to be erased by
1635 // `ty::Instance::new`
1636 panic!(
1637 "So far callers of `format_trait_thunks` didn't need traits with \
1638 methods that are type-generic or const-generic"
1639 );
1640 }
1641 assert!(generics.has_self);
1642 tcx.mk_substs_trait(self_ty, std::iter::empty())
1643 };
1644
1645 let thunk_name = {
1646 let instance = ty::Instance::new(method.def_id, substs);
1647 let symbol = tcx.symbol_name(instance);
1648 format!("__crubit_thunk_{}", &escape_non_identifier_chars(symbol.name))
1649 };
1650 method_name_to_cc_thunk_name.insert(method.name, format_cc_ident(&thunk_name)?);
1651
1652 let sig = tcx.fn_sig(method.def_id).subst(tcx, substs);
1653 let sig = liberate_and_deanonymize_late_bound_regions(tcx, sig, method.def_id);
1654
1655 cc_thunk_decls.add_assign({
1656 let thunk_name = format_cc_ident(&thunk_name)?;
1657 format_thunk_decl(input, method.def_id, &sig, &thunk_name)?
1658 });
1659
1660 rs_thunk_impls.extend({
1661 let fully_qualified_fn_name = {
1662 let struct_name = &adt.rs_fully_qualified_name;
1663 let fully_qualified_trait_name =
1664 FullyQualifiedName::new(tcx, trait_id).format_for_rs();
1665 let method_name = make_rs_ident(method.name.as_str());
1666 quote! { <#struct_name as #fully_qualified_trait_name>::#method_name }
1667 };
1668 format_thunk_impl(tcx, method.def_id, &sig, &thunk_name, fully_qualified_fn_name)?
1669 });
Lukasz Anforowicz9924c432023-04-04 12:51:22 -07001670 }
Lukasz Anforowicz36de5722023-06-16 08:17:44 -07001671
1672 Ok(TraitThunks { method_name_to_cc_thunk_name, cc_thunk_decls, rs_thunk_impls })
Lukasz Anforowicz9924c432023-04-04 12:51:22 -07001673}
1674
Lukasz Anforowicz10c52f22023-07-06 09:34:13 -07001675/// Gets the `DefId` for the `Default` trait.
1676fn get_def_id_of_default_trait(tcx: TyCtxt) -> DefId {
1677 tcx.get_diagnostic_item(sym::Default).expect("`Default` trait should always be present")
1678}
1679
Lukasz Anforowicz9924c432023-04-04 12:51:22 -07001680/// Formats a default constructor for an ADT if possible (i.e. if the `Default`
1681/// trait is implemented for the ADT). Returns an error otherwise (e.g. if
1682/// there is no `Default` impl).
Lukasz Anforowicz15a60022023-07-07 09:05:49 -07001683fn format_default_ctor<'tcx>(
1684 input: &Input<'tcx>,
1685 core: &AdtCoreBindings<'tcx>,
1686) -> Result<ApiSnippets> {
Lukasz Anforowicz9924c432023-04-04 12:51:22 -07001687 let tcx = input.tcx;
Lukasz Anforowicz10c52f22023-07-06 09:34:13 -07001688 let trait_id = get_def_id_of_default_trait(tcx);
Lukasz Anforowicz36de5722023-06-16 08:17:44 -07001689 let TraitThunks { method_name_to_cc_thunk_name, cc_thunk_decls, rs_thunk_impls: rs_details } =
1690 format_trait_thunks(input, trait_id, core)?;
Lukasz Anforowicz9924c432023-04-04 12:51:22 -07001691
Lukasz Anforowicz9924c432023-04-04 12:51:22 -07001692 let cc_struct_name = &core.cc_short_name;
1693 let main_api = CcSnippet::new(quote! {
1694 __NEWLINE__ __COMMENT__ "Default::default"
Lukasz Anforowiczb9e515a2023-07-05 12:48:22 -07001695 #cc_struct_name(); __NEWLINE__ __NEWLINE__
Lukasz Anforowicz9924c432023-04-04 12:51:22 -07001696 });
Lukasz Anforowicz9924c432023-04-04 12:51:22 -07001697 let cc_details = {
Lukasz Anforowicz36de5722023-06-16 08:17:44 -07001698 let thunk_name = method_name_to_cc_thunk_name
1699 .into_values()
1700 .exactly_one()
1701 .expect("Expecting a single `default` method");
1702
1703 let mut prereqs = CcPrerequisites::default();
1704 let cc_thunk_decls = cc_thunk_decls.into_tokens(&mut prereqs);
1705
Lukasz Anforowicz9924c432023-04-04 12:51:22 -07001706 let tokens = quote! {
Lukasz Anforowicz36de5722023-06-16 08:17:44 -07001707 #cc_thunk_decls
Lukasz Anforowiczb9e515a2023-07-05 12:48:22 -07001708 inline #cc_struct_name::#cc_struct_name() {
Lukasz Anforowicz9924c432023-04-04 12:51:22 -07001709 __crubit_internal::#thunk_name(this);
1710 }
1711 };
1712 CcSnippet { tokens, prereqs }
1713 };
Lukasz Anforowicz9924c432023-04-04 12:51:22 -07001714 Ok(ApiSnippets { main_api, cc_details, rs_details })
1715}
1716
Lukasz Anforowicz901161a2023-06-01 15:39:58 -07001717/// Formats the copy constructor and the copy-assignment operator for an ADT if
1718/// possible (i.e. if the `Clone` trait is implemented for the ADT). Returns an
1719/// error otherwise (e.g. if there is no `Clone` impl).
Lukasz Anforowicz15a60022023-07-07 09:05:49 -07001720fn format_copy_ctor_and_assignment_operator<'tcx>(
1721 input: &Input<'tcx>,
1722 core: &AdtCoreBindings<'tcx>,
Lukasz Anforowicz901161a2023-06-01 15:39:58 -07001723) -> Result<ApiSnippets> {
1724 let tcx = input.tcx;
Lukasz Anforowicz1b665a42023-06-20 13:04:54 -07001725 let cc_struct_name = &core.cc_short_name;
Lukasz Anforowicz901161a2023-06-01 15:39:58 -07001726
1727 let is_copy = {
Lukasz Anforowicz901161a2023-06-01 15:39:58 -07001728 // TODO(b/259749095): Once generic ADTs are supported, `is_copy_modulo_regions`
1729 // might need to be replaced with a more thorough check - see
1730 // b/258249993#comment4.
Lukasz Anforowicz15a60022023-07-07 09:05:49 -07001731 core.self_ty.is_copy_modulo_regions(tcx, tcx.param_env(core.def_id))
Lukasz Anforowicz901161a2023-06-01 15:39:58 -07001732 };
Lukasz Anforowicz901161a2023-06-01 15:39:58 -07001733 if is_copy {
Lukasz Anforowicz901161a2023-06-01 15:39:58 -07001734 let msg = "Rust types that are `Copy` get trivial, `default` C++ copy constructor \
1735 and assignment operator.";
1736 let main_api = CcSnippet::new(quote! {
1737 __NEWLINE__ __COMMENT__ #msg
1738 #cc_struct_name(const #cc_struct_name&) = default; __NEWLINE__
1739 #cc_struct_name& operator=(const #cc_struct_name&) = default;
1740 });
Lukasz Anforowiczf18a8572023-06-06 08:42:14 -07001741 let cc_details = CcSnippet::with_include(
1742 quote! {
1743 static_assert(std::is_trivially_copy_constructible_v<#cc_struct_name>);
1744 static_assert(std::is_trivially_copy_assignable_v<#cc_struct_name>);
1745 },
1746 CcInclude::type_traits(),
1747 );
1748
1749 return Ok(ApiSnippets { main_api, cc_details, rs_details: quote! {} });
Lukasz Anforowicz901161a2023-06-01 15:39:58 -07001750 }
1751
Lukasz Anforowicz36de5722023-06-16 08:17:44 -07001752 let trait_id =
1753 tcx.lang_items().clone_trait().ok_or_else(|| anyhow!("Can't find the `Clone` trait"))?;
Lukasz Anforowicz1b665a42023-06-20 13:04:54 -07001754 let TraitThunks { method_name_to_cc_thunk_name, cc_thunk_decls, rs_thunk_impls: rs_details } =
1755 format_trait_thunks(input, trait_id, core)?;
1756 let main_api = CcSnippet::new(quote! {
1757 __NEWLINE__ __COMMENT__ "Clone::clone"
Lukasz Anforowiczb9e515a2023-07-05 12:48:22 -07001758 #cc_struct_name(const #cc_struct_name&); __NEWLINE__
Lukasz Anforowicz1b665a42023-06-20 13:04:54 -07001759 __NEWLINE__ __COMMENT__ "Clone::clone_from"
Lukasz Anforowiczb9e515a2023-07-05 12:48:22 -07001760 #cc_struct_name& operator=(const #cc_struct_name&); __NEWLINE__ __NEWLINE__
Lukasz Anforowicz1b665a42023-06-20 13:04:54 -07001761 });
1762 let cc_details = {
1763 // `unwrap` calls are okay because `Clone` trait always has these methods.
1764 let clone_thunk_name = method_name_to_cc_thunk_name.get(&sym::clone).unwrap();
1765 let clone_from_thunk_name = method_name_to_cc_thunk_name.get(&sym::clone_from).unwrap();
1766
1767 let mut prereqs = CcPrerequisites::default();
1768 let cc_thunk_decls = cc_thunk_decls.into_tokens(&mut prereqs);
1769
1770 let tokens = quote! {
1771 #cc_thunk_decls
Lukasz Anforowiczb9e515a2023-07-05 12:48:22 -07001772 inline #cc_struct_name::#cc_struct_name(const #cc_struct_name& other) {
Lukasz Anforowicz1b665a42023-06-20 13:04:54 -07001773 __crubit_internal::#clone_thunk_name(other, this);
1774 }
Lukasz Anforowiczb9e515a2023-07-05 12:48:22 -07001775 inline #cc_struct_name& #cc_struct_name::operator=(const #cc_struct_name& other) {
Lukasz Anforowicz1b665a42023-06-20 13:04:54 -07001776 if (this != &other) {
1777 __crubit_internal::#clone_from_thunk_name(*this, other);
1778 }
1779 return *this;
1780 }
1781 static_assert(std::is_copy_constructible_v<#cc_struct_name>);
1782 static_assert(std::is_copy_assignable_v<#cc_struct_name>);
1783 };
1784 CcSnippet { tokens, prereqs }
1785 };
1786 Ok(ApiSnippets { main_api, cc_details, rs_details })
Lukasz Anforowicz901161a2023-06-01 15:39:58 -07001787}
1788
Lukasz Anforowiczb7145e62023-03-23 09:00:37 -07001789/// Formats an algebraic data type (an ADT - a struct, an enum, or a union)
1790/// represented by `core`. This function is infallible - after
1791/// `format_adt_core` returns success we have committed to emitting C++ bindings
1792/// for the ADT.
Lukasz Anforowicz15a60022023-07-07 09:05:49 -07001793fn format_adt<'tcx>(input: &Input<'tcx>, core: &AdtCoreBindings<'tcx>) -> ApiSnippets {
Lukasz Anforowiczb7145e62023-03-23 09:00:37 -07001794 let tcx = input.tcx;
Lukasz Anforowicz9924c432023-04-04 12:51:22 -07001795 let adt_cc_name = &core.cc_short_name;
Lukasz Anforowiczb7145e62023-03-23 09:00:37 -07001796
1797 // `format_adt` should only be called for local ADTs.
1798 let local_def_id = core.def_id.expect_local();
1799
Lukasz Anforowiczbcb40002023-07-07 08:39:19 -07001800 let default_ctor_snippets = format_default_ctor(input, core).unwrap_or_else(|err| {
Lukasz Anforowicz9924c432023-04-04 12:51:22 -07001801 let msg = format!("{err:#}");
1802 ApiSnippets {
1803 main_api: CcSnippet::new(quote! {
1804 __NEWLINE__ __COMMENT__ #msg
1805 #adt_cc_name() = delete; __NEWLINE__
1806 }),
1807 ..Default::default()
1808 }
1809 });
1810
Lukasz Anforowiczbcb40002023-07-07 08:39:19 -07001811 let destructor_and_move_snippets = {
Lukasz Anforowicz15a60022023-07-07 09:05:49 -07001812 assert!(!core.self_ty.needs_drop(tcx, tcx.param_env(core.def_id)));
Lukasz Anforowicz57c70142023-07-07 08:35:04 -07001813 ApiSnippets {
1814 main_api: CcSnippet::new(quote! {
1815 __NEWLINE__ __COMMENT__ "No custom `Drop` impl and no custom \"drop glue\" required"
1816 ~#adt_cc_name() = default; __NEWLINE__
1817 // The generated bindings have to follow Rust move semantics:
1818 // * All Rust types are memcpy-movable (e.g. <internal link>/constructors.html says
1819 // that "Every type must be ready for it to be blindly memcopied to somewhere
1820 // else in memory")
1821 // * The only valid operation on a moved-from non-`Copy` Rust struct is to assign to
1822 // it.
1823 //
1824 // The generated C++ bindings below match the required semantics because they:
1825 // * Generate trivial` C++ move constructor and move assignment operator. Per
1826 // <internal link>/cpp/language/move_constructor#Trivial_move_constructor: "A trivial
1827 // move constructor is a constructor that performs the same action as the trivial
1828 // copy constructor, that is, makes a copy of the object representation as if by
1829 // std::memmove."
1830 // * Generate trivial C++ destructor.
1831 //
1832 // In particular, note that the following C++ code and Rust code are exactly
1833 // equivalent (except that in Rust, reuse of `y` is forbidden at compile time,
1834 // whereas in C++, it's only prohibited by convention):
1835 // * C++, assumming trivial move constructor and trivial destructor:
1836 // `auto x = std::move(y);`
1837 // * Rust, assumming non-`Copy`, no custom `Drop` or drop glue:
1838 // `let x = y;`
1839 //
1840 // TODO(b/258251148): If the ADT provides a custom `Drop` impls or requires drop
1841 // glue, then extra care should be taken to ensure the C++ destructor can handle
1842 // the moved-from object in a way that meets Rust move semantics. For example, the
1843 // generated C++ move constructor might need to assign `Default::default()` to the
1844 // moved-from object.
1845 #adt_cc_name(#adt_cc_name&&) = default; __NEWLINE__
1846 #adt_cc_name& operator=(#adt_cc_name&&) = default; __NEWLINE__
1847 __NEWLINE__
1848 }),
1849 ..Default::default()
1850 }
1851 };
1852
Lukasz Anforowiczbcb40002023-07-07 08:39:19 -07001853 let copy_ctor_and_assignment_snippets = format_copy_ctor_and_assignment_operator(input, core)
1854 .unwrap_or_else(|err| {
1855 let msg = format!("{err:#}");
1856 ApiSnippets {
1857 main_api: CcSnippet::new(quote! {
1858 __NEWLINE__ __COMMENT__ #msg
1859 #adt_cc_name(const #adt_cc_name&) = delete; __NEWLINE__
1860 #adt_cc_name& operator=(const #adt_cc_name&) = delete;
1861 }),
1862 ..Default::default()
1863 }
1864 });
Lukasz Anforowicz901161a2023-06-01 15:39:58 -07001865
Lukasz Anforowiczbcb40002023-07-07 08:39:19 -07001866 let impl_items_snippets = tcx
Lukasz Anforowiczb7145e62023-03-23 09:00:37 -07001867 .inherent_impls(core.def_id)
1868 .iter()
1869 .map(|impl_id| tcx.hir().expect_item(impl_id.expect_local()))
1870 .flat_map(|item| match &item.kind {
1871 ItemKind::Impl(impl_) => impl_.items,
1872 other => panic!("Unexpected `ItemKind` from `inherent_impls`: {other:?}"),
1873 })
1874 .sorted_by_key(|impl_item_ref| {
1875 let def_id = impl_item_ref.id.owner_id.def_id;
1876 tcx.def_span(def_id)
1877 })
1878 .filter_map(|impl_item_ref| {
1879 let def_id = impl_item_ref.id.owner_id.def_id;
1880 if !tcx.effective_visibilities(()).is_directly_public(def_id) {
1881 return None;
1882 }
1883 let result = match impl_item_ref.kind {
1884 AssocItemKind::Fn { .. } => format_fn(input, def_id).map(Some),
1885 other => Err(anyhow!("Unsupported `impl` item kind: {other:?}")),
1886 };
1887 result.unwrap_or_else(|err| Some(format_unsupported_def(tcx, def_id, err)))
1888 })
1889 .collect();
1890
Lukasz Anforowiczbcb40002023-07-07 08:39:19 -07001891 let ApiSnippets {
1892 main_api: public_functions_main_api,
1893 cc_details: public_functions_cc_details,
1894 rs_details: public_functions_rs_details,
1895 } = [
1896 default_ctor_snippets,
1897 destructor_and_move_snippets,
1898 copy_ctor_and_assignment_snippets,
1899 impl_items_snippets,
1900 ]
1901 .into_iter()
1902 .collect();
1903
1904 let ApiSnippets {
1905 main_api: fields_main_api,
1906 cc_details: fields_cc_details,
1907 rs_details: fields_rs_details,
1908 } = format_fields(input, core);
1909
Lukasz Anforowiczb7145e62023-03-23 09:00:37 -07001910 let alignment = Literal::u64_unsuffixed(core.alignment_in_bytes);
1911 let size = Literal::u64_unsuffixed(core.size_in_bytes);
Lukasz Anforowiczb7145e62023-03-23 09:00:37 -07001912 let main_api = {
Devin Jeanpierre64ac8ad2023-05-30 17:22:55 -07001913 let rs_type = core.rs_fully_qualified_name.to_string();
1914 let mut attributes =
1915 vec![quote! {CRUBIT_INTERNAL_RUST_TYPE(#rs_type)}, quote! {alignas(#alignment)}];
1916 if tcx
1917 .get_attrs(core.def_id, rustc_span::symbol::sym::repr)
1918 .flat_map(|attr| rustc_attr::parse_repr_attr(tcx.sess(), attr))
1919 .any(|repr| matches!(repr, rustc_attr::ReprPacked { .. }))
1920 {
1921 attributes.push(quote! { __attribute__((packed)) })
1922 }
Lukasz Anforowiczb7145e62023-03-23 09:00:37 -07001923
1924 let doc_comment = format_doc_comment(tcx, core.def_id.expect_local());
1925 let keyword = &core.keyword;
Lukasz Anforowiczb7145e62023-03-23 09:00:37 -07001926
1927 let mut prereqs = CcPrerequisites::default();
Devin Jeanpierre64ac8ad2023-05-30 17:22:55 -07001928 prereqs.includes.insert(input.support_header("internal/attribute_macros.h"));
Lukasz Anforowiczbcb40002023-07-07 08:39:19 -07001929 let public_functions_main_api = public_functions_main_api.into_tokens(&mut prereqs);
Lukasz Anforowiczb7145e62023-03-23 09:00:37 -07001930 let fields_main_api = fields_main_api.into_tokens(&mut prereqs);
Lukasz Anforowicz64b04ba2023-02-10 17:19:05 -08001931 prereqs.fwd_decls.remove(&local_def_id);
Lukasz Anforowicz14229762023-02-10 15:28:33 -08001932
1933 CcSnippet {
1934 prereqs,
1935 tokens: quote! {
1936 __NEWLINE__ #doc_comment
Devin Jeanpierre64ac8ad2023-05-30 17:22:55 -07001937 #keyword #(#attributes)* #adt_cc_name final {
Lukasz Anforowicz52bc49d2023-06-09 03:12:57 -07001938 public: __NEWLINE__
Lukasz Anforowiczbcb40002023-07-07 08:39:19 -07001939 #public_functions_main_api
Lukasz Anforowiczb7145e62023-03-23 09:00:37 -07001940 #fields_main_api
Lukasz Anforowicz14229762023-02-10 15:28:33 -08001941 };
Lukasz Anforowicza0502fb2023-02-13 15:33:18 -08001942 __NEWLINE__
Lukasz Anforowicz14229762023-02-10 15:28:33 -08001943 },
1944 }
Lukasz Anforowicz3b579542023-02-06 11:23:49 -08001945 };
Lukasz Anforowicz73a2c0d2023-03-22 14:07:00 -07001946 let cc_details = {
Lukasz Anforowicz73a2c0d2023-03-22 14:07:00 -07001947 let mut prereqs = CcPrerequisites::default();
Lukasz Anforowiczbcb40002023-07-07 08:39:19 -07001948 let public_functions_cc_details = public_functions_cc_details.into_tokens(&mut prereqs);
Lukasz Anforowiczb7145e62023-03-23 09:00:37 -07001949 let fields_cc_details = fields_cc_details.into_tokens(&mut prereqs);
Lukasz Anforowicz73a2c0d2023-03-22 14:07:00 -07001950 prereqs.defs.insert(local_def_id);
Lukasz Anforowicze0a778c2023-06-06 09:00:32 -07001951 prereqs.includes.insert(CcInclude::type_traits());
Lukasz Anforowicz73a2c0d2023-03-22 14:07:00 -07001952 CcSnippet {
1953 prereqs,
1954 tokens: quote! {
Lukasz Anforowicze3eb1cf2023-03-14 09:56:00 -07001955 __NEWLINE__
1956 static_assert(
1957 sizeof(#adt_cc_name) == #size,
1958 "Verify that struct layout didn't change since this header got generated");
1959 static_assert(
1960 alignof(#adt_cc_name) == #alignment,
1961 "Verify that struct layout didn't change since this header got generated");
Lukasz Anforowicze0a778c2023-06-06 09:00:32 -07001962 static_assert(std::is_trivially_move_constructible_v<#adt_cc_name>);
1963 static_assert(std::is_trivially_move_assignable_v<#adt_cc_name>);
Lukasz Anforowicze3eb1cf2023-03-14 09:56:00 -07001964 __NEWLINE__
Lukasz Anforowiczbcb40002023-07-07 08:39:19 -07001965 #public_functions_cc_details
Lukasz Anforowiczb7145e62023-03-23 09:00:37 -07001966 #fields_cc_details
Lukasz Anforowicz73a2c0d2023-03-22 14:07:00 -07001967 },
1968 }
Lukasz Anforowicz3b579542023-02-06 11:23:49 -08001969 };
Lukasz Anforowiczb7145e62023-03-23 09:00:37 -07001970 let rs_details = {
Lukasz Anforowiczce56a802023-04-04 12:18:19 -07001971 let adt_rs_name = &core.rs_fully_qualified_name;
Lukasz Anforowiczb7145e62023-03-23 09:00:37 -07001972 quote! {
1973 const _: () = assert!(::std::mem::size_of::<#adt_rs_name>() == #size);
1974 const _: () = assert!(::std::mem::align_of::<#adt_rs_name>() == #alignment);
Lukasz Anforowiczbcb40002023-07-07 08:39:19 -07001975 #public_functions_rs_details
Lukasz Anforowiczb7145e62023-03-23 09:00:37 -07001976 #fields_rs_details
1977 }
Lukasz Anforowicz73a2c0d2023-03-22 14:07:00 -07001978 };
1979 ApiSnippets { main_api, cc_details, rs_details }
Lukasz Anforowicz75894832022-11-22 18:25:28 -08001980}
1981
Lukasz Anforowiczf4501b42023-01-07 09:41:29 -08001982/// Formats the forward declaration of an algebraic data type (an ADT - a
1983/// struct, an enum, or a union), returning something like
1984/// `quote!{ struct SomeStruct; }`.
1985///
1986/// Will panic if `def_id` doesn't identify an ADT that can be successfully
1987/// handled by `format_adt_core`.
1988fn format_fwd_decl(tcx: TyCtxt, def_id: LocalDefId) -> TokenStream {
1989 let def_id = def_id.to_def_id(); // LocalDefId -> DefId conversion.
1990
1991 // `format_fwd_decl` should only be called for items from
1992 // `CcPrerequisites::fwd_decls` and `fwd_decls` should only contain ADTs
1993 // that `format_adt_core` succeeds for.
Lukasz Anforowiczce56a802023-04-04 12:18:19 -07001994 let AdtCoreBindings { keyword, cc_short_name, .. } = format_adt_core(tcx, def_id)
Lukasz Anforowiczf4501b42023-01-07 09:41:29 -08001995 .expect("`format_fwd_decl` should only be called if `format_adt_core` succeeded");
1996
Lukasz Anforowiczce56a802023-04-04 12:18:19 -07001997 quote! { #keyword #cc_short_name; }
Lukasz Anforowiczf4501b42023-01-07 09:41:29 -08001998}
1999
Googler47fd9572023-01-20 09:57:32 -08002000fn format_source_location(tcx: TyCtxt, local_def_id: LocalDefId) -> String {
2001 let def_span = tcx.def_span(local_def_id);
2002 let rustc_span::FileLines { file, lines } =
2003 match tcx.sess().source_map().span_to_lines(def_span) {
2004 Ok(filelines) => filelines,
2005 Err(_) => return "unknown location".to_string(),
2006 };
2007 let file_name = file.name.prefer_local().to_string();
2008 // Note: line_index starts at 0, while CodeSearch starts indexing at 1.
2009 let line_number = lines[0].line_index + 1;
2010 let google3_prefix = {
2011 // If rustc_span::FileName isn't a 'real' file, then it's surrounded by by angle
Lukasz Anforowicz1f233912023-02-14 08:50:26 -08002012 // brackets, thus don't prepend "google3/" prefix.
2013 if file.name.is_real() { "google3/" } else { "" }
Googler47fd9572023-01-20 09:57:32 -08002014 };
Googler785e6b42023-01-23 12:11:36 -08002015 format!("{google3_prefix}{file_name};l={line_number}")
Googler47fd9572023-01-20 09:57:32 -08002016}
2017
2018/// Formats the doc comment (if any) associated with the item identified by
2019/// `local_def_id`, and appends the source location at which the item is
2020/// defined.
Googlerfb204272022-12-02 00:52:05 -08002021fn format_doc_comment(tcx: TyCtxt, local_def_id: LocalDefId) -> TokenStream {
2022 let hir_id = tcx.local_def_id_to_hir_id(local_def_id);
Googler47fd9572023-01-20 09:57:32 -08002023 let doc_comment = tcx
Googlerfb204272022-12-02 00:52:05 -08002024 .hir()
2025 .attrs(hir_id)
2026 .iter()
Lukasz Anforowiczdf363ee2022-12-16 14:56:38 -08002027 .filter_map(|attr| attr.doc_str())
Googler47fd9572023-01-20 09:57:32 -08002028 .map(|symbol| symbol.to_string())
Googler785e6b42023-01-23 12:11:36 -08002029 .chain(once(format!("Generated from: {}", format_source_location(tcx, local_def_id))))
Googler34f3d572022-12-02 00:53:37 -08002030 .join("\n\n");
Googler47fd9572023-01-20 09:57:32 -08002031 quote! { __COMMENT__ #doc_comment}
Googlerfb204272022-12-02 00:52:05 -08002032}
2033
Lukasz Anforowicze465b9e2023-02-06 15:00:25 -08002034/// Formats a HIR item idenfied by `def_id`. Returns `None` if the item
Lukasz Anforowicz14229762023-02-10 15:28:33 -08002035/// can be ignored. Returns an `Err` if the definition couldn't be formatted.
Lukasz Anforowicze4333062022-10-17 14:47:53 -07002036///
Lukasz Anforowicze465b9e2023-02-06 15:00:25 -08002037/// Will panic if `def_id` is invalid (i.e. doesn't identify a HIR item).
Lukasz Anforowicz73a2c0d2023-03-22 14:07:00 -07002038fn format_item(input: &Input, def_id: LocalDefId) -> Result<Option<ApiSnippets>> {
Lukasz Anforowicz1382f392022-12-12 17:13:23 -08002039 // TODO(b/262052635): When adding support for re-exports we may need to change
Devin Jeanpierre81aec502023-04-04 15:33:39 -07002040 // `is_directly_public` below into `is_exported`. (OTOH such change *alone* is
2041 // undesirable, because it would mean exposing items from a private module.
2042 // Exposing a private module is undesirable, because it would mean that
2043 // changes of private implementation details of the crate could become
2044 // breaking changes for users of the generated C++ bindings.)
Lukasz Anforowicz2eb24292023-03-14 09:38:21 -07002045 if !input.tcx.effective_visibilities(()).is_directly_public(def_id) {
Lukasz Anforowicz73a2c0d2023-03-22 14:07:00 -07002046 return Ok(None);
Lukasz Anforowicz8b68a552022-12-12 15:07:58 -08002047 }
2048
Lukasz Anforowicze465b9e2023-02-06 15:00:25 -08002049 match input.tcx.hir().expect_item(def_id) {
Lukasz Anforowicz56d94fc2023-02-17 12:23:20 -08002050 Item { kind: ItemKind::Struct(_, generics) |
Lukasz Anforowicz93513ec2023-02-10 14:21:20 -08002051 ItemKind::Enum(_, generics) |
2052 ItemKind::Union(_, generics),
2053 .. } if !generics.params.is_empty() => {
Lukasz Anforowicz56d94fc2023-02-17 12:23:20 -08002054 bail!("Generic types are not supported yet (b/259749095)");
Lukasz Anforowicz93513ec2023-02-10 14:21:20 -08002055 },
Lukasz Anforowicz73a2c0d2023-03-22 14:07:00 -07002056 Item { kind: ItemKind::Fn(..), .. } => format_fn(input, def_id).map(Some),
Lukasz Anforowicz93513ec2023-02-10 14:21:20 -08002057 Item { kind: ItemKind::Struct(..) | ItemKind::Enum(..) | ItemKind::Union(..), .. } =>
Lukasz Anforowicz2eb24292023-03-14 09:38:21 -07002058 format_adt_core(input.tcx, def_id.to_def_id())
Lukasz Anforowicz73a2c0d2023-03-22 14:07:00 -07002059 .map(|core| Some(format_adt(input, &core))),
Lukasz Anforowicz93513ec2023-02-10 14:21:20 -08002060 Item { kind: ItemKind::Impl(_), .. } | // Handled by `format_adt`
2061 Item { kind: ItemKind::Mod(_), .. } => // Handled by `format_crate`
Lukasz Anforowicz73a2c0d2023-03-22 14:07:00 -07002062 Ok(None),
Lukasz Anforowicze465b9e2023-02-06 15:00:25 -08002063 Item { kind, .. } => bail!("Unsupported rustc_hir::hir::ItemKind: {}", kind.descr()),
Lukasz Anforowicze4333062022-10-17 14:47:53 -07002064 }
2065}
2066
2067/// Formats a C++ comment explaining why no bindings have been generated for
2068/// `local_def_id`.
Lukasz Anforowiczed1c4f02022-09-29 11:11:20 -07002069fn format_unsupported_def(
2070 tcx: TyCtxt,
2071 local_def_id: LocalDefId,
Lukasz Anforowicze4333062022-10-17 14:47:53 -07002072 err: anyhow::Error,
Lukasz Anforowicz73a2c0d2023-03-22 14:07:00 -07002073) -> ApiSnippets {
Googler785e6b42023-01-23 12:11:36 -08002074 let source_loc = format_source_location(tcx, local_def_id);
Lukasz Anforowiczed1c4f02022-09-29 11:11:20 -07002075 let name = tcx.def_path_str(local_def_id.to_def_id());
Lukasz Anforowicze4333062022-10-17 14:47:53 -07002076
2077 // https://docs.rs/anyhow/latest/anyhow/struct.Error.html#display-representations
2078 // says: To print causes as well [...], use the alternate selector “{:#}”.
Googler785e6b42023-01-23 12:11:36 -08002079 let msg = format!("Error generating bindings for `{name}` defined at {source_loc}: {err:#}");
Lukasz Anforowicz73a2c0d2023-03-22 14:07:00 -07002080 let main_api = CcSnippet::new(quote! { __NEWLINE__ __NEWLINE__ __COMMENT__ #msg __NEWLINE__ });
Lukasz Anforowiczf04bc522022-11-04 09:19:51 -07002081
Lukasz Anforowicz73a2c0d2023-03-22 14:07:00 -07002082 ApiSnippets { main_api, cc_details: CcSnippet::default(), rs_details: quote! {} }
Lukasz Anforowiczed1c4f02022-09-29 11:11:20 -07002083}
2084
Lukasz Anforowicz2ec13122022-11-10 12:39:04 -08002085/// Formats all public items from the Rust crate being compiled.
Lukasz Anforowiczbe25c762023-01-17 12:43:52 -08002086fn format_crate(input: &Input) -> Result<Output> {
2087 let tcx = input.tcx;
Lukasz Anforowicz73a2c0d2023-03-22 14:07:00 -07002088 let mut cc_details_prereqs = CcPrerequisites::default();
2089 let mut cc_details: Vec<(LocalDefId, TokenStream)> = vec![];
2090 let mut rs_body = TokenStream::default();
Lukasz Anforowicz991b2a52023-03-23 07:28:41 -07002091 let mut main_apis = HashMap::<LocalDefId, CcSnippet>::new();
2092 let formatted_items = tcx
Lukasz Anforowicz2ec13122022-11-10 12:39:04 -08002093 .hir()
2094 .items()
Lukasz Anforowicz73a2c0d2023-03-22 14:07:00 -07002095 .filter_map(|item_id| {
Lukasz Anforowicz2ec13122022-11-10 12:39:04 -08002096 let def_id: LocalDefId = item_id.owner_id.def_id;
Lukasz Anforowicze465b9e2023-02-06 15:00:25 -08002097 format_item(input, def_id)
Lukasz Anforowicz73a2c0d2023-03-22 14:07:00 -07002098 .unwrap_or_else(|err| Some(format_unsupported_def(tcx, def_id, err)))
2099 .map(|api_snippets| (def_id, api_snippets))
Lukasz Anforowiczed17d052022-11-02 12:07:28 -07002100 })
Lukasz Anforowicz991b2a52023-03-23 07:28:41 -07002101 .sorted_by_key(|(def_id, _)| tcx.def_span(*def_id));
2102 for (def_id, api_snippets) in formatted_items {
2103 let old_item = main_apis.insert(def_id, api_snippets.main_api);
2104 assert!(old_item.is_none(), "Duplicated key: {def_id:?}");
Lukasz Anforowicz73a2c0d2023-03-22 14:07:00 -07002105
Lukasz Anforowicz991b2a52023-03-23 07:28:41 -07002106 // `cc_details` don't participate in the toposort, because
2107 // `CcPrerequisites::defs` always use `main_api` as the predecessor
2108 // - `chain`ing `cc_details` after `ordered_main_apis` trivially
2109 // meets the prerequisites.
2110 cc_details.push((def_id, api_snippets.cc_details.into_tokens(&mut cc_details_prereqs)));
2111 rs_body.extend(api_snippets.rs_details);
2112 }
Lukasz Anforowicz816cbaa2022-12-07 09:31:30 -08002113
Lukasz Anforowicz73a2c0d2023-03-22 14:07:00 -07002114 // Find the order of `main_apis` that 1) meets the requirements of
Lukasz Anforowicz816cbaa2022-12-07 09:31:30 -08002115 // `CcPrerequisites::defs` and 2) makes a best effort attempt to keep the
Lukasz Anforowicz73a2c0d2023-03-22 14:07:00 -07002116 // `main_apis` in the same order as the source order of the Rust APIs.
Lukasz Anforowicz2ecb27f2023-01-12 15:29:51 -08002117 let ordered_ids = {
2118 let toposort::TopoSortResult { ordered: ordered_ids, failed: failed_ids } = {
Lukasz Anforowicz73a2c0d2023-03-22 14:07:00 -07002119 let nodes = main_apis.keys().copied();
2120 let deps = main_apis.iter().flat_map(|(&successor, main_api)| {
Lukasz Anforowicze7212082023-07-05 13:50:10 -07002121 let predecessors = main_api.prereqs.defs.iter().copied();
Lukasz Anforowicz2ecb27f2023-01-12 15:29:51 -08002122 predecessors.map(move |predecessor| toposort::Dependency { predecessor, successor })
2123 });
Lukasz Anforowicz73a2c0d2023-03-22 14:07:00 -07002124 toposort::toposort(nodes, deps, move |lhs_id, rhs_id| {
2125 tcx.def_span(*lhs_id).cmp(&tcx.def_span(*rhs_id))
2126 })
Lukasz Anforowicz2ecb27f2023-01-12 15:29:51 -08002127 };
2128 assert_eq!(
2129 0,
2130 failed_ids.len(),
2131 "There are no known scenarios where CcPrerequisites::defs can form \
2132 a dependency cycle. These `LocalDefId`s form an unexpected cycle: {}",
2133 failed_ids.into_iter().map(|id| format!("{:?}", id)).join(",")
2134 );
2135 ordered_ids
Lukasz Anforowiczab563af2022-12-15 08:09:50 -08002136 };
Lukasz Anforowicz816cbaa2022-12-07 09:31:30 -08002137
Lukasz Anforowicz73a2c0d2023-03-22 14:07:00 -07002138 // Destructure/rebuild `main_apis` (in the same order as `ordered_ids`) into
2139 // `includes`, and `ordered_cc` (mixing in `fwd_decls` and `cc_details`).
2140 let (includes, ordered_cc) = {
Lukasz Anforowiczf4501b42023-01-07 09:41:29 -08002141 let mut already_declared = HashSet::new();
2142 let mut fwd_decls = HashSet::new();
Lukasz Anforowicz73a2c0d2023-03-22 14:07:00 -07002143 let mut includes = cc_details_prereqs.includes;
2144 let mut ordered_main_apis: Vec<(LocalDefId, TokenStream)> = Vec::new();
2145 for def_id in ordered_ids.into_iter() {
2146 let CcSnippet {
2147 tokens: cc_tokens,
2148 prereqs: CcPrerequisites {
2149 includes: mut inner_includes,
2150 fwd_decls: inner_fwd_decls,
2151 .. // `defs` have already been utilized by `toposort` above
Lukasz Anforowicz7f31f802022-12-16 08:24:13 -08002152 }
Lukasz Anforowicz73a2c0d2023-03-22 14:07:00 -07002153 } = main_apis.remove(&def_id).unwrap();
Lukasz Anforowiczf4501b42023-01-07 09:41:29 -08002154
2155 fwd_decls.extend(inner_fwd_decls.difference(&already_declared).copied());
Lukasz Anforowicz73a2c0d2023-03-22 14:07:00 -07002156 already_declared.insert(def_id);
Lukasz Anforowiczf4501b42023-01-07 09:41:29 -08002157 already_declared.extend(inner_fwd_decls.into_iter());
2158
2159 includes.append(&mut inner_includes);
Lukasz Anforowicz73a2c0d2023-03-22 14:07:00 -07002160 ordered_main_apis.push((def_id, cc_tokens));
Lukasz Anforowiczf4501b42023-01-07 09:41:29 -08002161 }
2162
Lukasz Anforowiczf4501b42023-01-07 09:41:29 -08002163 let fwd_decls = fwd_decls
2164 .into_iter()
2165 .sorted_by_key(|def_id| tcx.def_span(*def_id))
Lukasz Anforowicz5c1b3ad2023-04-13 17:05:00 -07002166 .map(|local_def_id| (local_def_id, format_fwd_decl(tcx, local_def_id)));
Lukasz Anforowicz73a2c0d2023-03-22 14:07:00 -07002167
2168 let ordered_cc: Vec<(NamespaceQualifier, TokenStream)> = fwd_decls
2169 .into_iter()
2170 .chain(ordered_main_apis.into_iter())
2171 .chain(cc_details.into_iter())
2172 .map(|(local_def_id, tokens)| {
Lukasz Anforowiczf4501b42023-01-07 09:41:29 -08002173 let mod_path = FullyQualifiedName::new(tcx, local_def_id.to_def_id()).mod_path;
Lukasz Anforowicz73a2c0d2023-03-22 14:07:00 -07002174 (mod_path, tokens)
Lukasz Anforowiczf4501b42023-01-07 09:41:29 -08002175 })
2176 .collect_vec();
Lukasz Anforowiczf4501b42023-01-07 09:41:29 -08002177
Lukasz Anforowicz73a2c0d2023-03-22 14:07:00 -07002178 (includes, ordered_cc)
Lukasz Anforowiczf4501b42023-01-07 09:41:29 -08002179 };
Lukasz Anforowicza577d822022-12-12 15:00:46 -08002180
2181 // Generate top-level elements of the C++ header file.
Lukasz Anforowiczbad99632022-11-18 16:39:13 -08002182 let h_body = {
Lukasz Anforowiczf04bc522022-11-04 09:19:51 -07002183 // TODO(b/254690602): Decide whether using `#crate_name` as the name of the
2184 // top-level namespace is okay (e.g. investigate if this name is globally
2185 // unique + ergonomic).
2186 let crate_name = format_cc_ident(tcx.crate_name(LOCAL_CRATE).as_str())?;
Lukasz Anforowiczbad99632022-11-18 16:39:13 -08002187
Lukasz Anforowicz54efc162022-12-16 15:58:44 -08002188 let includes = format_cc_includes(&includes);
Lukasz Anforowicze1da5372023-01-03 12:31:14 -08002189 let ordered_cc = format_namespace_bound_cc_tokens(ordered_cc);
Lukasz Anforowiczf04bc522022-11-04 09:19:51 -07002190 quote! {
Lukasz Anforowicza0502fb2023-02-13 15:33:18 -08002191 #includes
2192 __NEWLINE__ __NEWLINE__
Lukasz Anforowiczf04bc522022-11-04 09:19:51 -07002193 namespace #crate_name {
Lukasz Anforowicza0502fb2023-02-13 15:33:18 -08002194 __NEWLINE__
Lukasz Anforowicz54efc162022-12-16 15:58:44 -08002195 #ordered_cc
Lukasz Anforowicza0502fb2023-02-13 15:33:18 -08002196 __NEWLINE__
Lukasz Anforowiczf04bc522022-11-04 09:19:51 -07002197 }
Lukasz Anforowicza0502fb2023-02-13 15:33:18 -08002198 __NEWLINE__
Lukasz Anforowiczf04bc522022-11-04 09:19:51 -07002199 }
2200 };
Lukasz Anforowicz7f31f802022-12-16 08:24:13 -08002201
Lukasz Anforowiczbe25c762023-01-17 12:43:52 -08002202 Ok(Output { h_body, rs_body })
Lukasz Anforowiczed1c4f02022-09-29 11:11:20 -07002203}
2204
Lukasz Anforowiczbda1cfe2022-09-20 06:25:43 -07002205#[cfg(test)]
Lukasz Anforowiczf018e4d2022-09-28 07:35:59 -07002206pub mod tests {
Lukasz Anforowicz0b8a02f2023-02-10 15:55:40 -08002207 use super::*;
Lukasz Anforowicz581fd752022-09-21 11:30:15 -07002208
Lukasz Anforowicz8a833412022-10-14 14:44:13 -07002209 use anyhow::Result;
2210 use itertools::Itertools;
2211 use proc_macro2::TokenStream;
Lukasz Anforowicz2f2510a2022-09-29 13:06:44 -07002212 use quote::quote;
Lukasz Anforowicz4f9f5522022-10-17 15:03:37 -07002213 use rustc_middle::ty::{Ty, TyCtxt};
Lukasz Anforowicz8a833412022-10-14 14:44:13 -07002214 use rustc_span::def_id::LocalDefId;
Lukasz Anforowiczbda1cfe2022-09-20 06:25:43 -07002215
Lukasz Anforowicz0bef2642023-01-05 09:20:31 -08002216 use crate::run_compiler::tests::run_compiler_for_testing;
Lukasz Anforowicz52274992023-03-08 12:29:28 -08002217 use code_gen_utils::format_cc_includes;
Lukasz Anforowicz2f849cf2022-11-17 15:52:39 -08002218 use token_stream_matchers::{
2219 assert_cc_matches, assert_cc_not_matches, assert_rs_matches, assert_rs_not_matches,
2220 };
Lukasz Anforowicz2b38d272022-09-23 08:08:18 -07002221
Lukasz Anforowicz5bddf182022-09-30 16:06:59 -07002222 #[test]
Lukasz Anforowicz8a833412022-10-14 14:44:13 -07002223 #[should_panic(expected = "No items named `missing_name`.\n\
2224 Instead found:\n`bar`,\n`foo`,\n`m1`,\n`m2`,\n`std`")]
2225 fn test_find_def_id_by_name_panic_when_no_item_with_matching_name() {
2226 let test_src = r#"
2227 pub extern "C" fn foo() {}
2228
2229 pub mod m1 {
2230 pub fn bar() {}
2231 }
2232 pub mod m2 {
2233 pub fn bar() {}
2234 }
2235 "#;
Lukasz Anforowicz0bef2642023-01-05 09:20:31 -08002236 run_compiler_for_testing(test_src, |tcx| find_def_id_by_name(tcx, "missing_name"));
Lukasz Anforowicz8a833412022-10-14 14:44:13 -07002237 }
2238
2239 #[test]
2240 #[should_panic(expected = "More than one item named `some_name`")]
2241 fn test_find_def_id_by_name_panic_when_multiple_items_with_matching_name() {
2242 let test_src = r#"
2243 pub mod m1 {
2244 pub fn some_name() {}
2245 }
2246 pub mod m2 {
2247 pub fn some_name() {}
2248 }
2249 "#;
Lukasz Anforowicz0bef2642023-01-05 09:20:31 -08002250 run_compiler_for_testing(test_src, |tcx| find_def_id_by_name(tcx, "some_name"));
Lukasz Anforowicz8a833412022-10-14 14:44:13 -07002251 }
2252
Lukasz Anforowicz4c19ad92022-12-16 15:23:14 -08002253 /// This test covers only a single example of a function that should get a
2254 /// C++ binding. The test focuses on verification that the output from
2255 /// `format_fn` gets propagated all the way to `GenerateBindings::new`.
2256 /// Additional coverage of how functions are formatted is provided
Lukasz Anforowicze465b9e2023-02-06 15:00:25 -08002257 /// by `test_format_item_..._fn_...` tests (which work at the `format_fn`
Lukasz Anforowicz4c19ad92022-12-16 15:23:14 -08002258 /// level).
Lukasz Anforowicz8a833412022-10-14 14:44:13 -07002259 #[test]
Lukasz Anforowicz75894832022-11-22 18:25:28 -08002260 fn test_generated_bindings_fn_no_mangle_extern_c() {
Lukasz Anforowicz581fd752022-09-21 11:30:15 -07002261 let test_src = r#"
Lukasz Anforowicz7123f212022-10-20 08:51:04 -07002262 #[no_mangle]
Lukasz Anforowicz2f2510a2022-09-29 13:06:44 -07002263 pub extern "C" fn public_function() {
2264 println!("foo");
Lukasz Anforowicz581fd752022-09-21 11:30:15 -07002265 }
Lukasz Anforowicz581fd752022-09-21 11:30:15 -07002266 "#;
2267 test_generated_bindings(test_src, |bindings| {
Lukasz Anforowicz4c19ad92022-12-16 15:23:14 -08002268 let bindings = bindings.unwrap();
Lukasz Anforowicz2f2510a2022-09-29 13:06:44 -07002269 assert_cc_matches!(
2270 bindings.h_body,
2271 quote! {
Lukasz Anforowicze4333062022-10-17 14:47:53 -07002272 extern "C" void public_function();
Lukasz Anforowicz2f2510a2022-09-29 13:06:44 -07002273 }
Lukasz Anforowiczd9ff4ab2022-09-23 08:11:18 -07002274 );
Lukasz Anforowicz4c19ad92022-12-16 15:23:14 -08002275
2276 // No Rust thunks should be generated in this test scenario.
Devin Jeanpierre81aec502023-04-04 15:33:39 -07002277 assert_rs_not_matches!(bindings.rs_body, quote! { public_function });
Lukasz Anforowicz2f2510a2022-09-29 13:06:44 -07002278 });
2279 }
2280
Lukasz Anforowicz4c19ad92022-12-16 15:23:14 -08002281 /// `test_generated_bindings_fn_export_name` covers a scenario where
2282 /// `MixedSnippet::cc` is present but `MixedSnippet::rs` is empty
2283 /// (because no Rust thunks are needed).
Lukasz Anforowicz2f2510a2022-09-29 13:06:44 -07002284 #[test]
Lukasz Anforowiczf04bc522022-11-04 09:19:51 -07002285 fn test_generated_bindings_fn_export_name() {
Lukasz Anforowiczf04bc522022-11-04 09:19:51 -07002286 let test_src = r#"
2287 #[export_name = "export_name"]
2288 pub extern "C" fn public_function(x: f64, y: f64) -> f64 { x + y }
2289 "#;
2290 test_generated_bindings(test_src, |bindings| {
Lukasz Anforowicz4c19ad92022-12-16 15:23:14 -08002291 let bindings = bindings.unwrap();
Lukasz Anforowiczf04bc522022-11-04 09:19:51 -07002292 assert_cc_matches!(
2293 bindings.h_body,
2294 quote! {
Lukasz Anforowiczf04bc522022-11-04 09:19:51 -07002295 namespace rust_out {
Lukasz Anforowicz6753d402023-02-10 16:41:23 -08002296 ...
Lukasz Anforowiczb9e515a2023-07-05 12:48:22 -07002297 double public_function(double x, double y);
Lukasz Anforowiczbad99632022-11-18 16:39:13 -08002298 namespace __crubit_internal {
Lukasz Anforowicz73edf152023-04-04 12:05:00 -07002299 extern "C" double export_name(double, double);
Lukasz Anforowiczbad99632022-11-18 16:39:13 -08002300 }
Lukasz Anforowiczf04bc522022-11-04 09:19:51 -07002301 inline double public_function(double x, double y) {
Lukasz Anforowiczbad99632022-11-18 16:39:13 -08002302 return __crubit_internal::export_name(x, y);
Lukasz Anforowiczf04bc522022-11-04 09:19:51 -07002303 }
2304 }
2305 }
2306 );
2307 });
2308 }
2309
Lukasz Anforowicz4c19ad92022-12-16 15:23:14 -08002310 /// The `test_generated_bindings_struct` test covers only a single example
2311 /// of an ADT (struct/enum/union) that should get a C++ binding.
2312 /// Additional coverage of how items are formatted is provided by
Lukasz Anforowicze465b9e2023-02-06 15:00:25 -08002313 /// `test_format_item_..._struct_...`, `test_format_item_..._enum_...`,
2314 /// and `test_format_item_..._union_...` tests.
Lukasz Anforowicz4c19ad92022-12-16 15:23:14 -08002315 ///
2316 /// We don't want to duplicate coverage already provided by
Lukasz Anforowicze465b9e2023-02-06 15:00:25 -08002317 /// `test_format_item_struct_with_fields`, but we do want to verify that
Lukasz Anforowicz4c19ad92022-12-16 15:23:14 -08002318 /// * `format_crate` will actually find and process the struct
Lukasz Anforowicze465b9e2023-02-06 15:00:25 -08002319 /// (`test_format_item_...` doesn't cover this aspect - it uses a
2320 /// test-only `find_def_id_by_name` instead)
Lukasz Anforowicz4c19ad92022-12-16 15:23:14 -08002321 /// * The actual shape of the bindings still looks okay at this level.
Lukasz Anforowiczf04bc522022-11-04 09:19:51 -07002322 #[test]
Lukasz Anforowicz75894832022-11-22 18:25:28 -08002323 fn test_generated_bindings_struct() {
Lukasz Anforowicz75894832022-11-22 18:25:28 -08002324 let test_src = r#"
2325 pub struct Point {
2326 pub x: i32,
2327 pub y: i32,
2328 }
2329 "#;
2330 test_generated_bindings(test_src, |bindings| {
Lukasz Anforowicz4c19ad92022-12-16 15:23:14 -08002331 let bindings = bindings.unwrap();
Lukasz Anforowicz75894832022-11-22 18:25:28 -08002332 assert_cc_matches!(
2333 bindings.h_body,
2334 quote! {
2335 namespace rust_out {
Googler47fd9572023-01-20 09:57:32 -08002336 ...
Devin Jeanpierre64ac8ad2023-05-30 17:22:55 -07002337 struct CRUBIT_INTERNAL_RUST_TYPE(":: rust_out :: Point") alignas(4) Point final {
Lukasz Anforowicz75894832022-11-22 18:25:28 -08002338 // No point replicating test coverage of
Lukasz Anforowicze465b9e2023-02-06 15:00:25 -08002339 // `test_format_item_struct_with_fields`.
Lukasz Anforowicz75894832022-11-22 18:25:28 -08002340 ...
2341 };
2342 static_assert(sizeof(Point) == 8, ...);
2343 static_assert(alignof(Point) == 4, ...);
Lukasz Anforowicze3eb1cf2023-03-14 09:56:00 -07002344 ... // Other static_asserts are covered by
2345 // `test_format_item_struct_with_fields`
Lukasz Anforowicz75894832022-11-22 18:25:28 -08002346 } // namespace rust_out
2347 }
2348 );
2349 assert_rs_matches!(
2350 bindings.rs_body,
2351 quote! {
Lukasz Anforowiczcf60f522023-03-14 10:03:55 -07002352 // No point replicating test coverage of
2353 // `test_format_item_struct_with_fields`.
Lukasz Anforowicz8b475a42022-11-29 09:33:02 -08002354 const _: () = assert!(::std::mem::size_of::<::rust_out::Point>() == 8);
2355 const _: () = assert!(::std::mem::align_of::<::rust_out::Point>() == 4);
Lukasz Anforowiczcf60f522023-03-14 10:03:55 -07002356 const _: () = assert!( memoffset::offset_of!(::rust_out::Point, x) == 0);
2357 const _: () = assert!( memoffset::offset_of!(::rust_out::Point, y) == 4);
Lukasz Anforowicz75894832022-11-22 18:25:28 -08002358 }
2359 );
2360 });
2361 }
2362
Lukasz Anforowicz93513ec2023-02-10 14:21:20 -08002363 /// The `test_generated_bindings_impl` test covers only a single example of
2364 /// a non-trait `impl`. Additional coverage of how items are formatted
2365 /// should be provided in the future by `test_format_item_...` tests.
Lukasz Anforowicz43f06c62023-02-10 17:14:19 -08002366 ///
2367 /// We don't want to duplicate coverage already provided by
2368 /// `test_format_item_static_method`, but we do want to verify that
2369 /// * `format_crate` won't process the `impl` as a standalone HIR item
2370 /// * The actual shape of the bindings still looks okay at this level.
Lukasz Anforowicz93513ec2023-02-10 14:21:20 -08002371 #[test]
2372 fn test_generated_bindings_impl() {
2373 let test_src = r#"
2374 pub struct SomeStruct(i32);
2375
2376 impl SomeStruct {
2377 pub fn public_static_method() -> i32 { 123 }
2378
2379 #[allow(dead_code)]
2380 fn private_static_method() -> i32 { 123 }
2381 }
2382 "#;
2383 test_generated_bindings(test_src, |bindings| {
2384 let bindings = bindings.unwrap();
2385 assert_cc_matches!(
2386 bindings.h_body,
2387 quote! {
2388 namespace rust_out {
2389 ...
2390 struct ... SomeStruct ... {
2391 // No point replicating test coverage of
Lukasz Anforowicz43f06c62023-02-10 17:14:19 -08002392 // `test_format_item_static_method`.
Lukasz Anforowicz93513ec2023-02-10 14:21:20 -08002393 ...
Lukasz Anforowicz43f06c62023-02-10 17:14:19 -08002394 std::int32_t public_static_method();
2395 ...
Lukasz Anforowicz93513ec2023-02-10 14:21:20 -08002396 };
2397 ...
Lukasz Anforowicz43f06c62023-02-10 17:14:19 -08002398 std::int32_t SomeStruct::public_static_method() {
2399 ...
2400 }
Lukasz Anforowiczb7145e62023-03-23 09:00:37 -07002401 ...
Lukasz Anforowicz93513ec2023-02-10 14:21:20 -08002402 } // namespace rust_out
2403 }
2404 );
Lukasz Anforowicz43f06c62023-02-10 17:14:19 -08002405 assert_rs_matches!(
2406 bindings.rs_body,
2407 quote! {
2408 extern "C" fn ...() -> i32 {
2409 ::rust_out::SomeStruct::public_static_method()
2410 }
2411 }
2412 );
Lukasz Anforowicz93513ec2023-02-10 14:21:20 -08002413 });
2414 }
2415
Lukasz Anforowicz75894832022-11-22 18:25:28 -08002416 #[test]
Lukasz Anforowiczf04bc522022-11-04 09:19:51 -07002417 fn test_generated_bindings_includes() {
Lukasz Anforowiczed17d052022-11-02 12:07:28 -07002418 let test_src = r#"
2419 #[no_mangle]
2420 pub extern "C" fn public_function(i: i32, d: isize, u: u64) {
2421 dbg!(i);
2422 dbg!(d);
2423 dbg!(u);
2424 }
2425 "#;
2426 test_generated_bindings(test_src, |bindings| {
Lukasz Anforowicz4c19ad92022-12-16 15:23:14 -08002427 let bindings = bindings.unwrap();
Lukasz Anforowiczed17d052022-11-02 12:07:28 -07002428 assert_cc_matches!(
2429 bindings.h_body,
2430 quote! {
2431 __HASH_TOKEN__ include <cstdint> ...
2432 namespace ... {
Googler47fd9572023-01-20 09:57:32 -08002433 ...
Lukasz Anforowiczed17d052022-11-02 12:07:28 -07002434 extern "C" void public_function(
2435 std::int32_t i,
2436 std::intptr_t d,
2437 std::uint64_t u);
2438 }
2439 }
2440 );
2441 });
2442 }
2443
Lukasz Anforowicz2eb24292023-03-14 09:38:21 -07002444 /// Tests that `toposort` is used to reorder item bindings.
Lukasz Anforowiczed17d052022-11-02 12:07:28 -07002445 #[test]
Lukasz Anforowicz2eb24292023-03-14 09:38:21 -07002446 fn test_generated_bindings_prereq_defs_field_deps_require_reordering() {
Lukasz Anforowicz816cbaa2022-12-07 09:31:30 -08002447 let test_src = r#"
Lukasz Anforowicz2eb24292023-03-14 09:38:21 -07002448 // In the generated bindings `Outer` needs to come *after* `Inner`.
2449 pub struct Outer(Inner);
2450 pub struct Inner(bool);
Lukasz Anforowicz816cbaa2022-12-07 09:31:30 -08002451 "#;
2452 test_generated_bindings(test_src, |bindings| {
Lukasz Anforowicz4c19ad92022-12-16 15:23:14 -08002453 let bindings = bindings.unwrap();
Lukasz Anforowicz816cbaa2022-12-07 09:31:30 -08002454 assert_cc_matches!(
2455 bindings.h_body,
2456 quote! {
2457 namespace rust_out {
Lukasz Anforowicz0b8a02f2023-02-10 15:55:40 -08002458 ...
Devin Jeanpierre64ac8ad2023-05-30 17:22:55 -07002459 struct CRUBIT_INTERNAL_RUST_TYPE(...) alignas(1) Inner final {
Lukasz Anforowicz82502222023-06-23 09:27:14 -07002460 ... union { ... bool __field0; }; ...
Lukasz Anforowicz2eb24292023-03-14 09:38:21 -07002461 };
2462 ...
Devin Jeanpierre64ac8ad2023-05-30 17:22:55 -07002463 struct CRUBIT_INTERNAL_RUST_TYPE(...) alignas(1) Outer final {
Lukasz Anforowicz82502222023-06-23 09:27:14 -07002464 ... union { ... ::rust_out::Inner __field0; }; ...
Lukasz Anforowicz2eb24292023-03-14 09:38:21 -07002465 };
2466 ...
2467 } // namespace rust_out
Lukasz Anforowicz816cbaa2022-12-07 09:31:30 -08002468 }
2469 );
2470 });
2471 }
2472
Lukasz Anforowiczf4501b42023-01-07 09:41:29 -08002473 /// Tests that a forward declaration is present when it is required to
Lukasz Anforowicz7a7b81f2023-01-12 15:50:46 -08002474 /// preserve the original source order. In this test the
2475 /// `CcPrerequisites::fwd_decls` dependency comes from a pointer parameter.
Lukasz Anforowiczf4501b42023-01-07 09:41:29 -08002476 #[test]
Lukasz Anforowicz7a7b81f2023-01-12 15:50:46 -08002477 fn test_generated_bindings_prereq_fwd_decls_for_ptr_param() {
Lukasz Anforowiczf4501b42023-01-07 09:41:29 -08002478 let test_src = r#"
2479 // To preserve original API order we need to forward declare S.
2480 pub fn f(_: *const S) {}
2481 pub struct S(bool);
2482 "#;
2483 test_generated_bindings(test_src, |bindings| {
2484 let bindings = bindings.unwrap();
2485 assert_cc_matches!(
2486 bindings.h_body,
2487 quote! {
2488 namespace rust_out {
2489 ...
2490 // Verifing the presence of this forward declaration
Lukasz Anforowicz6753d402023-02-10 16:41:23 -08002491 // it the essence of this test. The order of the items
2492 // below also matters.
Lukasz Anforowiczf4501b42023-01-07 09:41:29 -08002493 struct S;
2494 ...
Lukasz Anforowiczb9e515a2023-07-05 12:48:22 -07002495 void f(::rust_out::S const* __param_0);
Lukasz Anforowiczf4501b42023-01-07 09:41:29 -08002496 ...
Devin Jeanpierre64ac8ad2023-05-30 17:22:55 -07002497 struct CRUBIT_INTERNAL_RUST_TYPE(...) alignas(...) S final { ... }
Lukasz Anforowiczf4501b42023-01-07 09:41:29 -08002498 ...
Lukasz Anforowiczcf783022023-06-15 10:36:58 -07002499 inline void f(::rust_out::S const* __param_0) { ... }
Lukasz Anforowicz6753d402023-02-10 16:41:23 -08002500 ...
Lukasz Anforowiczf4501b42023-01-07 09:41:29 -08002501 } // namespace rust_out
2502 }
2503 );
2504 });
2505 }
2506
Lukasz Anforowicz7a7b81f2023-01-12 15:50:46 -08002507 /// Tests that a forward declaration is present when it is required to
2508 /// preserve the original source order. In this test the
Lukasz Anforowicz10b1a292023-04-03 16:19:08 -07002509 /// `CcPrerequisites::fwd_decls` dependency comes from a
2510 /// function declaration that has a parameter that takes a struct by value.
Lukasz Anforowicz7a7b81f2023-01-12 15:50:46 -08002511 #[test]
2512 fn test_generated_bindings_prereq_fwd_decls_for_cpp_fn_decl() {
2513 let test_src = r#"
Lukasz Anforowicz7a7b81f2023-01-12 15:50:46 -08002514 #[no_mangle]
2515 pub extern "C" fn f(s: S) -> bool { s.0 }
2516
2517 #[repr(C)]
2518 pub struct S(bool);
2519 "#;
2520
2521 test_generated_bindings(test_src, |bindings| {
2522 let bindings = bindings.unwrap();
2523 assert_cc_matches!(
2524 bindings.h_body,
2525 quote! {
2526 namespace rust_out {
2527 ...
2528 // Verifing the presence of this forward declaration
Lukasz Anforowicz10b1a292023-04-03 16:19:08 -07002529 // is the essence of this test. The order also matters:
Lukasz Anforowicz7a7b81f2023-01-12 15:50:46 -08002530 // 1. The fwd decl of `S` should come first,
2531 // 2. Declaration of `f` and definition of `S` should come next
2532 // (in their original order - `f` first and then `S`).
2533 struct S;
2534 ...
Lukasz Anforowicz10b1a292023-04-03 16:19:08 -07002535 // `CcPrerequisites` of `f` declaration below (the main api of `f`) should
2536 // include `S` as a `fwd_decls` edge, rather than as a `defs` edge.
Lukasz Anforowiczb9e515a2023-07-05 12:48:22 -07002537 bool f(::rust_out::S s);
Lukasz Anforowicz7a7b81f2023-01-12 15:50:46 -08002538 ...
Devin Jeanpierre64ac8ad2023-05-30 17:22:55 -07002539 struct CRUBIT_INTERNAL_RUST_TYPE(...) alignas(...) S final { ... }
Lukasz Anforowicz7a7b81f2023-01-12 15:50:46 -08002540 ...
2541 } // namespace rust_out
2542 }
2543 );
2544 });
2545 }
2546
Lukasz Anforowiczf4501b42023-01-07 09:41:29 -08002547 /// This test verifies that a forward declaration for a given ADT is only
2548 /// emitted once (and not once for every API item that requires the
2549 /// forward declaration as a prerequisite).
2550 #[test]
2551 fn test_generated_bindings_prereq_fwd_decls_no_duplication() {
2552 let test_src = r#"
2553 // All three functions below require a forward declaration of S.
2554 pub fn f1(_: *const S) {}
2555 pub fn f2(_: *const S) {}
2556 pub fn f3(_: *const S) {}
2557
2558 pub struct S(bool);
2559
2560 // This function also includes S in its CcPrerequisites::fwd_decls
2561 // (although here it is not required, because the definition of S
2562 // is already available above).
2563 pub fn f4(_: *const S) {}
2564 "#;
2565 test_generated_bindings(test_src, |bindings| {
2566 let bindings = bindings.unwrap().h_body.to_string();
2567
2568 // Only a single forward declaration is expected.
2569 assert_eq!(1, bindings.matches("struct S ;").count(), "bindings = {bindings}");
2570 });
2571 }
2572
2573 /// This test verifies that forward declarations are emitted in a
2574 /// deterministic order. The particular order doesn't matter _that_
2575 /// much, but it definitely shouldn't change every time
2576 /// `cc_bindings_from_rs` is invoked again. The current order preserves
2577 /// the original source order of the Rust API items.
2578 #[test]
2579 fn test_generated_bindings_prereq_fwd_decls_deterministic_order() {
2580 let test_src = r#"
2581 // To try to mix things up, the bindings for the functions below
2582 // will *ask* for forward declarations in a different order:
2583 // * Different from the order in which the forward declarations
2584 // are expected to be *emitted* (the original source order).
2585 // * Different from alphabetical order.
2586 pub fn f1(_: *const b::S3) {}
2587 pub fn f2(_: *const a::S2) {}
2588 pub fn f3(_: *const a::S1) {}
2589
2590 pub mod a {
2591 pub struct S1(bool);
2592 pub struct S2(bool);
2593 }
2594
2595 pub mod b {
2596 pub struct S3(bool);
2597 }
2598 "#;
2599 test_generated_bindings(test_src, |bindings| {
2600 let bindings = bindings.unwrap();
2601 assert_cc_matches!(
2602 bindings.h_body,
2603 quote! {
2604 namespace rust_out {
2605 ...
2606 // Verifying that we get the same order in each test
2607 // run is the essence of this test.
2608 namespace a {
2609 struct S1;
2610 struct S2;
2611 }
2612 namespace b {
2613 struct S3;
2614 }
2615 ...
Lukasz Anforowiczb9e515a2023-07-05 12:48:22 -07002616 void f1 ...
2617 void f2 ...
2618 void f3 ...
Lukasz Anforowiczf4501b42023-01-07 09:41:29 -08002619
2620 namespace a { ...
Devin Jeanpierre64ac8ad2023-05-30 17:22:55 -07002621 struct CRUBIT_INTERNAL_RUST_TYPE(...) alignas(...) S1 final { ... } ...
2622 struct CRUBIT_INTERNAL_RUST_TYPE(...) alignas(...) S2 final { ... } ...
Lukasz Anforowiczf4501b42023-01-07 09:41:29 -08002623 } ...
2624 namespace b { ...
Devin Jeanpierre64ac8ad2023-05-30 17:22:55 -07002625 struct CRUBIT_INTERNAL_RUST_TYPE(...) alignas(...) S3 final { ... } ...
Lukasz Anforowiczf4501b42023-01-07 09:41:29 -08002626 } ...
2627 } // namespace rust_out
2628 }
2629 );
2630 });
2631 }
2632
2633 /// This test verifies that forward declarations are not emitted if they are
2634 /// not needed (e.g. if bindings the given `struct` or other ADT have
2635 /// already been defined earlier). In particular, we don't want to emit
2636 /// forward declarations for *all* `structs` (regardless if they are
2637 /// needed or not).
2638 #[test]
Lukasz Anforowicz64b04ba2023-02-10 17:19:05 -08002639 fn test_generated_bindings_prereq_fwd_decls_not_needed_because_of_initial_order() {
Lukasz Anforowiczf4501b42023-01-07 09:41:29 -08002640 let test_src = r#"
2641 pub struct S(bool);
2642
2643 // S is already defined above - no need for forward declaration in C++.
2644 pub fn f(_s: *const S) {}
2645 "#;
2646 test_generated_bindings(test_src, |bindings| {
2647 let bindings = bindings.unwrap();
2648 assert_cc_not_matches!(bindings.h_body, quote! { struct S; });
Lukasz Anforowiczcf783022023-06-15 10:36:58 -07002649 assert_cc_matches!(bindings.h_body, quote! { void f(::rust_out::S const* _s); });
Lukasz Anforowicz64b04ba2023-02-10 17:19:05 -08002650 });
2651 }
2652
2653 /// This test verifies that a method declaration doesn't ask for a forward
2654 /// declaration to the struct.
2655 #[test]
2656 fn test_generated_bindings_prereq_fwd_decls_not_needed_inside_struct_definition() {
2657 let test_src = r#"
Lukasz Anforowicz2eb24292023-03-14 09:38:21 -07002658 #![allow(dead_code)]
2659
2660 pub struct S {
2661 // This shouldn't require a fwd decl of S.
2662 field: *const S,
2663 }
Lukasz Anforowicz64b04ba2023-02-10 17:19:05 -08002664
2665 impl S {
2666 // This shouldn't require a fwd decl of S.
Lukasz Anforowicz2eb24292023-03-14 09:38:21 -07002667 pub fn create() -> S { Self{ field: std::ptr::null() } }
Lukasz Anforowicz64b04ba2023-02-10 17:19:05 -08002668 }
2669 "#;
2670 test_generated_bindings(test_src, |bindings| {
2671 let bindings = bindings.unwrap();
2672 assert_cc_not_matches!(bindings.h_body, quote! { struct S; });
Lukasz Anforowicz2eb24292023-03-14 09:38:21 -07002673 assert_cc_matches!(
2674 bindings.h_body,
2675 quote! {
Lukasz Anforowiczb9e515a2023-07-05 12:48:22 -07002676 static ::rust_out::S create(); ...
Lukasz Anforowicz82502222023-06-23 09:27:14 -07002677 union { ... ::rust_out::S const* field; }; ...
Lukasz Anforowicz2eb24292023-03-14 09:38:21 -07002678 }
2679 );
Lukasz Anforowiczf4501b42023-01-07 09:41:29 -08002680 });
2681 }
2682
Lukasz Anforowicz816cbaa2022-12-07 09:31:30 -08002683 #[test]
Lukasz Anforowicze1da5372023-01-03 12:31:14 -08002684 fn test_generated_bindings_module_basics() {
Lukasz Anforowicza577d822022-12-12 15:00:46 -08002685 let test_src = r#"
2686 pub mod some_module {
2687 pub fn some_func() {}
2688 }
2689 "#;
2690 test_generated_bindings(test_src, |bindings| {
Lukasz Anforowicz4c19ad92022-12-16 15:23:14 -08002691 let bindings = bindings.unwrap();
Lukasz Anforowicza577d822022-12-12 15:00:46 -08002692 assert_cc_matches!(
2693 bindings.h_body,
2694 quote! {
2695 namespace rust_out {
Lukasz Anforowicza577d822022-12-12 15:00:46 -08002696 namespace some_module {
2697 ...
2698 inline void some_func() { ... }
2699 ...
2700 } // namespace some_module
2701 } // namespace rust_out
2702 }
2703 );
2704 assert_rs_matches!(
2705 bindings.rs_body,
2706 quote! {
2707 #[no_mangle]
2708 extern "C"
2709 fn ...() -> () {
2710 ::rust_out::some_module::some_func()
2711 }
2712 }
2713 );
2714 });
2715 }
2716
Lukasz Anforowicze1da5372023-01-03 12:31:14 -08002717 #[test]
2718 fn test_generated_bindings_module_name_is_cpp_reserved_keyword() {
2719 let test_src = r#"
2720 pub mod working_module {
2721 pub fn working_module_f1() {}
2722 pub fn working_module_f2() {}
2723 }
2724 pub mod reinterpret_cast {
2725 pub fn broken_module_f1() {}
2726 pub fn broken_module_f2() {}
2727 }
2728 "#;
2729 test_generated_bindings(test_src, |bindings| {
2730 let bindings = bindings.unwrap();
2731
2732 // Items in the broken module should be replaced with a comment explaining the
2733 // problem.
2734 let broken_module_msg = "Failed to format namespace name `reinterpret_cast`: \
2735 `reinterpret_cast` is a C++ reserved keyword \
2736 and can't be used as a C++ identifier";
2737 assert_cc_not_matches!(bindings.h_body, quote! { namespace reinterpret_cast });
2738 assert_cc_not_matches!(bindings.h_body, quote! { broken_module_f1 });
2739 assert_cc_not_matches!(bindings.h_body, quote! { broken_module_f2 });
2740
2741 // Items in the other module should still go through.
2742 assert_cc_matches!(
2743 bindings.h_body,
2744 quote! {
2745 namespace rust_out {
2746 namespace working_module {
2747 ...
Lukasz Anforowiczb9e515a2023-07-05 12:48:22 -07002748 void working_module_f1();
Lukasz Anforowicze1da5372023-01-03 12:31:14 -08002749 ...
Lukasz Anforowiczb9e515a2023-07-05 12:48:22 -07002750 void working_module_f2();
Lukasz Anforowicze1da5372023-01-03 12:31:14 -08002751 ...
2752 } // namespace some_module
2753
2754 __COMMENT__ #broken_module_msg
Lukasz Anforowicz6753d402023-02-10 16:41:23 -08002755 ...
Lukasz Anforowicze1da5372023-01-03 12:31:14 -08002756 } // namespace rust_out
2757 }
2758 );
2759 });
2760 }
2761
Lukasz Anforowicz4c19ad92022-12-16 15:23:14 -08002762 /// `test_generated_bindings_non_pub_items` verifies that non-public items
2763 /// are not present/propagated into the generated bindings.
Lukasz Anforowicza577d822022-12-12 15:00:46 -08002764 #[test]
Lukasz Anforowicz75894832022-11-22 18:25:28 -08002765 fn test_generated_bindings_non_pub_items() {
Lukasz Anforowicz2f2510a2022-09-29 13:06:44 -07002766 let test_src = r#"
Lukasz Anforowicz88bde9b2022-10-14 14:48:07 -07002767 #![allow(dead_code)]
Lukasz Anforowicz75894832022-11-22 18:25:28 -08002768
Lukasz Anforowicz2f2510a2022-09-29 13:06:44 -07002769 extern "C" fn private_function() {
2770 println!("foo");
2771 }
Lukasz Anforowicz75894832022-11-22 18:25:28 -08002772
2773 struct PrivateStruct {
2774 x: i32,
2775 y: i32,
2776 }
Lukasz Anforowicza577d822022-12-12 15:00:46 -08002777
Lukasz Anforowicz14229762023-02-10 15:28:33 -08002778 pub struct PublicStruct(i32);
2779
2780 impl PublicStruct {
2781 fn private_method() {}
2782 }
2783
Lukasz Anforowicza577d822022-12-12 15:00:46 -08002784 pub mod public_module {
2785 fn priv_func_in_pub_module() {}
2786 }
2787
2788 mod private_module {
2789 pub fn pub_func_in_priv_module() { priv_func_in_priv_module() }
2790 fn priv_func_in_priv_module() {}
2791 }
Lukasz Anforowicz2f2510a2022-09-29 13:06:44 -07002792 "#;
2793 test_generated_bindings(test_src, |bindings| {
Lukasz Anforowicz4c19ad92022-12-16 15:23:14 -08002794 let bindings = bindings.unwrap();
Lukasz Anforowicz2f2510a2022-09-29 13:06:44 -07002795 assert_cc_not_matches!(bindings.h_body, quote! { private_function });
Lukasz Anforowicze7a25002022-11-10 06:21:42 -08002796 assert_rs_not_matches!(bindings.rs_body, quote! { private_function });
Lukasz Anforowicz75894832022-11-22 18:25:28 -08002797 assert_cc_not_matches!(bindings.h_body, quote! { PrivateStruct });
2798 assert_rs_not_matches!(bindings.rs_body, quote! { PrivateStruct });
Lukasz Anforowicz14229762023-02-10 15:28:33 -08002799 assert_cc_not_matches!(bindings.h_body, quote! { private_method });
2800 assert_rs_not_matches!(bindings.rs_body, quote! { private_method });
Lukasz Anforowicza577d822022-12-12 15:00:46 -08002801 assert_cc_not_matches!(bindings.h_body, quote! { priv_func_in_priv_module });
2802 assert_rs_not_matches!(bindings.rs_body, quote! { priv_func_in_priv_module });
2803 assert_cc_not_matches!(bindings.h_body, quote! { priv_func_in_pub_module });
2804 assert_rs_not_matches!(bindings.rs_body, quote! { priv_func_in_pub_module });
Lukasz Anforowicz1382f392022-12-12 17:13:23 -08002805 assert_cc_not_matches!(bindings.h_body, quote! { private_module });
2806 assert_rs_not_matches!(bindings.rs_body, quote! { private_module });
2807 assert_cc_not_matches!(bindings.h_body, quote! { pub_func_in_priv_module });
2808 assert_rs_not_matches!(bindings.rs_body, quote! { pub_func_in_priv_module });
Lukasz Anforowicz2f2510a2022-09-29 13:06:44 -07002809 });
2810 }
2811
2812 #[test]
Lukasz Anforowicz4aa1ff92022-10-10 11:22:22 -07002813 fn test_generated_bindings_top_level_items() {
Lukasz Anforowicz2f2510a2022-09-29 13:06:44 -07002814 let test_src = "pub fn public_function() {}";
2815 test_generated_bindings(test_src, |bindings| {
Lukasz Anforowicz4c19ad92022-12-16 15:23:14 -08002816 let bindings = bindings.unwrap();
Devin Jeanpierre81aec502023-04-04 15:33:39 -07002817 let expected_comment_txt = "Automatically @generated C++ bindings for the following Rust crate:\n\
Lukasz Anforowicz2f2510a2022-09-29 13:06:44 -07002818 rust_out";
2819 assert_cc_matches!(
2820 bindings.h_body,
2821 quote! {
2822 __COMMENT__ #expected_comment_txt
Lukasz Anforowicz4aa1ff92022-10-10 11:22:22 -07002823 ...
Lukasz Anforowicz31b29cd2022-10-10 11:33:41 -07002824 __HASH_TOKEN__ pragma once
2825 ...
Lukasz Anforowicz4aa1ff92022-10-10 11:22:22 -07002826 namespace rust_out {
2827 ...
2828 }
Lukasz Anforowicz2f2510a2022-09-29 13:06:44 -07002829 }
2830 );
Lukasz Anforowicze7a25002022-11-10 06:21:42 -08002831 assert_cc_matches!(
2832 bindings.rs_body,
2833 quote! {
2834 __COMMENT__ #expected_comment_txt
2835 }
2836 );
Lukasz Anforowicz2f2510a2022-09-29 13:06:44 -07002837 })
2838 }
2839
Lukasz Anforowicz4c19ad92022-12-16 15:23:14 -08002840 /// The `test_generated_bindings_unsupported_item` test verifies how `Err`
Lukasz Anforowicze465b9e2023-02-06 15:00:25 -08002841 /// from `format_item` is formatted as a C++ comment (in `format_crate`
Lukasz Anforowicz4c19ad92022-12-16 15:23:14 -08002842 /// and `format_unsupported_def`):
2843 /// - This test covers only a single example of an unsupported item.
Lukasz Anforowicze465b9e2023-02-06 15:00:25 -08002844 /// Additional coverage is provided by `test_format_item_unsupported_...`
Lukasz Anforowicz4c19ad92022-12-16 15:23:14 -08002845 /// tests.
2846 /// - This test somewhat arbitrarily chooses an example of an unsupported
2847 /// item, trying to pick one that 1) will never be supported (b/254104998
2848 /// has some extra notes about APIs named after reserved C++ keywords) and
2849 /// 2) tests that the full error chain is included in the message.
Lukasz Anforowicz2f2510a2022-09-29 13:06:44 -07002850 #[test]
2851 fn test_generated_bindings_unsupported_item() {
Lukasz Anforowicz2f2510a2022-09-29 13:06:44 -07002852 let test_src = r#"
Lukasz Anforowicz7123f212022-10-20 08:51:04 -07002853 #[no_mangle]
Lukasz Anforowicze4333062022-10-17 14:47:53 -07002854 pub extern "C" fn reinterpret_cast() {}
Lukasz Anforowicz2f2510a2022-09-29 13:06:44 -07002855 "#;
Lukasz Anforowicz2f2510a2022-09-29 13:06:44 -07002856 test_generated_bindings(test_src, |bindings| {
Lukasz Anforowicz4c19ad92022-12-16 15:23:14 -08002857 let bindings = bindings.unwrap();
Lukasz Anforowicze4333062022-10-17 14:47:53 -07002858 let expected_comment_txt = "Error generating bindings for `reinterpret_cast` \
Googler785e6b42023-01-23 12:11:36 -08002859 defined at <crubit_unittests.rs>;l=3: \
Lukasz Anforowicze4333062022-10-17 14:47:53 -07002860 Error formatting function name: \
2861 `reinterpret_cast` is a C++ reserved keyword \
2862 and can't be used as a C++ identifier";
Lukasz Anforowicz2f2510a2022-09-29 13:06:44 -07002863 assert_cc_matches!(
2864 bindings.h_body,
2865 quote! {
2866 __COMMENT__ #expected_comment_txt
2867 }
2868 );
Lukasz Anforowicz581fd752022-09-21 11:30:15 -07002869 })
2870 }
2871
Lukasz Anforowicz8a833412022-10-14 14:44:13 -07002872 #[test]
Lukasz Anforowiczce17f3f2023-02-27 11:32:14 -08002873 fn test_generated_bindings_reimports() {
2874 let test_src = r#"
2875 #![allow(dead_code)]
2876 #![allow(unused_imports)]
2877 mod private_submodule1 {
2878 pub fn subfunction1() {}
2879 pub fn subfunction2() {}
2880 pub fn subfunction3() {}
2881 }
2882 mod private_submodule2 {
2883 pub fn subfunction8() {}
2884 pub fn subfunction9() {}
2885 }
2886
2887 // Public re-import.
2888 pub use private_submodule1::subfunction1;
2889
2890 // Private re-import.
2891 use private_submodule1::subfunction2;
2892
2893 // Re-import that renames.
2894 pub use private_submodule1::subfunction3 as public_function3;
2895
2896 // Re-import of multiple items via glob.
2897 pub use private_submodule2::*;
2898 "#;
2899 test_generated_bindings(test_src, |bindings| {
2900 let bindings = bindings.unwrap();
2901
2902 let failures = vec![(1, 15), (3, 21), (4, 24)];
2903 for (use_number, line_number) in failures.into_iter() {
2904 let expected_comment_txt = format!(
2905 "Error generating bindings for `{{use#{use_number}}}` defined at \
2906 <crubit_unittests.rs>;l={line_number}: \
2907 Unsupported rustc_hir::hir::ItemKind: `use` import"
2908 );
2909 assert_cc_matches!(
2910 bindings.h_body,
2911 quote! {
2912 __COMMENT__ #expected_comment_txt
2913 }
2914 );
2915 }
2916 });
2917 }
2918
2919 #[test]
Lukasz Anforowicze465b9e2023-02-06 15:00:25 -08002920 fn test_format_item_fn_extern_c_no_mangle_no_params_no_return_type() {
Lukasz Anforowicz8a833412022-10-14 14:44:13 -07002921 let test_src = r#"
Lukasz Anforowicz7123f212022-10-20 08:51:04 -07002922 #[no_mangle]
Lukasz Anforowicz8a833412022-10-14 14:44:13 -07002923 pub extern "C" fn public_function() {}
2924 "#;
Lukasz Anforowicze465b9e2023-02-06 15:00:25 -08002925 test_format_item(test_src, "public_function", |result| {
Lukasz Anforowicz73a2c0d2023-03-22 14:07:00 -07002926 let result = result.unwrap().unwrap();
2927 let main_api = &result.main_api;
Lukasz Anforowicz0b8a02f2023-02-10 15:55:40 -08002928 assert!(main_api.prereqs.is_empty());
Lukasz Anforowicze4333062022-10-17 14:47:53 -07002929 assert_cc_matches!(
Lukasz Anforowicz0b8a02f2023-02-10 15:55:40 -08002930 main_api.tokens,
Lukasz Anforowicze4333062022-10-17 14:47:53 -07002931 quote! {
2932 extern "C" void public_function();
2933 }
2934 );
Lukasz Anforowicz10b1a292023-04-03 16:19:08 -07002935
2936 // Sufficient to just re-declare the Rust API in C++.
2937 // (i.e. there is no need to have a C++-side definition of `public_function`).
2938 assert!(result.cc_details.tokens.is_empty());
2939
2940 // There is no need to have a separate thunk for an `extern "C"` function.
2941 assert!(result.rs_details.is_empty());
Lukasz Anforowicze4333062022-10-17 14:47:53 -07002942 });
2943 }
2944
Lukasz Anforowicze465b9e2023-02-06 15:00:25 -08002945 /// The `test_format_item_fn_explicit_unit_return_type` test below is very
Lukasz Anforowicz4c19ad92022-12-16 15:23:14 -08002946 /// similar to the
Lukasz Anforowicze465b9e2023-02-06 15:00:25 -08002947 /// `test_format_item_fn_extern_c_no_mangle_no_params_no_return_type` above,
Lukasz Anforowicz4c19ad92022-12-16 15:23:14 -08002948 /// except that the return type is explicitly spelled out. There is no
2949 /// difference in `ty::FnSig` so our code behaves exactly the same, but the
2950 /// test has been planned based on earlier, hir-focused approach and having
2951 /// this extra test coverage shouldn't hurt. (`hir::FnSig`
2952 /// and `hir::FnRetTy` _would_ see a difference between the two tests, even
2953 /// though there is no different in the current `bindings.rs` code).
Lukasz Anforowicze4333062022-10-17 14:47:53 -07002954 #[test]
Lukasz Anforowicze465b9e2023-02-06 15:00:25 -08002955 fn test_format_item_fn_explicit_unit_return_type() {
Lukasz Anforowicze4333062022-10-17 14:47:53 -07002956 let test_src = r#"
Lukasz Anforowicz7123f212022-10-20 08:51:04 -07002957 #[no_mangle]
Lukasz Anforowicze4333062022-10-17 14:47:53 -07002958 pub extern "C" fn explicit_unit_return_type() -> () {}
2959 "#;
Lukasz Anforowicze465b9e2023-02-06 15:00:25 -08002960 test_format_item(test_src, "explicit_unit_return_type", |result| {
Lukasz Anforowicz73a2c0d2023-03-22 14:07:00 -07002961 let result = result.unwrap().unwrap();
2962 let main_api = &result.main_api;
Lukasz Anforowicz0b8a02f2023-02-10 15:55:40 -08002963 assert!(main_api.prereqs.is_empty());
Lukasz Anforowicze4333062022-10-17 14:47:53 -07002964 assert_cc_matches!(
Lukasz Anforowicz0b8a02f2023-02-10 15:55:40 -08002965 main_api.tokens,
Lukasz Anforowicze4333062022-10-17 14:47:53 -07002966 quote! {
2967 extern "C" void explicit_unit_return_type();
2968 }
2969 );
2970 });
2971 }
2972
2973 #[test]
Lukasz Anforowicze465b9e2023-02-06 15:00:25 -08002974 fn test_format_item_fn_never_return_type() {
Lukasz Anforowicz903fc632022-10-25 08:55:33 -07002975 let test_src = r#"
2976 #[no_mangle]
2977 pub extern "C" fn never_returning_function() -> ! {
2978 panic!("This function panics and therefore never returns");
2979 }
2980 "#;
Lukasz Anforowicze465b9e2023-02-06 15:00:25 -08002981 test_format_item(test_src, "never_returning_function", |result| {
Lukasz Anforowicz903fc632022-10-25 08:55:33 -07002982 // TODO(b/254507801): The function should be annotated with the `[[noreturn]]`
2983 // attribute.
2984 // TODO(b/254507801): Expect `crubit::Never` instead (see the bug for more
2985 // details).
Lukasz Anforowicz73a2c0d2023-03-22 14:07:00 -07002986 let result = result.unwrap().unwrap();
2987 let main_api = &result.main_api;
Lukasz Anforowicz0b8a02f2023-02-10 15:55:40 -08002988 assert!(main_api.prereqs.is_empty());
Lukasz Anforowicz903fc632022-10-25 08:55:33 -07002989 assert_cc_matches!(
Lukasz Anforowicz0b8a02f2023-02-10 15:55:40 -08002990 main_api.tokens,
Lukasz Anforowicz903fc632022-10-25 08:55:33 -07002991 quote! {
2992 extern "C" void never_returning_function();
2993 }
2994 );
2995 })
2996 }
2997
Lukasz Anforowicze465b9e2023-02-06 15:00:25 -08002998 /// `test_format_item_fn_mangling` checks that bindings can be generated for
Lukasz Anforowicz4c19ad92022-12-16 15:23:14 -08002999 /// `extern "C"` functions that do *not* have `#[no_mangle]` attribute. The
3000 /// test elides away the mangled name in the `assert_cc_matches` checks
3001 /// below, but end-to-end test coverage should eventually be provided by
3002 /// `test/functions` (see b/262904507).
Lukasz Anforowicz903fc632022-10-25 08:55:33 -07003003 #[test]
Lukasz Anforowicze465b9e2023-02-06 15:00:25 -08003004 fn test_format_item_fn_mangling() {
Lukasz Anforowicz7123f212022-10-20 08:51:04 -07003005 let test_src = r#"
Lukasz Anforowiczf04bc522022-11-04 09:19:51 -07003006 pub extern "C" fn public_function(x: f64, y: f64) -> f64 { x + y }
Lukasz Anforowicz7123f212022-10-20 08:51:04 -07003007 "#;
Lukasz Anforowicze465b9e2023-02-06 15:00:25 -08003008 test_format_item(test_src, "public_function", |result| {
Lukasz Anforowicz73a2c0d2023-03-22 14:07:00 -07003009 let result = result.unwrap().unwrap();
3010 let main_api = &result.main_api;
Lukasz Anforowicz6753d402023-02-10 16:41:23 -08003011 assert!(main_api.prereqs.is_empty());
Lukasz Anforowiczf04bc522022-11-04 09:19:51 -07003012 assert_cc_matches!(
Lukasz Anforowicz6753d402023-02-10 16:41:23 -08003013 main_api.tokens,
3014 quote! {
Lukasz Anforowiczb9e515a2023-07-05 12:48:22 -07003015 double public_function(double x, double y);
Lukasz Anforowicz6753d402023-02-10 16:41:23 -08003016 }
3017 );
Lukasz Anforowicz73a2c0d2023-03-22 14:07:00 -07003018 assert!(result.rs_details.is_empty());
3019 assert!(result.cc_details.prereqs.is_empty());
Lukasz Anforowicz6753d402023-02-10 16:41:23 -08003020 assert_cc_matches!(
Lukasz Anforowicz73a2c0d2023-03-22 14:07:00 -07003021 result.cc_details.tokens,
Lukasz Anforowiczf04bc522022-11-04 09:19:51 -07003022 quote! {
Lukasz Anforowiczbad99632022-11-18 16:39:13 -08003023 namespace __crubit_internal {
Lukasz Anforowicz73edf152023-04-04 12:05:00 -07003024 extern "C" double ...(double, double);
Lukasz Anforowiczbad99632022-11-18 16:39:13 -08003025 }
Googler47fd9572023-01-20 09:57:32 -08003026 ...
Lukasz Anforowiczf04bc522022-11-04 09:19:51 -07003027 inline double public_function(double x, double y) {
Lukasz Anforowicz75894832022-11-22 18:25:28 -08003028 return __crubit_internal::...(x, y);
Lukasz Anforowiczf04bc522022-11-04 09:19:51 -07003029 }
3030 }
3031 );
Lukasz Anforowicz7123f212022-10-20 08:51:04 -07003032 });
3033 }
3034
3035 #[test]
Lukasz Anforowicze465b9e2023-02-06 15:00:25 -08003036 fn test_format_item_fn_export_name() {
Lukasz Anforowicz7123f212022-10-20 08:51:04 -07003037 let test_src = r#"
Lukasz Anforowiczf04bc522022-11-04 09:19:51 -07003038 #[export_name = "export_name"]
3039 pub extern "C" fn public_function(x: f64, y: f64) -> f64 { x + y }
Lukasz Anforowicz7123f212022-10-20 08:51:04 -07003040 "#;
Lukasz Anforowicze465b9e2023-02-06 15:00:25 -08003041 test_format_item(test_src, "public_function", |result| {
Lukasz Anforowicz73a2c0d2023-03-22 14:07:00 -07003042 let result = result.unwrap().unwrap();
3043 let main_api = &result.main_api;
Lukasz Anforowicz6753d402023-02-10 16:41:23 -08003044 assert!(main_api.prereqs.is_empty());
Lukasz Anforowiczf04bc522022-11-04 09:19:51 -07003045 assert_cc_matches!(
Lukasz Anforowicz6753d402023-02-10 16:41:23 -08003046 main_api.tokens,
3047 quote! {
Lukasz Anforowiczb9e515a2023-07-05 12:48:22 -07003048 double public_function(double x, double y);
Lukasz Anforowicz6753d402023-02-10 16:41:23 -08003049 }
3050 );
Lukasz Anforowicz10b1a292023-04-03 16:19:08 -07003051
3052 // There is no need to have a separate thunk for an `extern "C"` function.
Lukasz Anforowicz73a2c0d2023-03-22 14:07:00 -07003053 assert!(result.rs_details.is_empty());
Lukasz Anforowicz10b1a292023-04-03 16:19:08 -07003054
3055 // We generate a C++-side definition of `public_function` so that we
3056 // can call a differently-named (but same-signature) `export_name` function.
Lukasz Anforowicz73a2c0d2023-03-22 14:07:00 -07003057 assert!(result.cc_details.prereqs.is_empty());
Lukasz Anforowicz6753d402023-02-10 16:41:23 -08003058 assert_cc_matches!(
Lukasz Anforowicz73a2c0d2023-03-22 14:07:00 -07003059 result.cc_details.tokens,
Lukasz Anforowiczf04bc522022-11-04 09:19:51 -07003060 quote! {
Lukasz Anforowiczbad99632022-11-18 16:39:13 -08003061 namespace __crubit_internal {
Lukasz Anforowicz73edf152023-04-04 12:05:00 -07003062 extern "C" double export_name(double, double);
Lukasz Anforowiczf04bc522022-11-04 09:19:51 -07003063 }
Googler47fd9572023-01-20 09:57:32 -08003064 ...
Lukasz Anforowiczbad99632022-11-18 16:39:13 -08003065 inline double public_function(double x, double y) {
3066 return __crubit_internal::export_name(x, y);
3067 }
Lukasz Anforowiczf04bc522022-11-04 09:19:51 -07003068 }
3069 );
Lukasz Anforowicz7123f212022-10-20 08:51:04 -07003070 });
3071 }
3072
3073 #[test]
Lukasz Anforowicze465b9e2023-02-06 15:00:25 -08003074 fn test_format_item_unsupported_fn_unsafe() {
Lukasz Anforowicze4333062022-10-17 14:47:53 -07003075 let test_src = r#"
Lukasz Anforowicz7123f212022-10-20 08:51:04 -07003076 #[no_mangle]
Lukasz Anforowicze4333062022-10-17 14:47:53 -07003077 pub unsafe extern "C" fn foo() {}
3078 "#;
Lukasz Anforowicze465b9e2023-02-06 15:00:25 -08003079 test_format_item(test_src, "foo", |result| {
Lukasz Anforowicz4c19ad92022-12-16 15:23:14 -08003080 let err = result.unwrap_err();
Lukasz Anforowicze4333062022-10-17 14:47:53 -07003081 assert_eq!(
3082 err,
3083 "Bindings for `unsafe` functions \
3084 are not fully designed yet (b/254095482)"
3085 );
3086 });
3087 }
3088
Lukasz Anforowicze465b9e2023-02-06 15:00:25 -08003089 /// `test_format_item_fn_const` tests how bindings for an `const fn` are
Lukasz Anforowicz4c19ad92022-12-16 15:23:14 -08003090 /// generated.
3091 ///
Googler47fd9572023-01-20 09:57:32 -08003092 /// Right now the `const` qualifier is ignored, but one can imagine that in
3093 /// the (very) long-term future such functions (including their bodies)
3094 /// could be translated into C++ `consteval` functions.
Lukasz Anforowicze4333062022-10-17 14:47:53 -07003095 #[test]
Lukasz Anforowicze465b9e2023-02-06 15:00:25 -08003096 fn test_format_item_fn_const() {
Lukasz Anforowicze4333062022-10-17 14:47:53 -07003097 let test_src = r#"
3098 pub const fn foo(i: i32) -> i32 { i * 42 }
3099 "#;
Lukasz Anforowicze465b9e2023-02-06 15:00:25 -08003100 test_format_item(test_src, "foo", |result| {
Lukasz Anforowicze4333062022-10-17 14:47:53 -07003101 // TODO(b/254095787): Update test expectations below once `const fn` from Rust
3102 // is translated into a `consteval` C++ function.
Lukasz Anforowicz73a2c0d2023-03-22 14:07:00 -07003103 let result = result.unwrap().unwrap();
3104 let main_api = &result.main_api;
Lukasz Anforowicz6753d402023-02-10 16:41:23 -08003105 assert!(!main_api.prereqs.is_empty());
Lukasz Anforowicz2f849cf2022-11-17 15:52:39 -08003106 assert_cc_matches!(
Lukasz Anforowicz6753d402023-02-10 16:41:23 -08003107 main_api.tokens,
3108 quote! {
Lukasz Anforowiczb9e515a2023-07-05 12:48:22 -07003109 std::int32_t foo(std::int32_t i);
Lukasz Anforowicz6753d402023-02-10 16:41:23 -08003110 }
3111 );
Lukasz Anforowicz73a2c0d2023-03-22 14:07:00 -07003112 assert!(!result.cc_details.prereqs.is_empty());
Lukasz Anforowicz6753d402023-02-10 16:41:23 -08003113 assert_cc_matches!(
Lukasz Anforowicz73a2c0d2023-03-22 14:07:00 -07003114 result.cc_details.tokens,
Lukasz Anforowicz2f849cf2022-11-17 15:52:39 -08003115 quote! {
Lukasz Anforowiczbad99632022-11-18 16:39:13 -08003116 namespace __crubit_internal {
Lukasz Anforowicz73edf152023-04-04 12:05:00 -07003117 extern "C" std::int32_t ...( std::int32_t);
Lukasz Anforowiczbad99632022-11-18 16:39:13 -08003118 }
Googler47fd9572023-01-20 09:57:32 -08003119 ...
Lukasz Anforowicz2f849cf2022-11-17 15:52:39 -08003120 inline std::int32_t foo(std::int32_t i) {
Lukasz Anforowiczb4beb392022-12-01 16:49:11 -08003121 return __crubit_internal::...(i);
Lukasz Anforowicz2f849cf2022-11-17 15:52:39 -08003122 }
3123 }
3124 );
Lukasz Anforowicz2f849cf2022-11-17 15:52:39 -08003125 assert_rs_matches!(
Lukasz Anforowicz73a2c0d2023-03-22 14:07:00 -07003126 result.rs_details,
Lukasz Anforowicz2f849cf2022-11-17 15:52:39 -08003127 quote! {
3128 #[no_mangle]
3129 extern "C"
Lukasz Anforowiczb4beb392022-12-01 16:49:11 -08003130 fn ...(i: i32) -> i32 {
Lukasz Anforowicz8b475a42022-11-29 09:33:02 -08003131 ::rust_out::foo(i)
Lukasz Anforowicz2f849cf2022-11-17 15:52:39 -08003132 }
3133 }
3134 );
Lukasz Anforowicze4333062022-10-17 14:47:53 -07003135 });
3136 }
3137
3138 #[test]
Lukasz Anforowicze465b9e2023-02-06 15:00:25 -08003139 fn test_format_item_fn_with_c_unwind_abi() {
Lukasz Anforowicze4333062022-10-17 14:47:53 -07003140 // See also https://rust-lang.github.io/rfcs/2945-c-unwind-abi.html
3141 let test_src = r#"
3142 #![feature(c_unwind)]
Lukasz Anforowicz7123f212022-10-20 08:51:04 -07003143
3144 #[no_mangle]
Lukasz Anforowicze4333062022-10-17 14:47:53 -07003145 pub extern "C-unwind" fn may_throw() {}
3146 "#;
Lukasz Anforowicze465b9e2023-02-06 15:00:25 -08003147 test_format_item(test_src, "may_throw", |result| {
Lukasz Anforowicz73a2c0d2023-03-22 14:07:00 -07003148 let result = result.unwrap().unwrap();
3149 let main_api = &result.main_api;
Lukasz Anforowicz0b8a02f2023-02-10 15:55:40 -08003150 assert!(main_api.prereqs.is_empty());
Lukasz Anforowicze4333062022-10-17 14:47:53 -07003151 assert_cc_matches!(
Lukasz Anforowicz0b8a02f2023-02-10 15:55:40 -08003152 main_api.tokens,
Lukasz Anforowicze4333062022-10-17 14:47:53 -07003153 quote! {
3154 extern "C" void may_throw();
3155 }
3156 );
3157 });
3158 }
3159
Lukasz Anforowicze465b9e2023-02-06 15:00:25 -08003160 /// This test mainly verifies that `format_item` correctly propagates
Lukasz Anforowicz5c9c00d2022-12-02 08:41:39 -08003161 /// `CcPrerequisites` of parameter types and return type.
3162 #[test]
Lukasz Anforowicze465b9e2023-02-06 15:00:25 -08003163 fn test_format_item_fn_cc_prerequisites_if_cpp_definition_needed() {
Lukasz Anforowicz5c9c00d2022-12-02 08:41:39 -08003164 let test_src = r#"
Lukasz Anforowicz5c9c00d2022-12-02 08:41:39 -08003165 pub fn foo(_i: i32) -> S { panic!("foo") }
Lukasz Anforowicz7a7b81f2023-01-12 15:50:46 -08003166 pub struct S(i32);
Lukasz Anforowicz5c9c00d2022-12-02 08:41:39 -08003167 "#;
Lukasz Anforowicze465b9e2023-02-06 15:00:25 -08003168 test_format_item(test_src, "foo", |result| {
Lukasz Anforowicz73a2c0d2023-03-22 14:07:00 -07003169 let result = result.unwrap().unwrap();
3170 let main_api = &result.main_api;
Lukasz Anforowicz5c9c00d2022-12-02 08:41:39 -08003171
3172 // Minimal coverage, just to double-check that the test setup works.
Lukasz Anforowicz7a7b81f2023-01-12 15:50:46 -08003173 //
3174 // Note that this is a definition, and therefore `S` should be defined
3175 // earlier (not just forward declared).
Lukasz Anforowicz6753d402023-02-10 16:41:23 -08003176 assert_cc_matches!(main_api.tokens, quote! { S foo(std::int32_t _i);});
Lukasz Anforowicz73a2c0d2023-03-22 14:07:00 -07003177 assert_cc_matches!(result.cc_details.tokens, quote! { S foo(std::int32_t _i) { ... }});
Lukasz Anforowicz5c9c00d2022-12-02 08:41:39 -08003178
3179 // Main checks: `CcPrerequisites::includes`.
3180 assert_cc_matches!(
Lukasz Anforowicz6753d402023-02-10 16:41:23 -08003181 format_cc_includes(&main_api.prereqs.includes),
3182 quote! { include <cstdint> }
3183 );
3184 assert_cc_matches!(
Lukasz Anforowicz73a2c0d2023-03-22 14:07:00 -07003185 format_cc_includes(&result.cc_details.prereqs.includes),
Lukasz Anforowicz5c9c00d2022-12-02 08:41:39 -08003186 quote! { include <cstdint> }
3187 );
Lukasz Anforowicz7a7b81f2023-01-12 15:50:46 -08003188
3189 // Main checks: `CcPrerequisites::defs` and `CcPrerequisites::fwd_decls`.
Lukasz Anforowicz5c9c00d2022-12-02 08:41:39 -08003190 //
Lukasz Anforowicze465b9e2023-02-06 15:00:25 -08003191 // Verifying the actual def_id is tricky, because `test_format_item` doesn't
Lukasz Anforowicz5c9c00d2022-12-02 08:41:39 -08003192 // expose `tcx` to the verification function (and therefore calling
3193 // `find_def_id_by_name` is not easily possible).
Lukasz Anforowicz6753d402023-02-10 16:41:23 -08003194 //
3195 // Note that `main_api` and `impl_details` have different expectations.
3196 assert_eq!(0, main_api.prereqs.defs.len());
3197 assert_eq!(1, main_api.prereqs.fwd_decls.len());
Lukasz Anforowicz73a2c0d2023-03-22 14:07:00 -07003198 assert_eq!(1, result.cc_details.prereqs.defs.len());
3199 assert_eq!(0, result.cc_details.prereqs.fwd_decls.len());
Lukasz Anforowicz7a7b81f2023-01-12 15:50:46 -08003200 });
3201 }
3202
Lukasz Anforowicze465b9e2023-02-06 15:00:25 -08003203 /// This test verifies that `format_item` uses `CcPrerequisites::fwd_decls`
Lukasz Anforowicz10b1a292023-04-03 16:19:08 -07003204 /// rather than `CcPrerequisites::defs` for function declarations in the
3205 /// `main_api`.
Lukasz Anforowicz7a7b81f2023-01-12 15:50:46 -08003206 #[test]
Lukasz Anforowicze465b9e2023-02-06 15:00:25 -08003207 fn test_format_item_fn_cc_prerequisites_if_only_cpp_declaration_needed() {
Lukasz Anforowicz7a7b81f2023-01-12 15:50:46 -08003208 let test_src = r#"
Lukasz Anforowicz7a7b81f2023-01-12 15:50:46 -08003209 #[no_mangle]
3210 pub extern "C" fn foo(s: S) -> bool { s.0 }
3211
3212 #[repr(C)]
3213 pub struct S(bool);
3214 "#;
Lukasz Anforowicze465b9e2023-02-06 15:00:25 -08003215 test_format_item(test_src, "foo", |result| {
Lukasz Anforowicz73a2c0d2023-03-22 14:07:00 -07003216 let result = result.unwrap().unwrap();
3217 let main_api = &result.main_api;
Lukasz Anforowicz7a7b81f2023-01-12 15:50:46 -08003218
3219 // Minimal coverage, just to double-check that the test setup works.
3220 //
3221 // Note that this is only a function *declaration* (not a function definition -
3222 // there is no function body), and therefore `S` just needs to be
3223 // forward-declared earlier.
Lukasz Anforowiczb9e515a2023-07-05 12:48:22 -07003224 assert_cc_matches!(main_api.tokens, quote! { bool foo(::rust_out::S s); });
Lukasz Anforowicz7a7b81f2023-01-12 15:50:46 -08003225
3226 // Main checks: `CcPrerequisites::defs` and `CcPrerequisites::fwd_decls`.
3227 //
Lukasz Anforowicze465b9e2023-02-06 15:00:25 -08003228 // Verifying the actual def_id is tricky, because `test_format_item` doesn't
Lukasz Anforowicz7a7b81f2023-01-12 15:50:46 -08003229 // expose `tcx` to the verification function (and therefore calling
3230 // `find_def_id_by_name` is not easily possible).
Lukasz Anforowicz0b8a02f2023-02-10 15:55:40 -08003231 assert_eq!(0, main_api.prereqs.defs.len());
3232 assert_eq!(1, main_api.prereqs.fwd_decls.len());
Lukasz Anforowicz5c9c00d2022-12-02 08:41:39 -08003233 });
3234 }
3235
Lukasz Anforowicze4333062022-10-17 14:47:53 -07003236 #[test]
Lukasz Anforowicze465b9e2023-02-06 15:00:25 -08003237 fn test_format_item_fn_with_type_aliased_return_type() {
Lukasz Anforowicze4333062022-10-17 14:47:53 -07003238 // Type aliases disappear at the `rustc_middle::ty::Ty` level and therefore in
3239 // the short-term the generated bindings also ignore type aliases.
3240 //
3241 // TODO(b/254096006): Consider preserving `type` aliases when generating
3242 // bindings.
3243 let test_src = r#"
Lukasz Anforowicz4f9f5522022-10-17 15:03:37 -07003244 type MyTypeAlias = f64;
Lukasz Anforowicze4333062022-10-17 14:47:53 -07003245
Lukasz Anforowicz7123f212022-10-20 08:51:04 -07003246 #[no_mangle]
Lukasz Anforowicz4f9f5522022-10-17 15:03:37 -07003247 pub extern "C" fn type_aliased_return() -> MyTypeAlias { 42.0 }
Lukasz Anforowicze4333062022-10-17 14:47:53 -07003248 "#;
Lukasz Anforowicze465b9e2023-02-06 15:00:25 -08003249 test_format_item(test_src, "type_aliased_return", |result| {
Lukasz Anforowicz73a2c0d2023-03-22 14:07:00 -07003250 let result = result.unwrap().unwrap();
3251 let main_api = &result.main_api;
Lukasz Anforowicz0b8a02f2023-02-10 15:55:40 -08003252 assert!(main_api.prereqs.is_empty());
Lukasz Anforowicze4333062022-10-17 14:47:53 -07003253 assert_cc_matches!(
Lukasz Anforowicz0b8a02f2023-02-10 15:55:40 -08003254 main_api.tokens,
Lukasz Anforowicze4333062022-10-17 14:47:53 -07003255 quote! {
Lukasz Anforowicz4f9f5522022-10-17 15:03:37 -07003256 extern "C" double type_aliased_return();
Lukasz Anforowicze4333062022-10-17 14:47:53 -07003257 }
3258 );
3259 });
3260 }
3261
3262 #[test]
Lukasz Anforowicze465b9e2023-02-06 15:00:25 -08003263 fn test_format_item_fn_with_doc_comment_with_unmangled_name() {
Googler624580b2022-12-01 01:23:42 -08003264 let test_src = r#"
3265 /// Outer line doc.
3266 /** Outer block doc that spans lines.
3267 */
3268 #[doc = "Doc comment via doc attribute."]
3269 #[no_mangle]
3270 pub extern "C" fn fn_with_doc_comment_with_unmangled_name() {}
3271 "#;
Lukasz Anforowicze465b9e2023-02-06 15:00:25 -08003272 test_format_item(test_src, "fn_with_doc_comment_with_unmangled_name", |result| {
Lukasz Anforowicz73a2c0d2023-03-22 14:07:00 -07003273 let result = result.unwrap().unwrap();
3274 let main_api = &result.main_api;
Lukasz Anforowicz0b8a02f2023-02-10 15:55:40 -08003275 assert!(main_api.prereqs.is_empty());
Googler624580b2022-12-01 01:23:42 -08003276 let doc_comments = [
3277 " Outer line doc.",
Googler34f3d572022-12-02 00:53:37 -08003278 "",
Googler624580b2022-12-01 01:23:42 -08003279 " Outer block doc that spans lines.",
3280 " ",
Googler34f3d572022-12-02 00:53:37 -08003281 "",
Googler624580b2022-12-01 01:23:42 -08003282 "Doc comment via doc attribute.",
Googler47fd9572023-01-20 09:57:32 -08003283 "",
3284 "Generated from: <crubit_unittests.rs>;l=7",
Googler624580b2022-12-01 01:23:42 -08003285 ]
3286 .join("\n");
3287 assert_cc_matches!(
Lukasz Anforowicz0b8a02f2023-02-10 15:55:40 -08003288 main_api.tokens,
Googler624580b2022-12-01 01:23:42 -08003289 quote! {
3290 __COMMENT__ #doc_comments
3291 extern "C" void fn_with_doc_comment_with_unmangled_name();
3292 }
3293 );
3294 });
3295 }
3296
3297 #[test]
Lukasz Anforowicze465b9e2023-02-06 15:00:25 -08003298 fn test_format_item_fn_with_inner_doc_comment_with_unmangled_name() {
Googler624580b2022-12-01 01:23:42 -08003299 let test_src = r#"
3300 /// Outer doc comment.
3301 #[no_mangle]
3302 pub extern "C" fn fn_with_inner_doc_comment_with_unmangled_name() {
3303 //! Inner doc comment.
3304 }
3305 "#;
Lukasz Anforowicze465b9e2023-02-06 15:00:25 -08003306 test_format_item(test_src, "fn_with_inner_doc_comment_with_unmangled_name", |result| {
Lukasz Anforowicz73a2c0d2023-03-22 14:07:00 -07003307 let result = result.unwrap().unwrap();
3308 let main_api = &result.main_api;
Lukasz Anforowicz0b8a02f2023-02-10 15:55:40 -08003309 assert!(main_api.prereqs.is_empty());
Googler47fd9572023-01-20 09:57:32 -08003310 let doc_comments = [
3311 " Outer doc comment.",
3312 " Inner doc comment.",
3313 "Generated from: <crubit_unittests.rs>;l=4",
3314 ]
3315 .join("\n\n");
Googler624580b2022-12-01 01:23:42 -08003316 assert_cc_matches!(
Lukasz Anforowicz0b8a02f2023-02-10 15:55:40 -08003317 main_api.tokens,
Googler624580b2022-12-01 01:23:42 -08003318 quote! {
3319 __COMMENT__ #doc_comments
3320 extern "C" void fn_with_inner_doc_comment_with_unmangled_name();
3321 }
3322 );
3323 });
3324 }
3325
3326 #[test]
Lukasz Anforowicze465b9e2023-02-06 15:00:25 -08003327 fn test_format_item_fn_with_doc_comment_with_mangled_name() {
Googler624580b2022-12-01 01:23:42 -08003328 let test_src = r#"
3329 /// Doc comment of a function with mangled name.
3330 pub extern "C" fn fn_with_doc_comment_with_mangled_name() {}
3331 "#;
Lukasz Anforowicze465b9e2023-02-06 15:00:25 -08003332 test_format_item(test_src, "fn_with_doc_comment_with_mangled_name", |result| {
Lukasz Anforowicz73a2c0d2023-03-22 14:07:00 -07003333 let result = result.unwrap().unwrap();
3334 let main_api = &result.main_api;
Lukasz Anforowicz6753d402023-02-10 16:41:23 -08003335 assert!(main_api.prereqs.is_empty());
Googler47fd9572023-01-20 09:57:32 -08003336 let comment = " Doc comment of a function with mangled name.\n\n\
3337 Generated from: <crubit_unittests.rs>;l=3";
Googler624580b2022-12-01 01:23:42 -08003338 assert_cc_matches!(
Lukasz Anforowicz6753d402023-02-10 16:41:23 -08003339 main_api.tokens,
Googler624580b2022-12-01 01:23:42 -08003340 quote! {
Googler624580b2022-12-01 01:23:42 -08003341 __COMMENT__ #comment
Lukasz Anforowiczb9e515a2023-07-05 12:48:22 -07003342 void fn_with_doc_comment_with_mangled_name();
Googler624580b2022-12-01 01:23:42 -08003343 }
3344 );
3345 });
3346 }
3347
3348 #[test]
Lukasz Anforowicze465b9e2023-02-06 15:00:25 -08003349 fn test_format_item_unsupported_fn_name_is_reserved_cpp_keyword() {
Lukasz Anforowicze4333062022-10-17 14:47:53 -07003350 let test_src = r#"
Lukasz Anforowicz7123f212022-10-20 08:51:04 -07003351 #[no_mangle]
Lukasz Anforowicze4333062022-10-17 14:47:53 -07003352 pub extern "C" fn reinterpret_cast() -> () {}
3353 "#;
Lukasz Anforowicze465b9e2023-02-06 15:00:25 -08003354 test_format_item(test_src, "reinterpret_cast", |result| {
Lukasz Anforowicz4c19ad92022-12-16 15:23:14 -08003355 let err = result.unwrap_err();
Lukasz Anforowicze4333062022-10-17 14:47:53 -07003356 assert_eq!(
3357 err,
3358 "Error formatting function name: \
3359 `reinterpret_cast` is a C++ reserved keyword \
3360 and can't be used as a C++ identifier"
3361 );
3362 });
3363 }
3364
3365 #[test]
Lukasz Anforowicze465b9e2023-02-06 15:00:25 -08003366 fn test_format_item_unsupported_fn_ret_type() {
Lukasz Anforowicze4333062022-10-17 14:47:53 -07003367 let test_src = r#"
Lukasz Anforowiczeb58a492023-01-07 08:25:48 -08003368 pub fn foo() -> (i32, i32) { (123, 456) }
Lukasz Anforowicze4333062022-10-17 14:47:53 -07003369 "#;
Lukasz Anforowicze465b9e2023-02-06 15:00:25 -08003370 test_format_item(test_src, "foo", |result| {
Lukasz Anforowicz4c19ad92022-12-16 15:23:14 -08003371 let err = result.unwrap_err();
Lukasz Anforowicze4333062022-10-17 14:47:53 -07003372 assert_eq!(
3373 err,
3374 "Error formatting function return type: \
Lukasz Anforowiczeb58a492023-01-07 08:25:48 -08003375 Tuples are not supported yet: (i32, i32) (b/254099023)"
Lukasz Anforowicze4333062022-10-17 14:47:53 -07003376 );
3377 });
3378 }
3379
Lukasz Anforowiczaa2de7e2023-06-15 11:32:05 -07003380 /// This test verifies handling of inferred, anonymous lifetimes.
3381 ///
3382 /// Note that `Region::get_name_or_anon()` may return the same name (e.g.
3383 /// `"anon"` for both lifetimes, but bindings should use 2 distinct
3384 /// lifetime names in the generated bindings and in the thunk impl.
Lukasz Anforowicze4333062022-10-17 14:47:53 -07003385 #[test]
Lukasz Anforowiczaa2de7e2023-06-15 11:32:05 -07003386 fn test_format_item_lifetime_generic_fn_with_inferred_lifetimes() {
Lukasz Anforowicze4333062022-10-17 14:47:53 -07003387 let test_src = r#"
Lukasz Anforowiczaa2de7e2023-06-15 11:32:05 -07003388 pub fn foo(arg: &i32) -> &i32 {
3389 unimplemented!("arg = {arg}")
3390 }
3391 "#;
3392 test_format_item(test_src, "foo", |result| {
3393 let result = result.unwrap().unwrap();
3394 let main_api = &result.main_api;
3395 assert_cc_matches!(
3396 main_api.tokens,
3397 quote! {
Lukasz Anforowiczaa2de7e2023-06-15 11:32:05 -07003398 std::int32_t const& [[clang::annotate_type("lifetime", "__anon1")]]
3399 foo(std::int32_t const& [[clang::annotate_type("lifetime", "__anon1")]] arg);
3400 }
3401 );
3402 assert_cc_matches!(
3403 result.cc_details.tokens,
3404 quote! {
3405 namespace __crubit_internal {
3406 extern "C"
3407 std::int32_t const& [[clang::annotate_type("lifetime", "__anon1")]] ...(
3408 std::int32_t const& [[clang::annotate_type("lifetime", "__anon1")]]);
3409 }
3410 inline
3411 std::int32_t const& [[clang::annotate_type("lifetime", "__anon1")]]
3412 foo(std::int32_t const& [[clang::annotate_type("lifetime", "__anon1")]] arg) {
3413 return __crubit_internal::...(arg);
3414 }
3415 }
3416 );
3417 assert_rs_matches!(
3418 result.rs_details,
3419 quote! {
3420 #[no_mangle]
3421 extern "C" fn ...<'__anon1>(arg: &'__anon1 i32) -> &'__anon1 i32 {
3422 ::rust_out::foo(arg)
3423 }
3424 }
3425 );
3426 });
3427 }
Lukasz Anforowicze4333062022-10-17 14:47:53 -07003428
Lukasz Anforowiczaa2de7e2023-06-15 11:32:05 -07003429 /// This test verifies handling of various explicit (i.e. non-inferred)
3430 /// lifetimes.
3431 ///
3432 /// * Note that the two `'_` specify two distinct lifetimes (i.e. two
3433 /// distinct names need to be used in the generated bindings and thunk
3434 /// impl).
3435 /// * Note that `'static` doesn't need to be listed in the generic
3436 /// parameters of the thunk impl
3437 /// * Note that even though `'foo` is used in 2 parameter types, it should
3438 /// only appear once in the list of generic parameters of the thunk impl
3439 /// * Note that in the future the following translation may be preferable:
3440 /// * `'a` => `$a` (no parens)
3441 /// * `'foo` => `$(foo)` (note the extra parens)
3442 #[test]
3443 fn test_format_item_lifetime_generic_fn_with_various_lifetimes() {
3444 let test_src = r#"
3445 pub fn foo<'a, 'foo>(
3446 arg1: &'a i32, // Single letter lifetime = `$a` is possible
3447 arg2: &'foo i32, // Multi-character lifetime
3448 arg3: &'foo i32, // Same lifetime used for 2 places
3449 arg4: &'static i32,
3450 arg5: &'_ i32,
3451 arg6: &'_ i32,
3452 ) -> &'foo i32 {
3453 unimplemented!("args: {arg1}, {arg2}, {arg3}, {arg4}, {arg5}, {arg6}")
3454 }
3455 "#;
3456 test_format_item(test_src, "foo", |result| {
3457 let result = result.unwrap().unwrap();
3458 let main_api = &result.main_api;
3459 assert_cc_matches!(
3460 main_api.tokens,
3461 quote! {
Lukasz Anforowiczaa2de7e2023-06-15 11:32:05 -07003462 std::int32_t const& [[clang::annotate_type("lifetime", "foo")]]
3463 foo(
3464 std::int32_t const& [[clang::annotate_type("lifetime", "a")]] arg1,
3465 std::int32_t const& [[clang::annotate_type("lifetime", "foo")]] arg2,
3466 std::int32_t const& [[clang::annotate_type("lifetime", "foo")]] arg3,
3467 std::int32_t const& [[clang::annotate_type("lifetime", "static")]] arg4,
3468 std::int32_t const& [[clang::annotate_type("lifetime", "__anon1")]] arg5,
3469 std::int32_t const& [[clang::annotate_type("lifetime", "__anon2")]] arg6);
3470 }
3471 );
3472 assert_cc_matches!(
3473 result.cc_details.tokens,
3474 quote! {
3475 namespace __crubit_internal {
3476 extern "C"
3477 std::int32_t const& [[clang::annotate_type("lifetime", "foo")]]
3478 ...(
3479 std::int32_t const& [[clang::annotate_type("lifetime", "a")]],
3480 std::int32_t const& [[clang::annotate_type("lifetime", "foo")]],
3481 std::int32_t const& [[clang::annotate_type("lifetime", "foo")]],
3482 std::int32_t const& [[clang::annotate_type("lifetime", "static")]],
3483 std::int32_t const& [[clang::annotate_type("lifetime", "__anon1")]],
3484 std::int32_t const& [[clang::annotate_type("lifetime", "__anon2")]]);
3485 }
3486 inline
3487 std::int32_t const& [[clang::annotate_type("lifetime", "foo")]]
3488 foo(
3489 std::int32_t const& [[clang::annotate_type("lifetime", "a")]] arg1,
3490 std::int32_t const& [[clang::annotate_type("lifetime", "foo")]] arg2,
3491 std::int32_t const& [[clang::annotate_type("lifetime", "foo")]] arg3,
3492 std::int32_t const& [[clang::annotate_type("lifetime", "static")]] arg4,
3493 std::int32_t const& [[clang::annotate_type("lifetime", "__anon1")]] arg5,
3494 std::int32_t const& [[clang::annotate_type("lifetime", "__anon2")]] arg6) {
3495 return __crubit_internal::...(arg1, arg2, arg3, arg4, arg5, arg6);
3496 }
3497 }
3498 );
3499 assert_rs_matches!(
3500 result.rs_details,
3501 quote! {
3502 #[no_mangle]
3503 extern "C" fn ...<'a, 'foo, '__anon1, '__anon2>(
3504 arg1: &'a i32,
3505 arg2: &'foo i32,
3506 arg3: &'foo i32,
3507 arg4: &'static i32,
3508 arg5: &'__anon1 i32,
3509 arg6: &'__anon2 i32
3510 ) -> &'foo i32 {
3511 ::rust_out::foo(arg1, arg2, arg3, arg4, arg5, arg6)
3512 }
3513 }
3514 );
3515 });
3516 }
3517
3518 /// Test of lifetime-generic function with a `where` clause.
3519 ///
3520 /// The `where` constraint below is a bit silly (why not just use `'static`
3521 /// directly), but it seems prudent to test and confirm that we disable
3522 /// generation of bindings for generic functions with `where` clauses
3523 /// (because it is unclear if such constraints can be replicated
3524 /// in C++).
3525 #[test]
3526 fn test_format_item_lifetime_generic_fn_with_where_clause() {
3527 let test_src = r#"
3528 pub fn foo<'a>(arg: &'a i32) where 'a : 'static {
3529 unimplemented!("{arg}")
3530 }
Lukasz Anforowicze4333062022-10-17 14:47:53 -07003531 "#;
Lukasz Anforowicze465b9e2023-02-06 15:00:25 -08003532 test_format_item(test_src, "foo", |result| {
Lukasz Anforowicz4c19ad92022-12-16 15:23:14 -08003533 let err = result.unwrap_err();
Lukasz Anforowicz56d94fc2023-02-17 12:23:20 -08003534 assert_eq!(err, "Generic functions are not supported yet (b/259749023)");
Lukasz Anforowicze4333062022-10-17 14:47:53 -07003535 });
3536 }
3537
3538 #[test]
Lukasz Anforowiczed96fcb2023-06-12 11:39:45 -07003539 fn test_format_item_unsupported_type_generic_fn() {
Lukasz Anforowicze4333062022-10-17 14:47:53 -07003540 let test_src = r#"
3541 use std::default::Default;
3542 use std::fmt::Display;
3543 pub fn generic_function<T: Default + Display>() {
3544 println!("{}", T::default());
3545 }
3546 "#;
Lukasz Anforowicze465b9e2023-02-06 15:00:25 -08003547 test_format_item(test_src, "generic_function", |result| {
Lukasz Anforowicz4c19ad92022-12-16 15:23:14 -08003548 let err = result.unwrap_err();
Lukasz Anforowicz56d94fc2023-02-17 12:23:20 -08003549 assert_eq!(err, "Generic functions are not supported yet (b/259749023)");
Lukasz Anforowicz8a833412022-10-14 14:44:13 -07003550 });
3551 }
3552
3553 #[test]
Lukasz Anforowiczed96fcb2023-06-12 11:39:45 -07003554 fn test_format_item_unsupported_type_generic_struct() {
Lukasz Anforowicz27914f52022-11-08 10:55:03 -08003555 let test_src = r#"
3556 pub struct Point<T> {
3557 pub x: T,
3558 pub y: T,
3559 }
3560 "#;
Lukasz Anforowicze465b9e2023-02-06 15:00:25 -08003561 test_format_item(test_src, "Point", |result| {
Lukasz Anforowicz4c19ad92022-12-16 15:23:14 -08003562 let err = result.unwrap_err();
Lukasz Anforowicz56d94fc2023-02-17 12:23:20 -08003563 assert_eq!(err, "Generic types are not supported yet (b/259749095)");
Lukasz Anforowicz27914f52022-11-08 10:55:03 -08003564 });
3565 }
3566
3567 #[test]
Lukasz Anforowiczed96fcb2023-06-12 11:39:45 -07003568 fn test_format_item_unsupported_lifetime_generic_struct() {
3569 let test_src = r#"
3570 pub struct Point<'a> {
3571 pub x: &'a i32,
3572 pub y: &'a i32,
3573 }
3574
3575 impl<'a> Point<'a> {
3576 // Some lifetimes are bound at the `impl` / `struct` level (the lifetime is
3577 // hidden underneath the `Self` type), and some at the `fn` level.
3578 pub fn new<'b, 'c>(_x: &'b i32, _y: &'c i32) -> Self { unimplemented!() }
3579 }
3580 "#;
3581 test_format_item(test_src, "Point", |result| {
3582 let err = result.unwrap_err();
3583 assert_eq!(err, "Generic types are not supported yet (b/259749095)");
3584 });
3585 }
3586
3587 #[test]
Lukasz Anforowicze465b9e2023-02-06 15:00:25 -08003588 fn test_format_item_unsupported_generic_enum() {
Lukasz Anforowicz27914f52022-11-08 10:55:03 -08003589 let test_src = r#"
3590 pub enum Point<T> {
3591 Cartesian{x: T, y: T},
3592 Polar{angle: T, dist: T},
3593 }
3594 "#;
Lukasz Anforowicze465b9e2023-02-06 15:00:25 -08003595 test_format_item(test_src, "Point", |result| {
Lukasz Anforowicz4c19ad92022-12-16 15:23:14 -08003596 let err = result.unwrap_err();
Lukasz Anforowicz56d94fc2023-02-17 12:23:20 -08003597 assert_eq!(err, "Generic types are not supported yet (b/259749095)");
Lukasz Anforowicz27914f52022-11-08 10:55:03 -08003598 });
3599 }
3600
3601 #[test]
Lukasz Anforowicze465b9e2023-02-06 15:00:25 -08003602 fn test_format_item_unsupported_generic_union() {
Lukasz Anforowicz27914f52022-11-08 10:55:03 -08003603 let test_src = r#"
3604 pub union SomeUnion<T> {
3605 pub x: std::mem::ManuallyDrop<T>,
3606 pub y: i32,
3607 }
3608 "#;
Lukasz Anforowicze465b9e2023-02-06 15:00:25 -08003609 test_format_item(test_src, "SomeUnion", |result| {
Lukasz Anforowicz4c19ad92022-12-16 15:23:14 -08003610 let err = result.unwrap_err();
Lukasz Anforowicz56d94fc2023-02-17 12:23:20 -08003611 assert_eq!(err, "Generic types are not supported yet (b/259749095)");
Lukasz Anforowicz27914f52022-11-08 10:55:03 -08003612 });
3613 }
3614
3615 #[test]
Lukasz Anforowicze465b9e2023-02-06 15:00:25 -08003616 fn test_format_item_unsupported_fn_async() {
Lukasz Anforowicz8a833412022-10-14 14:44:13 -07003617 let test_src = r#"
Lukasz Anforowicze4333062022-10-17 14:47:53 -07003618 pub async fn async_function() {}
Lukasz Anforowicz8a833412022-10-14 14:44:13 -07003619 "#;
Lukasz Anforowicze465b9e2023-02-06 15:00:25 -08003620 test_format_item(test_src, "async_function", |result| {
Lukasz Anforowicz4c19ad92022-12-16 15:23:14 -08003621 let err = result.unwrap_err();
Devin Jeanpierre81aec502023-04-04 15:33:39 -07003622 assert_eq!(
3623 err,
3624 "Error formatting function return type: \
Lukasz Anforowicz2f849cf2022-11-17 15:52:39 -08003625 The following Rust type is not supported yet: \
Devin Jeanpierre81aec502023-04-04 15:33:39 -07003626 impl std::future::Future<Output = ()>"
3627 );
Lukasz Anforowicze4333062022-10-17 14:47:53 -07003628 });
3629 }
3630
3631 #[test]
Lukasz Anforowicze465b9e2023-02-06 15:00:25 -08003632 fn test_format_item_fn_rust_abi() {
Lukasz Anforowicze4333062022-10-17 14:47:53 -07003633 let test_src = r#"
Lukasz Anforowicz2f849cf2022-11-17 15:52:39 -08003634 pub fn add(x: f64, y: f64) -> f64 { x * y }
Lukasz Anforowicze4333062022-10-17 14:47:53 -07003635 "#;
Lukasz Anforowicze465b9e2023-02-06 15:00:25 -08003636 test_format_item(test_src, "add", |result| {
Devin Jeanpierre81aec502023-04-04 15:33:39 -07003637 // TODO(b/261074843): Re-add thunk name verification once we are using stable
3638 // name mangling (which may be coming in Q1 2023). (This might mean
3639 // reverting cl/492333432 + manual review and tweaks.)
Lukasz Anforowicz73a2c0d2023-03-22 14:07:00 -07003640 let result = result.unwrap().unwrap();
3641 let main_api = &result.main_api;
Lukasz Anforowicz6753d402023-02-10 16:41:23 -08003642 assert!(main_api.prereqs.is_empty());
Lukasz Anforowicz2f849cf2022-11-17 15:52:39 -08003643 assert_cc_matches!(
Lukasz Anforowicz6753d402023-02-10 16:41:23 -08003644 main_api.tokens,
3645 quote! {
Lukasz Anforowiczb9e515a2023-07-05 12:48:22 -07003646 double add(double x, double y);
Lukasz Anforowicz6753d402023-02-10 16:41:23 -08003647 }
3648 );
Lukasz Anforowicz73a2c0d2023-03-22 14:07:00 -07003649 assert!(result.cc_details.prereqs.is_empty());
Lukasz Anforowicz6753d402023-02-10 16:41:23 -08003650 assert_cc_matches!(
Lukasz Anforowicz73a2c0d2023-03-22 14:07:00 -07003651 result.cc_details.tokens,
Lukasz Anforowicz2f849cf2022-11-17 15:52:39 -08003652 quote! {
Lukasz Anforowiczbad99632022-11-18 16:39:13 -08003653 namespace __crubit_internal {
Lukasz Anforowicz73edf152023-04-04 12:05:00 -07003654 extern "C" double ...(double, double);
Lukasz Anforowiczbad99632022-11-18 16:39:13 -08003655 }
Googler47fd9572023-01-20 09:57:32 -08003656 ...
Lukasz Anforowicz2f849cf2022-11-17 15:52:39 -08003657 inline double add(double x, double y) {
Lukasz Anforowiczb4beb392022-12-01 16:49:11 -08003658 return __crubit_internal::...(x, y);
Lukasz Anforowicz2f849cf2022-11-17 15:52:39 -08003659 }
3660 }
3661 );
Lukasz Anforowicz2f849cf2022-11-17 15:52:39 -08003662 assert_rs_matches!(
Lukasz Anforowicz73a2c0d2023-03-22 14:07:00 -07003663 result.rs_details,
Lukasz Anforowicz2f849cf2022-11-17 15:52:39 -08003664 quote! {
3665 #[no_mangle]
3666 extern "C"
Lukasz Anforowiczb4beb392022-12-01 16:49:11 -08003667 fn ...(x: f64, y: f64) -> f64 {
Lukasz Anforowicz8b475a42022-11-29 09:33:02 -08003668 ::rust_out::add(x, y)
Lukasz Anforowicz2f849cf2022-11-17 15:52:39 -08003669 }
3670 }
3671 );
3672 });
3673 }
3674
Lukasz Anforowicz52274992023-03-08 12:29:28 -08003675 #[test]
3676 fn test_format_item_fn_rust_abi_with_param_taking_struct_by_value() {
3677 let test_src = r#"
3678 pub struct S(i32);
3679 pub fn into_i32(s: S) -> i32 { s.0 }
3680 "#;
3681 test_format_item(test_src, "into_i32", |result| {
Lukasz Anforowicz73a2c0d2023-03-22 14:07:00 -07003682 let result = result.unwrap().unwrap();
3683 let main_api = &result.main_api;
Lukasz Anforowicz52274992023-03-08 12:29:28 -08003684 assert_cc_matches!(
3685 main_api.tokens,
3686 quote! {
Lukasz Anforowiczb9e515a2023-07-05 12:48:22 -07003687 std::int32_t into_i32(::rust_out::S s);
Lukasz Anforowicz52274992023-03-08 12:29:28 -08003688 }
3689 );
3690 assert_cc_matches!(
Lukasz Anforowicz73a2c0d2023-03-22 14:07:00 -07003691 result.cc_details.tokens,
Lukasz Anforowicz52274992023-03-08 12:29:28 -08003692 quote! {
3693 namespace __crubit_internal {
Lukasz Anforowicz73edf152023-04-04 12:05:00 -07003694 extern "C" std::int32_t ...(::rust_out::S*);
Lukasz Anforowicz52274992023-03-08 12:29:28 -08003695 }
3696 ...
3697 inline std::int32_t into_i32(::rust_out::S s) {
Lukasz Anforowiczd082f352023-03-09 17:46:11 -08003698 return __crubit_internal::...(&s);
Lukasz Anforowicz52274992023-03-08 12:29:28 -08003699 }
3700 }
3701 );
3702 assert_rs_matches!(
Lukasz Anforowicz73a2c0d2023-03-22 14:07:00 -07003703 result.rs_details,
Lukasz Anforowicz52274992023-03-08 12:29:28 -08003704 quote! {
3705 #[no_mangle]
3706 extern "C"
Lukasz Anforowiczd082f352023-03-09 17:46:11 -08003707 fn ...(s: &mut ::core::mem::MaybeUninit<::rust_out::S>) -> i32 {
3708 ::rust_out::into_i32(unsafe { s.assume_init_read() })
Lukasz Anforowicz52274992023-03-08 12:29:28 -08003709 }
3710 }
3711 );
3712 });
3713 }
3714
3715 #[test]
3716 fn test_format_item_fn_rust_abi_returning_struct_by_value() {
3717 let test_src = r#"
3718 pub struct S(i32);
3719 pub fn create(i: i32) -> S { S(i) }
3720 "#;
3721 test_format_item(test_src, "create", |result| {
Lukasz Anforowicz73a2c0d2023-03-22 14:07:00 -07003722 let result = result.unwrap().unwrap();
3723 let main_api = &result.main_api;
Lukasz Anforowicz52274992023-03-08 12:29:28 -08003724 assert_cc_matches!(
3725 main_api.tokens,
3726 quote! {
Lukasz Anforowiczb9e515a2023-07-05 12:48:22 -07003727 ::rust_out::S create(std::int32_t i);
Lukasz Anforowicz52274992023-03-08 12:29:28 -08003728 }
3729 );
3730 assert_cc_matches!(
Lukasz Anforowicz73a2c0d2023-03-22 14:07:00 -07003731 result.cc_details.tokens,
Lukasz Anforowicz52274992023-03-08 12:29:28 -08003732 quote! {
3733 namespace __crubit_internal {
Lukasz Anforowicz73edf152023-04-04 12:05:00 -07003734 extern "C" void ...(std::int32_t, ::rust_out::S* __ret_ptr);
Lukasz Anforowicz52274992023-03-08 12:29:28 -08003735 }
3736 ...
3737 inline ::rust_out::S create(std::int32_t i) {
Lukasz Anforowicza3b7db02023-03-09 17:34:05 -08003738 crubit::ReturnValueSlot<::rust_out::S> __ret_slot;
3739 __crubit_internal::...(i, __ret_slot.Get());
3740 return std::move(__ret_slot).AssumeInitAndTakeValue();
Lukasz Anforowicz52274992023-03-08 12:29:28 -08003741 }
3742 }
3743 );
3744 assert_rs_matches!(
Lukasz Anforowicz73a2c0d2023-03-22 14:07:00 -07003745 result.rs_details,
Lukasz Anforowicz52274992023-03-08 12:29:28 -08003746 quote! {
3747 #[no_mangle]
3748 extern "C"
Lukasz Anforowicza3b7db02023-03-09 17:34:05 -08003749 fn ...(
3750 i: i32,
3751 __ret_slot: &mut ::core::mem::MaybeUninit<::rust_out::S>
3752 ) -> () {
3753 __ret_slot.write(::rust_out::create(i));
Lukasz Anforowicz52274992023-03-08 12:29:28 -08003754 }
3755 }
3756 );
3757 });
3758 }
3759
Lukasz Anforowicze465b9e2023-02-06 15:00:25 -08003760 /// `test_format_item_fn_rust_abi` tests a function call that is not a
3761 /// C-ABI, and is not the default Rust ABI. It can't use `"stdcall"`,
3762 /// because it is not supported on the targets where Crubit's tests run.
3763 /// So, it ended up using `"vectorcall"`.
Lukasz Anforowicz4c19ad92022-12-16 15:23:14 -08003764 ///
Lukasz Anforowicze465b9e2023-02-06 15:00:25 -08003765 /// This test almost entirely replicates `test_format_item_fn_rust_abi`,
Googler47fd9572023-01-20 09:57:32 -08003766 /// except for the `extern "vectorcall"` part in the `test_src` test
3767 /// input.
Lukasz Anforowicz4c19ad92022-12-16 15:23:14 -08003768 ///
Googler47fd9572023-01-20 09:57:32 -08003769 /// This test verifies the current behavior that gives reasonable and
3770 /// functional FFI bindings. OTOH, in the future we may decide to avoid
3771 /// having the extra thunk for cases where the given non-C-ABI function
3772 /// call convention is supported by both C++ and Rust
Lukasz Anforowicz4c19ad92022-12-16 15:23:14 -08003773 /// (see also `format_cc_call_conv_as_clang_attribute` in
3774 /// `rs_bindings_from_cc/src_code_gen.rs`)
Lukasz Anforowicz2f849cf2022-11-17 15:52:39 -08003775 #[test]
Lukasz Anforowicze465b9e2023-02-06 15:00:25 -08003776 fn test_format_item_fn_vectorcall_abi() {
Lukasz Anforowicz2f849cf2022-11-17 15:52:39 -08003777 let test_src = r#"
3778 #![feature(abi_vectorcall)]
3779 pub extern "vectorcall" fn add(x: f64, y: f64) -> f64 { x * y }
3780 "#;
Lukasz Anforowicze465b9e2023-02-06 15:00:25 -08003781 test_format_item(test_src, "add", |result| {
Lukasz Anforowicz73a2c0d2023-03-22 14:07:00 -07003782 let result = result.unwrap().unwrap();
3783 let main_api = &result.main_api;
Lukasz Anforowicz6753d402023-02-10 16:41:23 -08003784 assert!(main_api.prereqs.is_empty());
Lukasz Anforowicz2f849cf2022-11-17 15:52:39 -08003785 assert_cc_matches!(
Lukasz Anforowicz6753d402023-02-10 16:41:23 -08003786 main_api.tokens,
3787 quote! {
Lukasz Anforowiczb9e515a2023-07-05 12:48:22 -07003788 double add(double x, double y);
Lukasz Anforowicz6753d402023-02-10 16:41:23 -08003789 }
3790 );
Lukasz Anforowicz73a2c0d2023-03-22 14:07:00 -07003791 assert!(result.cc_details.prereqs.is_empty());
Lukasz Anforowicz6753d402023-02-10 16:41:23 -08003792 assert_cc_matches!(
Lukasz Anforowicz73a2c0d2023-03-22 14:07:00 -07003793 result.cc_details.tokens,
Lukasz Anforowicz2f849cf2022-11-17 15:52:39 -08003794 quote! {
Lukasz Anforowiczbad99632022-11-18 16:39:13 -08003795 namespace __crubit_internal {
Lukasz Anforowicz73edf152023-04-04 12:05:00 -07003796 extern "C" double ...(double, double);
Lukasz Anforowiczbad99632022-11-18 16:39:13 -08003797 }
Googler47fd9572023-01-20 09:57:32 -08003798 ...
Lukasz Anforowicz2f849cf2022-11-17 15:52:39 -08003799 inline double add(double x, double y) {
Lukasz Anforowiczb4beb392022-12-01 16:49:11 -08003800 return __crubit_internal::...(x, y);
Lukasz Anforowicz2f849cf2022-11-17 15:52:39 -08003801 }
3802 }
3803 );
Lukasz Anforowicz2f849cf2022-11-17 15:52:39 -08003804 assert_rs_matches!(
Lukasz Anforowicz73a2c0d2023-03-22 14:07:00 -07003805 result.rs_details,
Lukasz Anforowicz2f849cf2022-11-17 15:52:39 -08003806 quote! {
3807 #[no_mangle]
3808 extern "C"
Lukasz Anforowiczb4beb392022-12-01 16:49:11 -08003809 fn ...(x: f64, y: f64) -> f64 {
Lukasz Anforowicz8b475a42022-11-29 09:33:02 -08003810 ::rust_out::add(x, y)
Lukasz Anforowicz2f849cf2022-11-17 15:52:39 -08003811 }
3812 }
3813 );
3814 });
Lukasz Anforowicze4333062022-10-17 14:47:53 -07003815 }
3816
3817 #[test]
Lukasz Anforowicze465b9e2023-02-06 15:00:25 -08003818 fn test_format_item_unsupported_fn_variadic() {
Lukasz Anforowicze4333062022-10-17 14:47:53 -07003819 let test_src = r#"
3820 #![feature(c_variadic)]
Lukasz Anforowicz7123f212022-10-20 08:51:04 -07003821
3822 #[no_mangle]
Lukasz Anforowicze4333062022-10-17 14:47:53 -07003823 pub unsafe extern "C" fn variadic_function(_fmt: *const u8, ...) {}
3824 "#;
Lukasz Anforowicze465b9e2023-02-06 15:00:25 -08003825 test_format_item(test_src, "variadic_function", |result| {
Lukasz Anforowicz13794df2022-10-21 07:56:34 -07003826 // TODO(b/254097223): Add support for variadic functions.
Lukasz Anforowicz4c19ad92022-12-16 15:23:14 -08003827 let err = result.unwrap_err();
Lukasz Anforowicze4333062022-10-17 14:47:53 -07003828 assert_eq!(err, "C variadic functions are not supported (b/254097223)");
3829 });
3830 }
3831
3832 #[test]
Lukasz Anforowicze465b9e2023-02-06 15:00:25 -08003833 fn test_format_item_fn_params() {
Lukasz Anforowicz903fc632022-10-25 08:55:33 -07003834 let test_src = r#"
3835 #[allow(unused_variables)]
3836 #[no_mangle]
3837 pub extern "C" fn foo(b: bool, f: f64) {}
3838 "#;
Lukasz Anforowicze465b9e2023-02-06 15:00:25 -08003839 test_format_item(test_src, "foo", |result| {
Lukasz Anforowicz73a2c0d2023-03-22 14:07:00 -07003840 let result = result.unwrap().unwrap();
3841 let main_api = &result.main_api;
Lukasz Anforowicz0b8a02f2023-02-10 15:55:40 -08003842 assert!(main_api.prereqs.is_empty());
Lukasz Anforowicz903fc632022-10-25 08:55:33 -07003843 assert_cc_matches!(
Lukasz Anforowicz0b8a02f2023-02-10 15:55:40 -08003844 main_api.tokens,
Lukasz Anforowicz903fc632022-10-25 08:55:33 -07003845 quote! {
Googler47fd9572023-01-20 09:57:32 -08003846 ...
Lukasz Anforowicz903fc632022-10-25 08:55:33 -07003847 extern "C" void foo(bool b, double f);
3848 }
3849 );
3850 });
3851 }
3852
3853 #[test]
Lukasz Anforowicze465b9e2023-02-06 15:00:25 -08003854 fn test_format_item_fn_param_name_reserved_keyword() {
Lukasz Anforowicz903fc632022-10-25 08:55:33 -07003855 let test_src = r#"
3856 #[allow(unused_variables)]
3857 #[no_mangle]
3858 pub extern "C" fn some_function(reinterpret_cast: f64) {}
3859 "#;
Lukasz Anforowicze465b9e2023-02-06 15:00:25 -08003860 test_format_item(test_src, "some_function", |result| {
Lukasz Anforowicz73a2c0d2023-03-22 14:07:00 -07003861 let result = result.unwrap().unwrap();
3862 let main_api = &result.main_api;
Lukasz Anforowicz0b8a02f2023-02-10 15:55:40 -08003863 assert!(main_api.prereqs.is_empty());
Lukasz Anforowicz903fc632022-10-25 08:55:33 -07003864 assert_cc_matches!(
Lukasz Anforowicz0b8a02f2023-02-10 15:55:40 -08003865 main_api.tokens,
Lukasz Anforowicz903fc632022-10-25 08:55:33 -07003866 quote! {
Googler47fd9572023-01-20 09:57:32 -08003867 ...
Lukasz Anforowicz903fc632022-10-25 08:55:33 -07003868 extern "C" void some_function(double __param_0);
3869 }
3870 );
3871 });
3872 }
3873
3874 #[test]
Lukasz Anforowicze465b9e2023-02-06 15:00:25 -08003875 fn test_format_item_fn_with_multiple_anonymous_parameter_names() {
Lukasz Anforowiczc51aeb12022-11-07 10:56:18 -08003876 let test_src = r#"
Lukasz Anforowicz2f849cf2022-11-17 15:52:39 -08003877 pub fn foo(_: f64, _: f64) {}
Lukasz Anforowiczc51aeb12022-11-07 10:56:18 -08003878 "#;
Lukasz Anforowicze465b9e2023-02-06 15:00:25 -08003879 test_format_item(test_src, "foo", |result| {
Lukasz Anforowicz73a2c0d2023-03-22 14:07:00 -07003880 let result = result.unwrap().unwrap();
3881 let main_api = &result.main_api;
Lukasz Anforowicz6753d402023-02-10 16:41:23 -08003882 assert!(main_api.prereqs.is_empty());
Lukasz Anforowiczc51aeb12022-11-07 10:56:18 -08003883 assert_cc_matches!(
Lukasz Anforowicz6753d402023-02-10 16:41:23 -08003884 main_api.tokens,
3885 quote! {
Lukasz Anforowiczb9e515a2023-07-05 12:48:22 -07003886 void foo(double __param_0, double __param_1);
Lukasz Anforowicz6753d402023-02-10 16:41:23 -08003887 }
3888 );
Lukasz Anforowicz73a2c0d2023-03-22 14:07:00 -07003889 assert!(result.cc_details.prereqs.is_empty());
Lukasz Anforowicz6753d402023-02-10 16:41:23 -08003890 assert_cc_matches!(
Lukasz Anforowicz73a2c0d2023-03-22 14:07:00 -07003891 result.cc_details.tokens,
Lukasz Anforowiczc51aeb12022-11-07 10:56:18 -08003892 quote! {
Lukasz Anforowiczbad99632022-11-18 16:39:13 -08003893 namespace __crubit_internal {
Lukasz Anforowicz73edf152023-04-04 12:05:00 -07003894 extern "C" void ...(double, double);
Lukasz Anforowiczbad99632022-11-18 16:39:13 -08003895 }
Googler47fd9572023-01-20 09:57:32 -08003896 ...
Lukasz Anforowicz2f849cf2022-11-17 15:52:39 -08003897 inline void foo(double __param_0, double __param_1) {
Lukasz Anforowicz75894832022-11-22 18:25:28 -08003898 return __crubit_internal::...(__param_0, __param_1);
Lukasz Anforowicz2f849cf2022-11-17 15:52:39 -08003899 }
3900 }
3901 );
Lukasz Anforowicz2f849cf2022-11-17 15:52:39 -08003902 assert_rs_matches!(
Lukasz Anforowicz73a2c0d2023-03-22 14:07:00 -07003903 result.rs_details,
Lukasz Anforowicz2f849cf2022-11-17 15:52:39 -08003904 quote! {
3905 #[no_mangle]
3906 extern "C" fn ...(__param_0: f64, __param_1: f64) -> () {
Lukasz Anforowicz8b475a42022-11-29 09:33:02 -08003907 ::rust_out::foo(__param_0, __param_1)
Lukasz Anforowicz2f849cf2022-11-17 15:52:39 -08003908 }
Lukasz Anforowiczc51aeb12022-11-07 10:56:18 -08003909 }
3910 );
3911 });
3912 }
3913
Lukasz Anforowiczc51aeb12022-11-07 10:56:18 -08003914 #[test]
Lukasz Anforowicze465b9e2023-02-06 15:00:25 -08003915 fn test_format_item_fn_with_destructuring_parameter_name() {
Lukasz Anforowicz75894832022-11-22 18:25:28 -08003916 let test_src = r#"
3917 pub struct S {
3918 pub f1: i32,
3919 pub f2: i32,
3920 }
3921
3922 // This test mostly focuses on the weird parameter "name" below.
3923 // See also
3924 // https://doc.rust-lang.org/reference/items/functions.html#function-parameters
3925 // which points out that function parameters are just irrefutable patterns.
3926 pub fn func(S{f1, f2}: S) -> i32 { f1 + f2 }
3927 "#;
Lukasz Anforowicze465b9e2023-02-06 15:00:25 -08003928 test_format_item(test_src, "func", |result| {
Lukasz Anforowicz73a2c0d2023-03-22 14:07:00 -07003929 let result = result.unwrap().unwrap();
3930 let main_api = &result.main_api;
Lukasz Anforowicz75894832022-11-22 18:25:28 -08003931 assert_cc_matches!(
Lukasz Anforowicz6753d402023-02-10 16:41:23 -08003932 main_api.tokens,
3933 quote! {
Lukasz Anforowiczb9e515a2023-07-05 12:48:22 -07003934 std::int32_t func(::rust_out::S __param_0);
Lukasz Anforowicz6753d402023-02-10 16:41:23 -08003935 }
3936 );
3937 assert_cc_matches!(
Lukasz Anforowicz73a2c0d2023-03-22 14:07:00 -07003938 result.cc_details.tokens,
Lukasz Anforowicz75894832022-11-22 18:25:28 -08003939 quote! {
3940 namespace __crubit_internal {
Lukasz Anforowicz73edf152023-04-04 12:05:00 -07003941 extern "C" std::int32_t ...(::rust_out::S*);
Lukasz Anforowicz75894832022-11-22 18:25:28 -08003942 }
Googler47fd9572023-01-20 09:57:32 -08003943 ...
Lukasz Anforowicz8b475a42022-11-29 09:33:02 -08003944 inline std::int32_t func(::rust_out::S __param_0) {
Lukasz Anforowiczd082f352023-03-09 17:46:11 -08003945 return __crubit_internal::...(&__param_0);
Lukasz Anforowicz75894832022-11-22 18:25:28 -08003946 }
3947 }
3948 );
3949 assert_rs_matches!(
Lukasz Anforowicz73a2c0d2023-03-22 14:07:00 -07003950 result.rs_details,
Lukasz Anforowicz75894832022-11-22 18:25:28 -08003951 quote! {
3952 #[no_mangle]
Lukasz Anforowiczd082f352023-03-09 17:46:11 -08003953 extern "C" fn ...(
3954 __param_0: &mut ::core::mem::MaybeUninit<::rust_out::S>
3955 ) -> i32 {
3956 ::rust_out::func(unsafe {__param_0.assume_init_read() })
Lukasz Anforowicz75894832022-11-22 18:25:28 -08003957 }
3958 }
3959 );
3960 });
3961 }
3962
3963 #[test]
Lukasz Anforowicze465b9e2023-02-06 15:00:25 -08003964 fn test_format_item_unsupported_fn_param_type() {
Lukasz Anforowicze4333062022-10-17 14:47:53 -07003965 let test_src = r#"
Lukasz Anforowiczeb58a492023-01-07 08:25:48 -08003966 pub fn foo(_param: (i32, i32)) {}
Lukasz Anforowicze4333062022-10-17 14:47:53 -07003967 "#;
Lukasz Anforowicze465b9e2023-02-06 15:00:25 -08003968 test_format_item(test_src, "foo", |result| {
Lukasz Anforowicz4c19ad92022-12-16 15:23:14 -08003969 let err = result.unwrap_err();
Devin Jeanpierre81aec502023-04-04 15:33:39 -07003970 assert_eq!(
3971 err,
3972 "Error handling parameter #0: \
3973 Tuples are not supported yet: (i32, i32) (b/254099023)"
3974 );
Lukasz Anforowicz903fc632022-10-25 08:55:33 -07003975 });
3976 }
3977
3978 #[test]
Lukasz Anforowicze465b9e2023-02-06 15:00:25 -08003979 fn test_format_item_unsupported_fn_param_type_unit() {
Lukasz Anforowicz903fc632022-10-25 08:55:33 -07003980 let test_src = r#"
3981 #[no_mangle]
3982 pub fn fn_with_params(_param: ()) {}
3983 "#;
Lukasz Anforowicze465b9e2023-02-06 15:00:25 -08003984 test_format_item(test_src, "fn_with_params", |result| {
Lukasz Anforowicz4c19ad92022-12-16 15:23:14 -08003985 let err = result.unwrap_err();
Devin Jeanpierre81aec502023-04-04 15:33:39 -07003986 assert_eq!(
3987 err,
3988 "Error handling parameter #0: \
3989 `()` / `void` is only supported as a return type (b/254507801)"
3990 );
Lukasz Anforowicz903fc632022-10-25 08:55:33 -07003991 });
3992 }
3993
3994 #[test]
Lukasz Anforowicze465b9e2023-02-06 15:00:25 -08003995 fn test_format_item_unsupported_fn_param_type_never() {
Lukasz Anforowicz903fc632022-10-25 08:55:33 -07003996 let test_src = r#"
3997 #![feature(never_type)]
3998
3999 #[no_mangle]
4000 pub extern "C" fn fn_with_params(_param: !) {}
4001 "#;
Lukasz Anforowicze465b9e2023-02-06 15:00:25 -08004002 test_format_item(test_src, "fn_with_params", |result| {
Lukasz Anforowicz4c19ad92022-12-16 15:23:14 -08004003 let err = result.unwrap_err();
Lukasz Anforowicz903fc632022-10-25 08:55:33 -07004004 assert_eq!(
4005 err,
Lukasz Anforowicza691cf52023-03-08 12:24:33 -08004006 "Error handling parameter #0: \
Lukasz Anforowicz903fc632022-10-25 08:55:33 -07004007 The never type `!` is only supported as a return type (b/254507801)"
4008 );
Lukasz Anforowicze4333062022-10-17 14:47:53 -07004009 });
4010 }
4011
Lukasz Anforowicz75894832022-11-22 18:25:28 -08004012 /// This is a test for a regular struct - a struct with named fields.
4013 /// https://doc.rust-lang.org/reference/items/structs.html refers to this kind of struct as
4014 /// `StructStruct` or "nominal struct type".
4015 #[test]
Lukasz Anforowicze465b9e2023-02-06 15:00:25 -08004016 fn test_format_item_struct_with_fields() {
Lukasz Anforowicz75894832022-11-22 18:25:28 -08004017 let test_src = r#"
4018 pub struct SomeStruct {
4019 pub x: i32,
4020 pub y: i32,
4021 }
4022
4023 const _: () = assert!(std::mem::size_of::<SomeStruct>() == 8);
4024 const _: () = assert!(std::mem::align_of::<SomeStruct>() == 4);
4025 "#;
Lukasz Anforowicze465b9e2023-02-06 15:00:25 -08004026 test_format_item(test_src, "SomeStruct", |result| {
Lukasz Anforowicz73a2c0d2023-03-22 14:07:00 -07004027 let result = result.unwrap().unwrap();
4028 let main_api = &result.main_api;
Lukasz Anforowicz2eb24292023-03-14 09:38:21 -07004029 assert!(!main_api.prereqs.is_empty());
Lukasz Anforowicz75894832022-11-22 18:25:28 -08004030 assert_cc_matches!(
Lukasz Anforowicz0b8a02f2023-02-10 15:55:40 -08004031 main_api.tokens,
Lukasz Anforowicz75894832022-11-22 18:25:28 -08004032 quote! {
Googler47fd9572023-01-20 09:57:32 -08004033 ...
Devin Jeanpierre64ac8ad2023-05-30 17:22:55 -07004034 struct CRUBIT_INTERNAL_RUST_TYPE(...) alignas(4) SomeStruct final {
Lukasz Anforowicz75894832022-11-22 18:25:28 -08004035 public:
Lukasz Anforowicz9924c432023-04-04 12:51:22 -07004036 __COMMENT__ "`SomeStruct` doesn't implement the `Default` trait"
Lukasz Anforowicz75894832022-11-22 18:25:28 -08004037 SomeStruct() = delete;
4038
Lukasz Anforowicz57c70142023-07-07 08:35:04 -07004039 __COMMENT__ "No custom `Drop` impl and no custom \"drop glue\" required"
4040 ~SomeStruct() = default;
Lukasz Anforowicz75894832022-11-22 18:25:28 -08004041 SomeStruct(SomeStruct&&) = default;
Lukasz Anforowiczc12fc182023-06-06 08:57:47 -07004042 SomeStruct& operator=(SomeStruct&&) = default;
Lukasz Anforowicz75894832022-11-22 18:25:28 -08004043
Lukasz Anforowicz57c70142023-07-07 08:35:04 -07004044 __COMMENT__ "`SomeStruct` doesn't implement the `Clone` trait"
4045 SomeStruct(const SomeStruct&) = delete;
4046 SomeStruct& operator=(const SomeStruct&) = delete;
Lukasz Anforowicz82502222023-06-23 09:27:14 -07004047 public: union { ... std::int32_t x; };
4048 public: union { ... std::int32_t y; };
Lukasz Anforowicz75894832022-11-22 18:25:28 -08004049 private:
Lukasz Anforowiczb9e515a2023-07-05 12:48:22 -07004050 static void __crubit_field_offset_assertions();
Lukasz Anforowicz75894832022-11-22 18:25:28 -08004051 };
Lukasz Anforowicz0b8a02f2023-02-10 15:55:40 -08004052 }
4053 );
4054 assert_cc_matches!(
Lukasz Anforowicz73a2c0d2023-03-22 14:07:00 -07004055 result.cc_details.tokens,
Lukasz Anforowicz0b8a02f2023-02-10 15:55:40 -08004056 quote! {
Lukasz Anforowicz75894832022-11-22 18:25:28 -08004057 static_assert(sizeof(SomeStruct) == 8, ...);
4058 static_assert(alignof(SomeStruct) == 4, ...);
Lukasz Anforowicze0a778c2023-06-06 09:00:32 -07004059 static_assert(std::is_trivially_move_constructible_v<SomeStruct>);
4060 static_assert(std::is_trivially_move_assignable_v<SomeStruct>);
Lukasz Anforowicze3eb1cf2023-03-14 09:56:00 -07004061 inline void SomeStruct::__crubit_field_offset_assertions() {
4062 static_assert(0 == offsetof(SomeStruct, x));
4063 static_assert(4 == offsetof(SomeStruct, y));
4064 }
Lukasz Anforowicz75894832022-11-22 18:25:28 -08004065 }
4066 );
4067 assert_rs_matches!(
Lukasz Anforowicz73a2c0d2023-03-22 14:07:00 -07004068 result.rs_details,
Lukasz Anforowicz75894832022-11-22 18:25:28 -08004069 quote! {
Lukasz Anforowicz8b475a42022-11-29 09:33:02 -08004070 const _: () = assert!(::std::mem::size_of::<::rust_out::SomeStruct>() == 8);
4071 const _: () = assert!(::std::mem::align_of::<::rust_out::SomeStruct>() == 4);
Lukasz Anforowiczcf60f522023-03-14 10:03:55 -07004072 const _: () = assert!( memoffset::offset_of!(::rust_out::SomeStruct, x) == 0);
4073 const _: () = assert!( memoffset::offset_of!(::rust_out::SomeStruct, y) == 4);
Lukasz Anforowicz75894832022-11-22 18:25:28 -08004074 }
4075 );
4076 });
4077 }
4078
4079 /// This is a test for `TupleStruct` or "tuple struct" - for more details
4080 /// please refer to https://doc.rust-lang.org/reference/items/structs.html
4081 #[test]
Lukasz Anforowicze465b9e2023-02-06 15:00:25 -08004082 fn test_format_item_struct_with_tuple() {
Lukasz Anforowicz75894832022-11-22 18:25:28 -08004083 let test_src = r#"
Lukasz Anforowiczcf60f522023-03-14 10:03:55 -07004084 pub struct TupleStruct(pub i32, pub i32);
Lukasz Anforowicz75894832022-11-22 18:25:28 -08004085 const _: () = assert!(std::mem::size_of::<TupleStruct>() == 8);
4086 const _: () = assert!(std::mem::align_of::<TupleStruct>() == 4);
4087 "#;
Lukasz Anforowicze465b9e2023-02-06 15:00:25 -08004088 test_format_item(test_src, "TupleStruct", |result| {
Lukasz Anforowicz73a2c0d2023-03-22 14:07:00 -07004089 let result = result.unwrap().unwrap();
4090 let main_api = &result.main_api;
Lukasz Anforowicz2eb24292023-03-14 09:38:21 -07004091 assert!(!main_api.prereqs.is_empty());
Lukasz Anforowicz75894832022-11-22 18:25:28 -08004092 assert_cc_matches!(
Lukasz Anforowicz0b8a02f2023-02-10 15:55:40 -08004093 main_api.tokens,
Lukasz Anforowicz75894832022-11-22 18:25:28 -08004094 quote! {
Googler47fd9572023-01-20 09:57:32 -08004095 ...
Devin Jeanpierre64ac8ad2023-05-30 17:22:55 -07004096 struct CRUBIT_INTERNAL_RUST_TYPE(...) alignas(4) TupleStruct final {
Lukasz Anforowicz75894832022-11-22 18:25:28 -08004097 public:
Lukasz Anforowicz9924c432023-04-04 12:51:22 -07004098 __COMMENT__ "`TupleStruct` doesn't implement the `Default` trait"
Lukasz Anforowicz75894832022-11-22 18:25:28 -08004099 TupleStruct() = delete;
4100
Lukasz Anforowicz57c70142023-07-07 08:35:04 -07004101 __COMMENT__ "No custom `Drop` impl and no custom \"drop glue\" required"
4102 ~TupleStruct() = default;
Lukasz Anforowicz75894832022-11-22 18:25:28 -08004103 TupleStruct(TupleStruct&&) = default;
Lukasz Anforowiczc12fc182023-06-06 08:57:47 -07004104 TupleStruct& operator=(TupleStruct&&) = default;
Lukasz Anforowicz75894832022-11-22 18:25:28 -08004105
Lukasz Anforowicz57c70142023-07-07 08:35:04 -07004106 __COMMENT__ "`TupleStruct` doesn't implement the `Clone` trait"
4107 TupleStruct(const TupleStruct&) = delete;
4108 TupleStruct& operator=(const TupleStruct&) = delete;
Lukasz Anforowicz82502222023-06-23 09:27:14 -07004109 public: union { ... std::int32_t __field0; };
4110 public: union { ... std::int32_t __field1; };
Lukasz Anforowicz75894832022-11-22 18:25:28 -08004111 private:
Lukasz Anforowiczb9e515a2023-07-05 12:48:22 -07004112 static void __crubit_field_offset_assertions();
Lukasz Anforowicz75894832022-11-22 18:25:28 -08004113 };
Lukasz Anforowicz0b8a02f2023-02-10 15:55:40 -08004114 }
4115 );
4116 assert_cc_matches!(
Lukasz Anforowicz73a2c0d2023-03-22 14:07:00 -07004117 result.cc_details.tokens,
Lukasz Anforowicz0b8a02f2023-02-10 15:55:40 -08004118 quote! {
Lukasz Anforowicz75894832022-11-22 18:25:28 -08004119 static_assert(sizeof(TupleStruct) == 8, ...);
4120 static_assert(alignof(TupleStruct) == 4, ...);
Lukasz Anforowicze0a778c2023-06-06 09:00:32 -07004121 static_assert(std::is_trivially_move_constructible_v<TupleStruct>);
4122 static_assert(std::is_trivially_move_assignable_v<TupleStruct>);
Lukasz Anforowicze3eb1cf2023-03-14 09:56:00 -07004123 inline void TupleStruct::__crubit_field_offset_assertions() {
4124 static_assert(0 == offsetof(TupleStruct, __field0));
4125 static_assert(4 == offsetof(TupleStruct, __field1));
4126 }
Lukasz Anforowicz75894832022-11-22 18:25:28 -08004127 }
4128 );
4129 assert_rs_matches!(
Lukasz Anforowicz73a2c0d2023-03-22 14:07:00 -07004130 result.rs_details,
Lukasz Anforowicz75894832022-11-22 18:25:28 -08004131 quote! {
Lukasz Anforowicz8b475a42022-11-29 09:33:02 -08004132 const _: () = assert!(::std::mem::size_of::<::rust_out::TupleStruct>() == 8);
4133 const _: () = assert!(::std::mem::align_of::<::rust_out::TupleStruct>() == 4);
Lukasz Anforowiczcf60f522023-03-14 10:03:55 -07004134 const _: () = assert!( memoffset::offset_of!(::rust_out::TupleStruct, 0) == 0);
4135 const _: () = assert!( memoffset::offset_of!(::rust_out::TupleStruct, 1) == 4);
Lukasz Anforowicz75894832022-11-22 18:25:28 -08004136 }
4137 );
4138 });
4139 }
4140
Lukasz Anforowicz2eb24292023-03-14 09:38:21 -07004141 /// This test the scenario where Rust lays out field in a different order
4142 /// than the source order.
4143 #[test]
4144 fn test_format_item_struct_with_reordered_field_offsets() {
4145 let test_src = r#"
4146 pub struct SomeStruct {
4147 pub field1: i16,
4148 pub field2: i32,
4149 pub field3: i16,
4150 }
4151
4152 const _: () = assert!(std::mem::size_of::<SomeStruct>() == 8);
4153 const _: () = assert!(std::mem::align_of::<SomeStruct>() == 4);
4154 "#;
4155 test_format_item(test_src, "SomeStruct", |result| {
Lukasz Anforowicz73a2c0d2023-03-22 14:07:00 -07004156 let result = result.unwrap().unwrap();
4157 let main_api = &result.main_api;
Lukasz Anforowicz2eb24292023-03-14 09:38:21 -07004158 assert!(!main_api.prereqs.is_empty());
4159 assert_cc_matches!(
4160 main_api.tokens,
4161 quote! {
4162 ...
Devin Jeanpierre64ac8ad2023-05-30 17:22:55 -07004163 struct CRUBIT_INTERNAL_RUST_TYPE(...) alignas(4) SomeStruct final {
Lukasz Anforowicz2eb24292023-03-14 09:38:21 -07004164 ...
Lukasz Anforowicz68c88822023-06-01 16:06:15 -07004165 // The particular order below is not guaranteed,
4166 // so we may need to adjust this test assertion
4167 // (if Rust changes how it lays out the fields).
Lukasz Anforowicz82502222023-06-23 09:27:14 -07004168 public: union { ... std::int32_t field2; };
4169 public: union { ... std::int16_t field1; };
4170 public: union { ... std::int16_t field3; };
Lukasz Anforowicz2eb24292023-03-14 09:38:21 -07004171 private:
Lukasz Anforowiczb9e515a2023-07-05 12:48:22 -07004172 static void __crubit_field_offset_assertions();
Lukasz Anforowicz2eb24292023-03-14 09:38:21 -07004173 };
4174 }
4175 );
4176 assert_cc_matches!(
Lukasz Anforowicz73a2c0d2023-03-22 14:07:00 -07004177 result.cc_details.tokens,
Lukasz Anforowicz2eb24292023-03-14 09:38:21 -07004178 quote! {
4179 static_assert(sizeof(SomeStruct) == 8, ...);
4180 static_assert(alignof(SomeStruct) == 4, ...);
Lukasz Anforowicze0a778c2023-06-06 09:00:32 -07004181 static_assert(std::is_trivially_move_constructible_v<SomeStruct>);
4182 static_assert(std::is_trivially_move_assignable_v<SomeStruct>);
Lukasz Anforowicze3eb1cf2023-03-14 09:56:00 -07004183 inline void SomeStruct::__crubit_field_offset_assertions() {
4184 static_assert(0 == offsetof(SomeStruct, field2));
4185 static_assert(4 == offsetof(SomeStruct, field1));
4186 static_assert(6 == offsetof(SomeStruct, field3));
4187 }
Lukasz Anforowicz2eb24292023-03-14 09:38:21 -07004188 }
4189 );
4190 assert_rs_matches!(
Lukasz Anforowicz73a2c0d2023-03-22 14:07:00 -07004191 result.rs_details,
Lukasz Anforowicz2eb24292023-03-14 09:38:21 -07004192 quote! {
4193 const _: () = assert!(::std::mem::size_of::<::rust_out::SomeStruct>() == 8);
4194 const _: () = assert!(::std::mem::align_of::<::rust_out::SomeStruct>() == 4);
Lukasz Anforowiczcf60f522023-03-14 10:03:55 -07004195 const _: () = assert!( memoffset::offset_of!(::rust_out::SomeStruct, field2)
4196 == 0);
4197 const _: () = assert!( memoffset::offset_of!(::rust_out::SomeStruct, field1)
4198 == 4);
4199 const _: () = assert!( memoffset::offset_of!(::rust_out::SomeStruct, field3)
4200 == 6);
Lukasz Anforowicz2eb24292023-03-14 09:38:21 -07004201 }
4202 );
4203 });
4204 }
4205
4206 #[test]
4207 fn test_format_item_struct_with_packed_layout() {
4208 let test_src = r#"
4209 #[repr(packed(1))]
4210 pub struct SomeStruct {
4211 pub field1: u16,
4212 pub field2: u32,
4213 }
4214 const _: () = assert!(::std::mem::size_of::<SomeStruct>() == 6);
4215 const _: () = assert!(::std::mem::align_of::<SomeStruct>() == 1);
4216 "#;
4217 test_format_item(test_src, "SomeStruct", |result| {
Lukasz Anforowicz73a2c0d2023-03-22 14:07:00 -07004218 let result = result.unwrap().unwrap();
4219 let main_api = &result.main_api;
Lukasz Anforowicz2eb24292023-03-14 09:38:21 -07004220 assert!(!main_api.prereqs.is_empty());
4221 assert_cc_matches!(
4222 main_api.tokens,
4223 quote! {
4224 ...
Devin Jeanpierre64ac8ad2023-05-30 17:22:55 -07004225 struct CRUBIT_INTERNAL_RUST_TYPE(...) alignas(1) __attribute__((packed)) SomeStruct final {
Lukasz Anforowicz2eb24292023-03-14 09:38:21 -07004226 ...
Lukasz Anforowicz82502222023-06-23 09:27:14 -07004227 public: union { ... std::uint16_t field1; };
4228 public: union { ... std::uint32_t field2; };
Lukasz Anforowicz68c88822023-06-01 16:06:15 -07004229 private:
Lukasz Anforowiczb9e515a2023-07-05 12:48:22 -07004230 static void __crubit_field_offset_assertions();
Lukasz Anforowicz2eb24292023-03-14 09:38:21 -07004231 };
4232 }
4233 );
4234 assert_cc_matches!(
Lukasz Anforowicz73a2c0d2023-03-22 14:07:00 -07004235 result.cc_details.tokens,
Lukasz Anforowicz2eb24292023-03-14 09:38:21 -07004236 quote! {
4237 static_assert(sizeof(SomeStruct) == 6, ...);
4238 static_assert(alignof(SomeStruct) == 1, ...);
Lukasz Anforowicze0a778c2023-06-06 09:00:32 -07004239 static_assert(std::is_trivially_move_constructible_v<SomeStruct>);
4240 static_assert(std::is_trivially_move_assignable_v<SomeStruct>);
Lukasz Anforowicze3eb1cf2023-03-14 09:56:00 -07004241 inline void SomeStruct::__crubit_field_offset_assertions() {
4242 static_assert(0 == offsetof(SomeStruct, field1));
4243 static_assert(2 == offsetof(SomeStruct, field2));
4244 }
Lukasz Anforowicz2eb24292023-03-14 09:38:21 -07004245 }
4246 );
4247 assert_rs_matches!(
Lukasz Anforowicz73a2c0d2023-03-22 14:07:00 -07004248 result.rs_details,
Lukasz Anforowicz2eb24292023-03-14 09:38:21 -07004249 quote! {
4250 const _: () = assert!(::std::mem::size_of::<::rust_out::SomeStruct>() == 6);
4251 const _: () = assert!(::std::mem::align_of::<::rust_out::SomeStruct>() == 1);
Lukasz Anforowiczcf60f522023-03-14 10:03:55 -07004252 const _: () = assert!( memoffset::offset_of!(::rust_out::SomeStruct, field1)
4253 == 0);
4254 const _: () = assert!( memoffset::offset_of!(::rust_out::SomeStruct, field2)
4255 == 2);
Lukasz Anforowicz2eb24292023-03-14 09:38:21 -07004256 }
4257 );
4258 });
4259 }
4260
Lukasz Anforowicz75894832022-11-22 18:25:28 -08004261 #[test]
Lukasz Anforowicz735bfb82023-03-15 16:18:44 -07004262 fn test_format_item_struct_with_explicit_padding_in_generated_code() {
4263 let test_src = r#"
4264 pub struct SomeStruct {
4265 pub f1: u8,
4266 pub f2: u32,
4267 }
4268 const _: () = assert!(::std::mem::size_of::<SomeStruct>() == 8);
4269 const _: () = assert!(::std::mem::align_of::<SomeStruct>() == 4);
4270 "#;
4271 test_format_item(test_src, "SomeStruct", |result| {
Lukasz Anforowicz73a2c0d2023-03-22 14:07:00 -07004272 let result = result.unwrap().unwrap();
4273 let main_api = &result.main_api;
Lukasz Anforowicz735bfb82023-03-15 16:18:44 -07004274 assert!(!main_api.prereqs.is_empty());
4275 assert_cc_matches!(
4276 main_api.tokens,
4277 quote! {
4278 ...
Devin Jeanpierre64ac8ad2023-05-30 17:22:55 -07004279 struct CRUBIT_INTERNAL_RUST_TYPE(...) alignas(4) SomeStruct final {
Lukasz Anforowicz735bfb82023-03-15 16:18:44 -07004280 ...
Lukasz Anforowicz82502222023-06-23 09:27:14 -07004281 public: union { ... std::uint32_t f2; };
4282 public: union { ... std::uint8_t f1; };
Lukasz Anforowicz68c88822023-06-01 16:06:15 -07004283 private: unsigned char __padding0[3];
4284 private:
Lukasz Anforowiczb9e515a2023-07-05 12:48:22 -07004285 static void __crubit_field_offset_assertions();
Lukasz Anforowicz735bfb82023-03-15 16:18:44 -07004286 };
4287 }
4288 );
4289 assert_cc_matches!(
Lukasz Anforowicz73a2c0d2023-03-22 14:07:00 -07004290 result.cc_details.tokens,
Lukasz Anforowicz735bfb82023-03-15 16:18:44 -07004291 quote! {
4292 static_assert(sizeof(SomeStruct) == 8, ...);
4293 static_assert(alignof(SomeStruct) == 4, ...);
Lukasz Anforowicze0a778c2023-06-06 09:00:32 -07004294 static_assert(std::is_trivially_move_constructible_v<SomeStruct>);
4295 static_assert(std::is_trivially_move_assignable_v<SomeStruct>);
Lukasz Anforowicz735bfb82023-03-15 16:18:44 -07004296 inline void SomeStruct::__crubit_field_offset_assertions() {
4297 static_assert(0 == offsetof(SomeStruct, f2));
4298 static_assert(4 == offsetof(SomeStruct, f1));
4299 }
4300 }
4301 );
4302 assert_rs_matches!(
Lukasz Anforowicz73a2c0d2023-03-22 14:07:00 -07004303 result.rs_details,
Lukasz Anforowicz735bfb82023-03-15 16:18:44 -07004304 quote! {
4305 const _: () = assert!(::std::mem::size_of::<::rust_out::SomeStruct>() == 8);
4306 const _: () = assert!(::std::mem::align_of::<::rust_out::SomeStruct>() == 4);
4307 const _: () = assert!( memoffset::offset_of!(::rust_out::SomeStruct, f2) == 0);
4308 const _: () = assert!( memoffset::offset_of!(::rust_out::SomeStruct, f1) == 4);
4309 }
4310 );
4311 });
4312 }
4313
4314 #[test]
Lukasz Anforowicz43f06c62023-02-10 17:14:19 -08004315 fn test_format_item_static_method() {
4316 let test_src = r#"
Lukasz Anforowicz56d94fc2023-02-17 12:23:20 -08004317 /// No-op `f32` placeholder is used, because ZSTs are not supported
4318 /// (b/258259459).
Lukasz Anforowicz43f06c62023-02-10 17:14:19 -08004319 pub struct Math(f32);
4320
4321 impl Math {
4322 pub fn add_i32(x: f32, y: f32) -> f32 {
4323 x + y
4324 }
4325 }
4326 "#;
4327 test_format_item(test_src, "Math", |result| {
Lukasz Anforowicz73a2c0d2023-03-22 14:07:00 -07004328 let result = result.unwrap().unwrap();
4329 let main_api = &result.main_api;
Lukasz Anforowicz43f06c62023-02-10 17:14:19 -08004330 assert_cc_matches!(
4331 main_api.tokens,
4332 quote! {
4333 ...
4334 struct ... Math final {
4335 ...
4336 public:
4337 ...
Lukasz Anforowiczb9e515a2023-07-05 12:48:22 -07004338 static float add_i32(float x, float y);
Lukasz Anforowicz43f06c62023-02-10 17:14:19 -08004339 ...
4340 };
4341 }
4342 );
4343 assert_cc_matches!(
Lukasz Anforowicz73a2c0d2023-03-22 14:07:00 -07004344 result.cc_details.tokens,
Lukasz Anforowicz43f06c62023-02-10 17:14:19 -08004345 quote! {
4346 namespace __crubit_internal {
Lukasz Anforowicz73edf152023-04-04 12:05:00 -07004347 extern "C" float ... (float, float);
Lukasz Anforowicz43f06c62023-02-10 17:14:19 -08004348 }
4349 inline float Math::add_i32(float x, float y) {
4350 return __crubit_internal::...(x, y);
4351 }
4352 }
4353 );
4354 assert_rs_matches!(
Lukasz Anforowicz73a2c0d2023-03-22 14:07:00 -07004355 result.rs_details,
Lukasz Anforowicz43f06c62023-02-10 17:14:19 -08004356 quote! {
4357 #[no_mangle]
4358 extern "C" fn ...(x: f32, y: f32) -> f32 {
4359 ::rust_out::Math::add_i32(x, y)
4360 }
4361 }
4362 );
4363 });
4364 }
4365
4366 #[test]
Lukasz Anforowicz56d94fc2023-02-17 12:23:20 -08004367 fn test_format_item_static_method_with_generic_type_parameters() {
4368 let test_src = r#"
4369 /// No-op `f32` placeholder is used, because ZSTs are not supported
4370 /// (b/258259459).
4371 pub struct SomeStruct(f32);
4372
4373 impl SomeStruct {
4374 // To make this testcase distinct / non-overlapping wrt
4375 // test_format_item_static_method_with_generic_lifetime_parameters
4376 // `t` is taken by value below.
4377 pub fn generic_method<T: Clone>(t: T) -> T {
4378 t.clone()
4379 }
4380 }
4381 "#;
4382 test_format_item(test_src, "SomeStruct", |result| {
Lukasz Anforowicz73a2c0d2023-03-22 14:07:00 -07004383 let result = result.unwrap().unwrap();
4384 let main_api = &result.main_api;
Lukasz Anforowicz56d94fc2023-02-17 12:23:20 -08004385 let unsupported_msg = "Error generating bindings for `SomeStruct::generic_method` \
4386 defined at <crubit_unittests.rs>;l=10: \
4387 Generic functions are not supported yet (b/259749023)";
4388 assert_cc_matches!(
4389 main_api.tokens,
4390 quote! {
4391 ...
4392 struct ... SomeStruct final {
4393 ...
4394 __COMMENT__ #unsupported_msg
4395 ...
4396 };
4397 ...
4398 }
4399 );
Lukasz Anforowicz73a2c0d2023-03-22 14:07:00 -07004400 assert_cc_not_matches!(result.cc_details.tokens, quote! { SomeStruct::generic_method },);
4401 assert_rs_not_matches!(result.rs_details, quote! { generic_method },);
Lukasz Anforowicz56d94fc2023-02-17 12:23:20 -08004402 });
4403 }
4404
4405 #[test]
Lukasz Anforowiczed96fcb2023-06-12 11:39:45 -07004406 fn test_format_item_static_method_with_generic_lifetime_parameters_at_fn_level() {
Lukasz Anforowicz56d94fc2023-02-17 12:23:20 -08004407 let test_src = r#"
4408 /// No-op `f32` placeholder is used, because ZSTs are not supported
4409 /// (b/258259459).
4410 pub struct SomeStruct(f32);
4411
4412 impl SomeStruct {
4413 pub fn fn_taking_reference<'a>(x: &'a i32) -> i32 { *x }
4414 }
4415 "#;
4416 test_format_item(test_src, "SomeStruct", |result| {
Lukasz Anforowicz73a2c0d2023-03-22 14:07:00 -07004417 let result = result.unwrap().unwrap();
4418 let main_api = &result.main_api;
Lukasz Anforowicz56d94fc2023-02-17 12:23:20 -08004419 assert_cc_matches!(
4420 main_api.tokens,
4421 quote! {
4422 ...
4423 struct ... SomeStruct final {
4424 ...
Lukasz Anforowiczb9e515a2023-07-05 12:48:22 -07004425 static std::int32_t fn_taking_reference(
Lukasz Anforowiczaa2de7e2023-06-15 11:32:05 -07004426 std::int32_t const& [[clang::annotate_type("lifetime", "a")]] x);
Lukasz Anforowicz56d94fc2023-02-17 12:23:20 -08004427 ...
4428 };
4429 ...
4430 }
4431 );
Lukasz Anforowiczaa2de7e2023-06-15 11:32:05 -07004432 assert_cc_matches!(
Lukasz Anforowicz73a2c0d2023-03-22 14:07:00 -07004433 result.cc_details.tokens,
Lukasz Anforowiczaa2de7e2023-06-15 11:32:05 -07004434 quote! {
4435 namespace __crubit_internal {
4436 extern "C" std::int32_t ...(
4437 std::int32_t const& [[clang::annotate_type("lifetime", "a")]]);
4438 }
4439 inline std::int32_t SomeStruct::fn_taking_reference(
4440 std::int32_t const& [[clang::annotate_type("lifetime", "a")]] x) {
4441 return __crubit_internal::...(x);
4442 }
4443 },
Lukasz Anforowicz56d94fc2023-02-17 12:23:20 -08004444 );
Lukasz Anforowiczaa2de7e2023-06-15 11:32:05 -07004445 assert_rs_matches!(
4446 result.rs_details,
4447 quote! {
4448 #[no_mangle]
4449 extern "C" fn ...<'a>(x: &'a i32) -> i32 {
4450 ::rust_out::SomeStruct::fn_taking_reference(x)
4451 }
4452 },
4453 );
Lukasz Anforowicz56d94fc2023-02-17 12:23:20 -08004454 });
4455 }
4456
4457 #[test]
Lukasz Anforowiczed96fcb2023-06-12 11:39:45 -07004458 fn test_format_item_static_method_with_generic_lifetime_parameters_at_impl_level() {
4459 let test_src = r#"
4460 /// No-op `f32` placeholder is used, because ZSTs are not supported
4461 /// (b/258259459).
4462 pub struct SomeStruct(f32);
4463
4464 impl<'a> SomeStruct {
4465 pub fn fn_taking_reference(x: &'a i32) -> i32 { *x }
4466 }
4467 "#;
4468 test_format_item(test_src, "SomeStruct", |result| {
4469 let result = result.unwrap().unwrap();
4470 let main_api = &result.main_api;
4471 let unsupported_msg = "Error generating bindings for `SomeStruct::fn_taking_reference` \
4472 defined at <crubit_unittests.rs>;l=7: \
4473 Generic functions are not supported yet (b/259749023)";
4474 assert_cc_matches!(
4475 main_api.tokens,
4476 quote! {
4477 ...
4478 struct ... SomeStruct final {
4479 ...
4480 __COMMENT__ #unsupported_msg
4481 ...
4482 };
4483 ...
4484 }
4485 );
4486 assert_cc_not_matches!(
4487 result.cc_details.tokens,
4488 quote! { SomeStruct::fn_taking_reference },
4489 );
4490 assert_rs_not_matches!(result.rs_details, quote! { fn_taking_reference },);
4491 });
4492 }
4493
Lukasz Anforowiczae1ae172023-07-05 12:11:24 -07004494 fn test_format_item_method_taking_self_by_value(test_src: &str) {
4495 test_format_item(test_src, "SomeStruct", |result| {
4496 let result = result.unwrap().unwrap();
4497 let main_api = &result.main_api;
4498 assert_cc_matches!(
4499 main_api.tokens,
4500 quote! {
4501 ...
4502 struct ... SomeStruct final {
4503 ...
Lukasz Anforowiczb9e515a2023-07-05 12:48:22 -07004504 float into_f32() &&;
Lukasz Anforowiczae1ae172023-07-05 12:11:24 -07004505 ...
4506 };
4507 ...
4508 }
4509 );
4510 assert_cc_matches!(
4511 result.cc_details.tokens,
4512 quote! {
4513 namespace __crubit_internal {
4514 extern "C" float ...(::rust_out::SomeStruct*);
4515 }
4516 inline float SomeStruct::into_f32() && {
4517 return __crubit_internal::...(this);
4518 }
4519 },
4520 );
4521 assert_rs_matches!(
4522 result.rs_details,
4523 quote! {
4524 #[no_mangle]
4525 extern "C" fn ...(__self: &mut ::core::mem::MaybeUninit<::rust_out::SomeStruct>) -> f32 {
4526 ::rust_out::SomeStruct::into_f32(unsafe { __self.assume_init_read() })
4527 }
4528 },
4529 );
4530 });
4531 }
4532
Lukasz Anforowiczed96fcb2023-06-12 11:39:45 -07004533 #[test]
Lukasz Anforowiczae1ae172023-07-05 12:11:24 -07004534 fn test_format_item_method_taking_self_by_value_implicit_type() {
Lukasz Anforowicz56d94fc2023-02-17 12:23:20 -08004535 let test_src = r#"
4536 pub struct SomeStruct(f32);
4537
4538 impl SomeStruct {
4539 pub fn into_f32(self) -> f32 {
4540 self.0
4541 }
4542 }
4543 "#;
Lukasz Anforowiczae1ae172023-07-05 12:11:24 -07004544 test_format_item_method_taking_self_by_value(test_src);
4545 }
4546
4547 /// One difference from
4548 /// `test_format_item_method_taking_self_by_value_implicit_type` is that
4549 /// `fn_sig.decl.implicit_self` is `ImplicitSelfKind::None` here (vs
4550 /// `ImplicitSelfKind::Imm` in the other test).
4551 #[test]
4552 fn test_format_item_method_taking_self_by_value_explicit_type() {
4553 let test_src = r#"
4554 pub struct SomeStruct(f32);
4555
4556 impl SomeStruct {
4557 pub fn into_f32(self: SomeStruct) -> f32 {
4558 self.0
4559 }
Lukasz Anforowicz56d94fc2023-02-17 12:23:20 -08004560 }
Lukasz Anforowiczae1ae172023-07-05 12:11:24 -07004561 "#;
4562 test_format_item_method_taking_self_by_value(test_src);
Lukasz Anforowicz56d94fc2023-02-17 12:23:20 -08004563 }
4564
Lukasz Anforowiczec4c8c72023-07-05 12:30:57 -07004565 fn test_format_item_method_taking_self_by_const_ref(test_src: &str) {
4566 test_format_item(test_src, "SomeStruct", |result| {
4567 let result = result.unwrap().unwrap();
4568 let main_api = &result.main_api;
4569 assert_cc_matches!(
4570 main_api.tokens,
4571 quote! {
4572 ...
4573 struct ... SomeStruct final {
4574 ...
Lukasz Anforowiczec4c8c72023-07-05 12:30:57 -07004575 float get_f32() const [[clang::annotate_type("lifetime", "__anon1")]];
4576 ...
4577 };
4578 ...
4579 }
4580 );
4581 assert_cc_matches!(
4582 result.cc_details.tokens,
4583 quote! {
4584 namespace __crubit_internal {
4585 extern "C" float ...(
4586 ::rust_out::SomeStruct const& [[clang::annotate_type("lifetime",
4587 "__anon1")]]);
4588 }
4589 inline float SomeStruct::get_f32()
4590 const [[clang::annotate_type("lifetime", "__anon1")]] {
4591 return __crubit_internal::...(*this);
4592 }
4593 },
4594 );
4595 assert_rs_matches!(
4596 result.rs_details,
4597 quote! {
4598 #[no_mangle]
4599 extern "C" fn ...<'__anon1>(__self: &'__anon1 ::rust_out::SomeStruct) -> f32 {
4600 ::rust_out::SomeStruct::get_f32(__self)
4601 }
4602 },
4603 );
4604 });
4605 }
4606
Lukasz Anforowicz56d94fc2023-02-17 12:23:20 -08004607 #[test]
Lukasz Anforowiczec4c8c72023-07-05 12:30:57 -07004608 fn test_format_item_method_taking_self_by_const_ref_implicit_type() {
Lukasz Anforowicz56d94fc2023-02-17 12:23:20 -08004609 let test_src = r#"
4610 pub struct SomeStruct(f32);
4611
4612 impl SomeStruct {
4613 pub fn get_f32(&self) -> f32 {
4614 self.0
4615 }
4616 }
4617 "#;
Lukasz Anforowiczec4c8c72023-07-05 12:30:57 -07004618 test_format_item_method_taking_self_by_const_ref(test_src);
4619 }
4620
4621 #[test]
4622 fn test_format_item_method_taking_self_by_const_ref_explicit_type() {
4623 let test_src = r#"
4624 pub struct SomeStruct(f32);
4625
4626 impl SomeStruct {
4627 pub fn get_f32(self: &SomeStruct) -> f32 {
4628 self.0
4629 }
4630 }
4631 "#;
4632 test_format_item_method_taking_self_by_const_ref(test_src);
4633 }
4634
4635 fn test_format_item_method_taking_self_by_mutable_ref(test_src: &str) {
4636 test_format_item(test_src, "SomeStruct", |result| {
4637 let result = result.unwrap().unwrap();
4638 let main_api = &result.main_api;
4639 assert_cc_matches!(
4640 main_api.tokens,
4641 quote! {
4642 ...
4643 struct ... SomeStruct final {
4644 ...
Lukasz Anforowiczb9e515a2023-07-05 12:48:22 -07004645 void set_f32(float new_value)
Lukasz Anforowiczec4c8c72023-07-05 12:30:57 -07004646 [[clang::annotate_type("lifetime", "__anon1")]];
4647 ...
4648 };
4649 ...
4650 }
4651 );
4652 assert_cc_matches!(
4653 result.cc_details.tokens,
4654 quote! {
4655 namespace __crubit_internal {
4656 extern "C" void ...(
4657 ::rust_out::SomeStruct& [[clang::annotate_type("lifetime", "__anon1")]],
4658 float);
4659 }
4660 inline void SomeStruct::set_f32(float new_value)
4661 [[clang::annotate_type("lifetime", "__anon1")]] {
4662 return __crubit_internal::...(*this, new_value);
4663 }
4664 },
4665 );
4666 assert_rs_matches!(
4667 result.rs_details,
4668 quote! {
4669 #[no_mangle]
4670 extern "C" fn ...<'__anon1>(
4671 __self: &'__anon1 mut ::rust_out::SomeStruct,
4672 new_value: f32
4673 ) -> () {
4674 ::rust_out::SomeStruct::set_f32(__self, new_value)
4675 }
4676 },
4677 );
4678 });
4679 }
4680
4681 #[test]
4682 fn test_format_item_method_taking_self_by_mutable_ref_implicit_type() {
4683 let test_src = r#"
4684 pub struct SomeStruct(f32);
4685
4686 impl SomeStruct {
4687 pub fn set_f32(&mut self, new_value: f32) {
4688 self.0 = new_value;
4689 }
4690 }
4691 "#;
4692 test_format_item_method_taking_self_by_mutable_ref(test_src);
4693 }
4694
4695 #[test]
4696 fn test_format_item_method_taking_self_by_mutable_ref_explicit_type() {
4697 let test_src = r#"
4698 pub struct SomeStruct(f32);
4699
4700 impl SomeStruct {
4701 pub fn set_f32(self: &mut SomeStruct, new_value: f32) {
4702 self.0 = new_value;
4703 }
4704 }
4705 "#;
4706 test_format_item_method_taking_self_by_mutable_ref(test_src);
4707 }
4708
4709 #[test]
4710 fn test_format_item_method_taking_self_by_arc() {
4711 let test_src = r#"
4712 use std::sync::Arc;
4713
4714 pub struct SomeStruct(f32);
4715
4716 impl SomeStruct {
4717 pub fn get_f32(self: Arc<Self>) -> f32 {
4718 self.0
4719 }
4720 }
4721 "#;
Lukasz Anforowicz56d94fc2023-02-17 12:23:20 -08004722 test_format_item(test_src, "SomeStruct", |result| {
Lukasz Anforowicz73a2c0d2023-03-22 14:07:00 -07004723 let result = result.unwrap().unwrap();
4724 let main_api = &result.main_api;
Lukasz Anforowicz56d94fc2023-02-17 12:23:20 -08004725 let unsupported_msg = "Error generating bindings for `SomeStruct::get_f32` \
Lukasz Anforowiczec4c8c72023-07-05 12:30:57 -07004726 defined at <crubit_unittests.rs>;l=7: \
4727 Error handling parameter #0: \
4728 Generic types are not supported yet (b/259749095)";
Lukasz Anforowicz56d94fc2023-02-17 12:23:20 -08004729 assert_cc_matches!(
4730 main_api.tokens,
4731 quote! {
4732 ...
4733 struct ... SomeStruct final {
4734 ...
4735 __COMMENT__ #unsupported_msg
4736 ...
4737 };
4738 ...
4739 }
4740 );
Lukasz Anforowicz73a2c0d2023-03-22 14:07:00 -07004741 assert_cc_not_matches!(result.cc_details.tokens, quote! { SomeStruct::get_f32 },);
4742 assert_rs_not_matches!(result.rs_details, quote! { get_f32 },);
Lukasz Anforowicz56d94fc2023-02-17 12:23:20 -08004743 });
4744 }
4745
4746 #[test]
Lukasz Anforowiczae1ae172023-07-05 12:11:24 -07004747 fn test_format_item_method_taking_self_by_pinned_mut_ref() {
4748 let test_src = r#"
4749 use core::pin::Pin;
4750
4751 pub struct SomeStruct(f32);
4752
4753 impl SomeStruct {
4754 pub fn set_f32(mut self: Pin<&mut Self>, f: f32) {
4755 self.0 = f;
4756 }
4757 }
4758 "#;
4759 test_format_item(test_src, "SomeStruct", |result| {
4760 let result = result.unwrap().unwrap();
4761 let main_api = &result.main_api;
4762 let unsupported_msg = "Error generating bindings for `SomeStruct::set_f32` \
4763 defined at <crubit_unittests.rs>;l=7: \
4764 Error handling parameter #0: \
4765 Generic types are not supported yet (b/259749095)";
Lukasz Anforowicz56d94fc2023-02-17 12:23:20 -08004766 assert_cc_matches!(
4767 main_api.tokens,
4768 quote! {
4769 ...
4770 struct ... SomeStruct final {
4771 ...
4772 __COMMENT__ #unsupported_msg
4773 ...
4774 };
4775 ...
4776 }
4777 );
Lukasz Anforowicz73a2c0d2023-03-22 14:07:00 -07004778 assert_cc_not_matches!(result.cc_details.tokens, quote! { SomeStruct::set_f32 },);
4779 assert_rs_not_matches!(result.rs_details, quote! { set_f32 },);
Lukasz Anforowicz56d94fc2023-02-17 12:23:20 -08004780 });
4781 }
4782
4783 #[test]
Lukasz Anforowicz9924c432023-04-04 12:51:22 -07004784 fn test_format_item_struct_with_default_constructor() {
4785 let test_src = r#"
4786 #[derive(Default)]
4787 pub struct Point(i32, i32);
4788 "#;
4789 test_format_item(test_src, "Point", |result| {
4790 let result = result.unwrap().unwrap();
4791 let main_api = &result.main_api;
4792 assert_cc_matches!(
4793 main_api.tokens,
4794 quote! {
4795 ...
4796 struct ... Point final {
4797 ...
4798 public:
4799 __COMMENT__ "Default::default"
Lukasz Anforowiczb9e515a2023-07-05 12:48:22 -07004800 Point();
Lukasz Anforowicz9924c432023-04-04 12:51:22 -07004801 ...
4802 };
4803 }
4804 );
4805 assert_cc_matches!(
4806 result.cc_details.tokens,
4807 quote! {
4808 namespace __crubit_internal {
4809 extern "C" void ...(::rust_out::Point* __ret_ptr);
4810 }
Lukasz Anforowiczb9e515a2023-07-05 12:48:22 -07004811 inline Point::Point() {
Lukasz Anforowicz9924c432023-04-04 12:51:22 -07004812 ...(this);
4813 }
4814 }
4815 );
4816 assert_rs_matches!(
4817 result.rs_details,
4818 quote! {
4819 #[no_mangle]
4820 extern "C" fn ...(
4821 __ret_slot: &mut ::core::mem::MaybeUninit<::rust_out::Point>
4822 ) -> () {
4823 __ret_slot.write(<::rust_out::Point as ::core::default::Default>::default());
4824 }
4825 }
4826 );
4827 });
4828 }
4829
4830 #[test]
Lukasz Anforowicz901161a2023-06-01 15:39:58 -07004831 fn test_format_item_struct_with_copy_trait() {
4832 let test_src = r#"
4833 #[derive(Clone, Copy)]
4834 pub struct Point(i32, i32);
4835 "#;
4836 let msg = "Rust types that are `Copy` get trivial, `default` C++ copy constructor \
4837 and assignment operator.";
4838 test_format_item(test_src, "Point", |result| {
4839 let result = result.unwrap().unwrap();
4840 let main_api = &result.main_api;
4841 assert_cc_matches!(
4842 main_api.tokens,
4843 quote! {
4844 ...
4845 struct ... Point final {
4846 ...
4847 public:
4848 ...
4849 __COMMENT__ #msg
4850 Point(const Point&) = default;
4851 Point& operator=(const Point&) = default;
4852 ...
4853 };
4854 }
4855 );
4856
Lukasz Anforowiczf18a8572023-06-06 08:42:14 -07004857 // Trivial copy doesn't require any C++ details except `static_assert`s.
Lukasz Anforowicz901161a2023-06-01 15:39:58 -07004858 assert_cc_not_matches!(result.cc_details.tokens, quote! { Point::Point(const Point&) },);
4859 assert_cc_not_matches!(
4860 result.cc_details.tokens,
4861 quote! { Point::operator=(const Point&) },
4862 );
Lukasz Anforowiczf18a8572023-06-06 08:42:14 -07004863 assert_cc_matches!(
4864 result.cc_details.tokens,
4865 quote! {
4866 static_assert(std::is_trivially_copy_constructible_v<Point>);
4867 static_assert(std::is_trivially_copy_assignable_v<Point>);
4868 },
4869 );
4870
4871 // Trivial copy doesn't require any Rust details.
Lukasz Anforowicz901161a2023-06-01 15:39:58 -07004872 assert_rs_not_matches!(result.rs_details, quote! { Copy });
4873 assert_rs_not_matches!(result.rs_details, quote! { copy });
4874 });
4875 }
4876
Lukasz Anforowicz1b665a42023-06-20 13:04:54 -07004877 /// Test of `format_copy_ctor_and_assignment_operator` when the ADT
4878 /// implements a `Clone` trait.
4879 ///
4880 /// Notes:
4881 /// * `Copy` trait is covered in `test_format_item_struct_with_copy_trait`.
4882 /// * The test below implements `clone` and uses the default `clone_from`.
Lukasz Anforowicz901161a2023-06-01 15:39:58 -07004883 #[test]
4884 fn test_format_item_struct_with_clone_trait() {
4885 let test_src = r#"
Lukasz Anforowicz901161a2023-06-01 15:39:58 -07004886 pub struct Point(i32, i32);
Lukasz Anforowicz1b665a42023-06-20 13:04:54 -07004887 impl Clone for Point {
4888 fn clone(&self) -> Self {
4889 unimplemented!()
4890 }
4891 }
Lukasz Anforowicz901161a2023-06-01 15:39:58 -07004892 "#;
Lukasz Anforowicz901161a2023-06-01 15:39:58 -07004893 test_format_item(test_src, "Point", |result| {
4894 let result = result.unwrap().unwrap();
4895 let main_api = &result.main_api;
4896 assert_cc_matches!(
4897 main_api.tokens,
4898 quote! {
4899 ...
4900 struct ... Point final {
4901 ...
4902 public:
4903 ...
Lukasz Anforowicz1b665a42023-06-20 13:04:54 -07004904 __COMMENT__ "Clone::clone"
Lukasz Anforowiczb9e515a2023-07-05 12:48:22 -07004905 Point(const Point&);
Lukasz Anforowicz901161a2023-06-01 15:39:58 -07004906
Lukasz Anforowicz1b665a42023-06-20 13:04:54 -07004907 __COMMENT__ "Clone::clone_from"
Lukasz Anforowiczb9e515a2023-07-05 12:48:22 -07004908 Point& operator=(const Point&);
Lukasz Anforowicz901161a2023-06-01 15:39:58 -07004909 ...
4910 };
4911 }
4912 );
Lukasz Anforowicz1b665a42023-06-20 13:04:54 -07004913 assert_cc_matches!(
4914 result.cc_details.tokens,
4915 quote! {
4916 namespace __crubit_internal {
4917 extern "C" void ...(
4918 ::rust_out::Point const& [[clang::annotate_type("lifetime",
4919 "__anon1")]],
4920 ::rust_out::Point* __ret_ptr);
4921 }
4922 namespace __crubit_internal {
4923 extern "C" void ...(
4924 ::rust_out::Point& [[clang::annotate_type("lifetime", "__anon1")]],
4925 ::rust_out::Point const& [[clang::annotate_type("lifetime",
4926 "__anon2")]]);
4927 }
Lukasz Anforowiczb9e515a2023-07-05 12:48:22 -07004928 inline Point::Point(const Point& other) {
Lukasz Anforowicz1b665a42023-06-20 13:04:54 -07004929 __crubit_internal::...(other, this);
4930 }
Lukasz Anforowiczb9e515a2023-07-05 12:48:22 -07004931 inline Point& Point::operator=(const Point& other) {
Lukasz Anforowicz1b665a42023-06-20 13:04:54 -07004932 if (this != &other) {
4933 __crubit_internal::...(*this, other);
4934 }
4935 return *this;
4936 }
4937 static_assert(std::is_copy_constructible_v<Point>);
4938 static_assert(std::is_copy_assignable_v<Point>);
4939 }
4940 );
4941 assert_rs_matches!(
4942 result.rs_details,
4943 quote! {
4944 #[no_mangle]
4945 extern "C" fn ...<'__anon1>(
4946 __self: &'__anon1 ::rust_out::Point,
4947 __ret_slot: &mut ::core::mem::MaybeUninit<::rust_out::Point>
4948 ) -> () {
4949 __ret_slot.write(
4950 <::rust_out::Point as ::core::clone::Clone>::clone(__self)
4951 );
4952 }
4953 #[no_mangle]
4954 extern "C" fn ...<'__anon1, '__anon2>(
4955 __self: &'__anon1 mut ::rust_out::Point,
4956 source: &'__anon2 ::rust_out::Point
4957 ) -> () {
4958 <::rust_out::Point as ::core::clone::Clone>::clone_from(__self, source)
4959 }
4960 }
4961 );
Lukasz Anforowicz901161a2023-06-01 15:39:58 -07004962 });
4963 }
4964
4965 #[test]
Lukasz Anforowicze465b9e2023-02-06 15:00:25 -08004966 fn test_format_item_unsupported_struct_with_name_that_is_reserved_keyword() {
Lukasz Anforowicz75894832022-11-22 18:25:28 -08004967 let test_src = r#"
4968 #[allow(non_camel_case_types)]
4969 pub struct reinterpret_cast {
4970 pub x: i32,
4971 pub y: i32,
4972 }
4973 "#;
Lukasz Anforowicze465b9e2023-02-06 15:00:25 -08004974 test_format_item(test_src, "reinterpret_cast", |result| {
Lukasz Anforowicz4c19ad92022-12-16 15:23:14 -08004975 let err = result.unwrap_err();
Lukasz Anforowicz75894832022-11-22 18:25:28 -08004976 assert_eq!(
4977 err,
4978 "Error formatting item name: \
4979 `reinterpret_cast` is a C++ reserved keyword \
4980 and can't be used as a C++ identifier"
4981 );
4982 });
4983 }
4984
4985 #[test]
Lukasz Anforowicz672c5152023-03-15 16:23:00 -07004986 fn test_format_item_struct_with_unsupported_field_type() {
Lukasz Anforowicz2eb24292023-03-14 09:38:21 -07004987 let test_src = r#"
4988 pub struct SomeStruct {
Lukasz Anforowicz672c5152023-03-15 16:23:00 -07004989 pub successful_field: i32,
4990 pub unsupported_field: Option<[i32; 3]>,
Lukasz Anforowicz2eb24292023-03-14 09:38:21 -07004991 }
4992 "#;
4993 test_format_item(test_src, "SomeStruct", |result| {
Lukasz Anforowicz73a2c0d2023-03-22 14:07:00 -07004994 let result = result.unwrap().unwrap();
4995 let main_api = &result.main_api;
Lukasz Anforowicz672c5152023-03-15 16:23:00 -07004996 let broken_field_msg = "Field type has been replaced with a blob of bytes: \
Lukasz Anforowicz2eb24292023-03-14 09:38:21 -07004997 Generic types are not supported yet (b/259749095)";
Lukasz Anforowicz2eb24292023-03-14 09:38:21 -07004998 assert_cc_matches!(
4999 main_api.tokens,
5000 quote! {
5001 ...
5002 struct ... SomeStruct final {
5003 ...
5004 private:
5005 __COMMENT__ #broken_field_msg
Lukasz Anforowicz672c5152023-03-15 16:23:00 -07005006 unsigned char unsupported_field[16];
Lukasz Anforowicz68c88822023-06-01 16:06:15 -07005007 public:
Lukasz Anforowicz82502222023-06-23 09:27:14 -07005008 union { ... std::int32_t successful_field; };
Lukasz Anforowicz68c88822023-06-01 16:06:15 -07005009 private:
Lukasz Anforowiczb9e515a2023-07-05 12:48:22 -07005010 static void __crubit_field_offset_assertions();
Lukasz Anforowicz2eb24292023-03-14 09:38:21 -07005011 };
5012 ...
5013 }
5014 );
5015 assert_cc_matches!(
Lukasz Anforowicz73a2c0d2023-03-22 14:07:00 -07005016 result.cc_details.tokens,
Lukasz Anforowicz2eb24292023-03-14 09:38:21 -07005017 quote! {
Lukasz Anforowicz672c5152023-03-15 16:23:00 -07005018 static_assert(sizeof(SomeStruct) == 20, ...);
Lukasz Anforowicz2eb24292023-03-14 09:38:21 -07005019 static_assert(alignof(SomeStruct) == 4, ...);
Lukasz Anforowicze0a778c2023-06-06 09:00:32 -07005020 static_assert(std::is_trivially_move_constructible_v<SomeStruct>);
5021 static_assert(std::is_trivially_move_assignable_v<SomeStruct>);
Lukasz Anforowicz672c5152023-03-15 16:23:00 -07005022 inline void SomeStruct::__crubit_field_offset_assertions() {
5023 static_assert(0 == offsetof(SomeStruct, unsupported_field));
5024 static_assert(16 == offsetof(SomeStruct, successful_field));
5025 }
Lukasz Anforowicz2eb24292023-03-14 09:38:21 -07005026 }
5027 );
5028 assert_rs_matches!(
Lukasz Anforowicz73a2c0d2023-03-22 14:07:00 -07005029 result.rs_details,
Lukasz Anforowicz2eb24292023-03-14 09:38:21 -07005030 quote! {
Lukasz Anforowicz672c5152023-03-15 16:23:00 -07005031 const _: () = assert!(::std::mem::size_of::<::rust_out::SomeStruct>() == 20);
Lukasz Anforowicz2eb24292023-03-14 09:38:21 -07005032 const _: () = assert!(::std::mem::align_of::<::rust_out::SomeStruct>() == 4);
Lukasz Anforowicz672c5152023-03-15 16:23:00 -07005033 const _: () = assert!( memoffset::offset_of!(::rust_out::SomeStruct,
5034 unsupported_field) == 0);
5035 const _: () = assert!( memoffset::offset_of!(::rust_out::SomeStruct,
5036 successful_field) == 16);
Lukasz Anforowicz2eb24292023-03-14 09:38:21 -07005037 }
5038 );
5039 });
5040 }
5041
Lukasz Anforowiczed96fcb2023-06-12 11:39:45 -07005042 /// This test verifies how reference type fields are represented in the
5043 /// generated bindings. See b/286256327.
5044 ///
5045 /// In some of the past discussions we tentatively decided that the
5046 /// generated bindings shouldn't use C++ references in fields - instead
5047 /// a C++ pointer should be used. One reason is that C++ references
5048 /// cannot be assigned to (i.e. rebound), and therefore C++ pointers
5049 /// more accurately represent the semantics of Rust fields. The pointer
5050 /// type should probably use some form of C++ annotations to mark it as
5051 /// non-nullable.
5052 #[test]
5053 fn test_format_item_struct_with_unsupported_field_of_reference_type() {
5054 let test_src = r#"
5055 // `'static` lifetime can be used in a non-generic struct - this let's us
5056 // test reference fieles without requiring support for generic structs.
5057 pub struct NonGenericSomeStruct {
5058 pub reference_field: &'static i32,
5059 }
5060 "#;
5061 test_format_item(test_src, "NonGenericSomeStruct", |result| {
5062 let result = result.unwrap().unwrap();
5063 let main_api = &result.main_api;
5064 let broken_field_msg = "Field type has been replaced with a blob of bytes: \
Lukasz Anforowiczaa2de7e2023-06-15 11:32:05 -07005065 Can't format `&'static i32`, because references \
5066 are only supported in function parameter types and \
5067 return types (b/286256327)";
Lukasz Anforowiczed96fcb2023-06-12 11:39:45 -07005068 assert_cc_matches!(
5069 main_api.tokens,
5070 quote! {
5071 ...
5072 private:
5073 __COMMENT__ #broken_field_msg
5074 unsigned char reference_field[8];
5075 ...
5076 }
5077 );
5078 });
5079 }
5080
Lukasz Anforowicz2eb24292023-03-14 09:38:21 -07005081 #[test]
Lukasz Anforowicz35f73742023-07-07 09:18:26 -07005082 fn test_format_item_unsupported_struct_with_custom_drop_impl_and_no_default_impl() {
Lukasz Anforowicz75894832022-11-22 18:25:28 -08005083 let test_src = r#"
5084 pub struct StructWithCustomDropImpl {
5085 pub x: i32,
5086 pub y: i32,
5087 }
5088
5089 impl Drop for StructWithCustomDropImpl {
5090 fn drop(&mut self) {}
5091 }
5092 "#;
Lukasz Anforowicze465b9e2023-02-06 15:00:25 -08005093 test_format_item(test_src, "StructWithCustomDropImpl", |result| {
Lukasz Anforowicz4c19ad92022-12-16 15:23:14 -08005094 let err = result.unwrap_err();
Lukasz Anforowicz75894832022-11-22 18:25:28 -08005095 assert_eq!(err, "`Drop` trait and \"drop glue\" are not supported yet (b/258251148)");
5096 });
5097 }
5098
5099 #[test]
Lukasz Anforowicz35f73742023-07-07 09:18:26 -07005100 fn test_format_item_unsupported_struct_with_custom_drop_glue_and_no_default_impl() {
Lukasz Anforowicz75894832022-11-22 18:25:28 -08005101 let test_src = r#"
5102 #![allow(dead_code)]
5103
5104 // `i32` is present to avoid hitting the ZST checks related to (b/258259459)
5105 struct StructWithCustomDropImpl(i32);
5106
5107 impl Drop for StructWithCustomDropImpl {
5108 fn drop(&mut self) {
5109 println!("dropping!");
5110 }
5111 }
5112
5113 pub struct StructRequiringCustomDropGlue {
5114 field: StructWithCustomDropImpl,
5115 }
5116 "#;
Lukasz Anforowicze465b9e2023-02-06 15:00:25 -08005117 test_format_item(test_src, "StructRequiringCustomDropGlue", |result| {
Lukasz Anforowicz4c19ad92022-12-16 15:23:14 -08005118 let err = result.unwrap_err();
Lukasz Anforowicz75894832022-11-22 18:25:28 -08005119 assert_eq!(err, "`Drop` trait and \"drop glue\" are not supported yet (b/258251148)");
5120 });
5121 }
5122
Lukasz Anforowicz35f73742023-07-07 09:18:26 -07005123 #[test]
5124 fn test_format_item_struct_with_custom_drop_impl_and_with_default_impl() {
5125 let test_src = r#"
5126 #[derive(Default)]
5127 pub struct StructWithCustomDropImpl {
5128 pub x: i32,
5129 pub y: i32,
5130 }
5131
5132 impl Drop for StructWithCustomDropImpl {
5133 fn drop(&mut self) {}
5134 }
5135 "#;
5136 test_format_item(test_src, "StructWithCustomDropImpl", |result| {
5137 let err = result.unwrap_err();
5138 assert_eq!(err, "`Drop` trait and \"drop glue\" are not supported yet (b/258251148)");
5139 });
5140 }
5141
5142 #[test]
5143 fn test_format_item_struct_with_custom_drop_glue_and_with_default_impl() {
5144 let test_src = r#"
5145 #![allow(dead_code)]
5146
5147 // `i32` is present to avoid hitting the ZST checks related to (b/258259459)
5148 #[derive(Default)]
5149 struct StructWithCustomDropImpl(i32);
5150
5151 impl Drop for StructWithCustomDropImpl {
5152 fn drop(&mut self) {
5153 println!("dropping!");
5154 }
5155 }
5156
5157 #[derive(Default)]
5158 pub struct StructRequiringCustomDropGlue {
5159 field: StructWithCustomDropImpl,
5160 }
5161 "#;
5162 test_format_item(test_src, "StructRequiringCustomDropGlue", |result| {
5163 let err = result.unwrap_err();
5164 assert_eq!(err, "`Drop` trait and \"drop glue\" are not supported yet (b/258251148)");
5165 });
5166 }
5167
Lukasz Anforowicz4c19ad92022-12-16 15:23:14 -08005168 /// This test covers how ZSTs (zero-sized-types) are handled.
5169 /// https://doc.rust-lang.org/reference/items/structs.html refers to this kind of struct as a
5170 /// "unit-like struct".
Lukasz Anforowicz75894832022-11-22 18:25:28 -08005171 #[test]
Lukasz Anforowicz2eb24292023-03-14 09:38:21 -07005172 fn test_format_item_unsupported_struct_zero_sized_type_with_no_fields() {
Lukasz Anforowicz75894832022-11-22 18:25:28 -08005173 let test_src = r#"
5174 pub struct ZeroSizedType1;
5175 pub struct ZeroSizedType2();
5176 pub struct ZeroSizedType3{}
5177 "#;
5178 for name in ["ZeroSizedType1", "ZeroSizedType2", "ZeroSizedType3"] {
Lukasz Anforowicze465b9e2023-02-06 15:00:25 -08005179 test_format_item(test_src, name, |result| {
Lukasz Anforowicz4c19ad92022-12-16 15:23:14 -08005180 let err = result.unwrap_err();
Lukasz Anforowicz75894832022-11-22 18:25:28 -08005181 assert_eq!(err, "Zero-sized types (ZSTs) are not supported (b/258259459)");
5182 });
5183 }
5184 }
5185
Lukasz Anforowicz2eb24292023-03-14 09:38:21 -07005186 #[test]
5187 fn test_format_item_unsupported_struct_with_only_zero_sized_type_fields() {
5188 let test_src = r#"
5189 pub struct ZeroSizedType;
5190 pub struct SomeStruct {
5191 pub zst1: ZeroSizedType,
5192 pub zst2: ZeroSizedType,
5193 }
5194 "#;
5195 test_format_item(test_src, "SomeStruct", |result| {
5196 let err = result.unwrap_err();
5197 assert_eq!(err, "Zero-sized types (ZSTs) are not supported (b/258259459)",);
5198 });
5199 }
5200
Devin Jeanpierre9e15d0b2023-04-06 13:18:22 -07005201 #[test]
5202 fn test_format_item_unsupported_struct_with_some_zero_sized_type_fields() {
5203 let test_src = r#"
5204 pub struct ZeroSizedType;
5205 pub struct SomeStruct {
5206 pub zst1: ZeroSizedType,
5207 pub zst2: ZeroSizedType,
5208 pub successful_field: i32,
5209 }
5210 "#;
5211 test_format_item(test_src, "SomeStruct", |result| {
5212 let result = result.unwrap().unwrap();
5213 let main_api = &result.main_api;
5214 let broken_field_msg = "Field type has been replaced with a blob of bytes: \
5215 Failed to generate bindings for the definition of `ZeroSizedType`: \
5216 Zero-sized types (ZSTs) are not supported (b/258259459)";
5217 assert_cc_matches!(
5218 main_api.tokens,
5219 quote! {
5220 ...
5221 struct ... SomeStruct final {
5222 ...
5223 private:
5224 __COMMENT__ #broken_field_msg
5225 [[no_unique_address]] struct{} zst1;
Lukasz Anforowicz68c88822023-06-01 16:06:15 -07005226 private:
Devin Jeanpierre9e15d0b2023-04-06 13:18:22 -07005227 __COMMENT__ #broken_field_msg
5228 [[no_unique_address]] struct{} zst2;
Lukasz Anforowicz68c88822023-06-01 16:06:15 -07005229 public:
Lukasz Anforowicz82502222023-06-23 09:27:14 -07005230 union { ... std::int32_t successful_field; };
Lukasz Anforowicz68c88822023-06-01 16:06:15 -07005231 private:
Lukasz Anforowiczb9e515a2023-07-05 12:48:22 -07005232 static void __crubit_field_offset_assertions();
Devin Jeanpierre9e15d0b2023-04-06 13:18:22 -07005233 };
5234 ...
5235 }
5236 );
5237 assert_cc_matches!(
5238 result.cc_details.tokens,
5239 quote! {
5240 static_assert(sizeof(SomeStruct) == 4, ...);
5241 static_assert(alignof(SomeStruct) == 4, ...);
Lukasz Anforowicze0a778c2023-06-06 09:00:32 -07005242 static_assert(std::is_trivially_move_constructible_v<SomeStruct>);
5243 static_assert(std::is_trivially_move_assignable_v<SomeStruct>);
Devin Jeanpierre9e15d0b2023-04-06 13:18:22 -07005244 inline void SomeStruct::__crubit_field_offset_assertions() {
5245 static_assert(0 == offsetof(SomeStruct, zst1));
5246 static_assert(0 == offsetof(SomeStruct, zst2));
5247 static_assert(0 == offsetof(SomeStruct, successful_field));
5248 }
5249 }
5250 );
5251 assert_rs_matches!(
5252 result.rs_details,
5253 quote! {
5254 const _: () = assert!(::std::mem::size_of::<::rust_out::SomeStruct>() == 4);
5255 const _: () = assert!(::std::mem::align_of::<::rust_out::SomeStruct>() == 4);
5256 const _: () = assert!( memoffset::offset_of!(::rust_out::SomeStruct,
5257 zst1) == 0);
5258 const _: () = assert!( memoffset::offset_of!(::rust_out::SomeStruct,
5259 zst2) == 0);
5260 const _: () = assert!( memoffset::offset_of!(::rust_out::SomeStruct,
5261 successful_field) == 0);
5262 }
5263 );
5264 });
5265 }
5266
Lukasz Anforowicz74e090d2023-04-25 15:14:14 -07005267 #[test]
5268 fn test_format_item_struct_with_dynamically_sized_field() {
5269 let test_src = r#"
5270 pub struct DynamicallySizedStruct {
5271 /// Having a non-ZST field avoids hitting the following error:
5272 /// "Zero-sized types (ZSTs) are not supported (b/258259459)"
5273 _non_zst_field: f32,
5274 _dynamically_sized_field: [i32],
5275 }
5276 "#;
5277 test_format_item(test_src, "DynamicallySizedStruct", |result| {
5278 let err = result.unwrap_err();
5279 assert_eq!(err, "Bindings for dynamically sized types are not supported.");
5280 });
5281 }
5282
Lukasz Anforowiczf4b37602023-06-01 16:16:34 -07005283 #[test]
5284 fn test_format_item_struct_fields_with_doc_comments() {
5285 let test_src = r#"
5286 pub struct SomeStruct {
5287 /// Documentation of `successful_field`.
5288 pub successful_field: i32,
5289
5290 /// Documentation of `unsupported_field`.
5291 pub unsupported_field: Option<[i32; 3]>,
5292 }
5293 "#;
5294 test_format_item(test_src, "SomeStruct", |result| {
5295 let result = result.unwrap().unwrap();
5296 let main_api = &result.main_api;
5297 let comment_for_successful_field = " Documentation of `successful_field`.\n\n\
5298 Generated from: <crubit_unittests.rs>;l=4";
5299 let comment_for_unsupported_field = "Field type has been replaced with a blob of bytes: \
5300 Generic types are not supported yet (b/259749095)";
5301 assert_cc_matches!(
5302 main_api.tokens,
5303 quote! {
5304 ...
5305 struct ... SomeStruct final {
5306 ...
5307 private:
5308 __COMMENT__ #comment_for_unsupported_field
5309 unsigned char unsupported_field[16];
5310 public:
Lukasz Anforowicz82502222023-06-23 09:27:14 -07005311 union {
5312 __COMMENT__ #comment_for_successful_field
5313 std::int32_t successful_field;
5314 };
Lukasz Anforowiczf4b37602023-06-01 16:16:34 -07005315 private:
Lukasz Anforowiczb9e515a2023-07-05 12:48:22 -07005316 static void __crubit_field_offset_assertions();
Lukasz Anforowiczf4b37602023-06-01 16:16:34 -07005317 };
5318 ...
5319 }
5320 );
5321 });
5322 }
5323
Lukasz Anforowicz75894832022-11-22 18:25:28 -08005324 /// This is a test for an enum that only has `EnumItemDiscriminant` items
5325 /// (and doesn't have `EnumItemTuple` or `EnumItemStruct` items). See
5326 /// also https://doc.rust-lang.org/reference/items/enumerations.html
5327 #[test]
Lukasz Anforowicz32ad9562023-03-10 14:48:25 -08005328 fn test_format_item_enum_with_only_discriminant_items() {
Lukasz Anforowicz75894832022-11-22 18:25:28 -08005329 let test_src = r#"
5330 pub enum SomeEnum {
5331 Red,
5332 Green = 123,
5333 Blue,
5334 }
5335
5336 const _: () = assert!(std::mem::size_of::<SomeEnum>() == 1);
5337 const _: () = assert!(std::mem::align_of::<SomeEnum>() == 1);
5338 "#;
Lukasz Anforowicze465b9e2023-02-06 15:00:25 -08005339 test_format_item(test_src, "SomeEnum", |result| {
Lukasz Anforowicz73a2c0d2023-03-22 14:07:00 -07005340 let result = result.unwrap().unwrap();
5341 let main_api = &result.main_api;
Lukasz Anforowicz672c5152023-03-15 16:23:00 -07005342 let no_fields_msg = "Field type has been replaced with a blob of bytes: \
Lukasz Anforowicz2eb24292023-03-14 09:38:21 -07005343 No support for bindings of individual fields of \
5344 `union` (b/272801632) or `enum`";
Lukasz Anforowicz32ad9562023-03-10 14:48:25 -08005345 assert_cc_matches!(
5346 main_api.tokens,
5347 quote! {
5348 ...
Devin Jeanpierre64ac8ad2023-05-30 17:22:55 -07005349 struct CRUBIT_INTERNAL_RUST_TYPE(...) alignas(1) SomeEnum final {
Lukasz Anforowicz32ad9562023-03-10 14:48:25 -08005350 public:
Lukasz Anforowicz9924c432023-04-04 12:51:22 -07005351 __COMMENT__ "`SomeEnum` doesn't implement the `Default` trait"
Lukasz Anforowicz32ad9562023-03-10 14:48:25 -08005352 SomeEnum() = delete;
5353
Lukasz Anforowicz57c70142023-07-07 08:35:04 -07005354 __COMMENT__ "No custom `Drop` impl and no custom \"drop glue\" required"
5355 ~SomeEnum() = default;
Lukasz Anforowicz32ad9562023-03-10 14:48:25 -08005356 SomeEnum(SomeEnum&&) = default;
Lukasz Anforowiczc12fc182023-06-06 08:57:47 -07005357 SomeEnum& operator=(SomeEnum&&) = default;
Lukasz Anforowicz32ad9562023-03-10 14:48:25 -08005358
Lukasz Anforowicz57c70142023-07-07 08:35:04 -07005359 __COMMENT__ "`SomeEnum` doesn't implement the `Clone` trait"
5360 SomeEnum(const SomeEnum&) = delete;
5361 SomeEnum& operator=(const SomeEnum&) = delete;
Lukasz Anforowicz32ad9562023-03-10 14:48:25 -08005362 private:
Lukasz Anforowicz2eb24292023-03-14 09:38:21 -07005363 __COMMENT__ #no_fields_msg
5364 unsigned char __opaque_blob_of_bytes[1];
Lukasz Anforowicz68c88822023-06-01 16:06:15 -07005365 private:
Lukasz Anforowiczb9e515a2023-07-05 12:48:22 -07005366 static void __crubit_field_offset_assertions();
Lukasz Anforowicz32ad9562023-03-10 14:48:25 -08005367 };
5368 }
5369 );
5370 assert_cc_matches!(
Lukasz Anforowicz73a2c0d2023-03-22 14:07:00 -07005371 result.cc_details.tokens,
Lukasz Anforowicz32ad9562023-03-10 14:48:25 -08005372 quote! {
5373 static_assert(sizeof(SomeEnum) == 1, ...);
5374 static_assert(alignof(SomeEnum) == 1, ...);
5375 }
5376 );
5377 assert_rs_matches!(
Lukasz Anforowicz73a2c0d2023-03-22 14:07:00 -07005378 result.rs_details,
Lukasz Anforowicz32ad9562023-03-10 14:48:25 -08005379 quote! {
5380 const _: () = assert!(::std::mem::size_of::<::rust_out::SomeEnum>() == 1);
5381 const _: () = assert!(::std::mem::align_of::<::rust_out::SomeEnum>() == 1);
5382 }
5383 );
Lukasz Anforowicz75894832022-11-22 18:25:28 -08005384 });
5385 }
5386
5387 /// This is a test for an enum that has `EnumItemTuple` and `EnumItemStruct`
5388 /// items. See also https://doc.rust-lang.org/reference/items/enumerations.html
5389 #[test]
Lukasz Anforowicz32ad9562023-03-10 14:48:25 -08005390 fn test_format_item_enum_with_tuple_and_struct_items() {
Lukasz Anforowicz75894832022-11-22 18:25:28 -08005391 let test_src = r#"
5392 pub enum Point {
5393 Cartesian(f32, f32),
5394 Polar{ dist: f32, angle: f32 },
5395 }
5396
5397 const _: () = assert!(std::mem::size_of::<Point>() == 12);
5398 const _: () = assert!(std::mem::align_of::<Point>() == 4);
5399 "#;
Lukasz Anforowicze465b9e2023-02-06 15:00:25 -08005400 test_format_item(test_src, "Point", |result| {
Lukasz Anforowicz73a2c0d2023-03-22 14:07:00 -07005401 let result = result.unwrap().unwrap();
5402 let main_api = &result.main_api;
Lukasz Anforowicz672c5152023-03-15 16:23:00 -07005403 let no_fields_msg = "Field type has been replaced with a blob of bytes: \
Lukasz Anforowicz2eb24292023-03-14 09:38:21 -07005404 No support for bindings of individual fields of \
5405 `union` (b/272801632) or `enum`";
Lukasz Anforowicz32ad9562023-03-10 14:48:25 -08005406 assert_cc_matches!(
5407 main_api.tokens,
5408 quote! {
5409 ...
Devin Jeanpierre64ac8ad2023-05-30 17:22:55 -07005410 struct CRUBIT_INTERNAL_RUST_TYPE(...) alignas(4) Point final {
Lukasz Anforowicz32ad9562023-03-10 14:48:25 -08005411 public:
Lukasz Anforowicz9924c432023-04-04 12:51:22 -07005412 __COMMENT__ "`Point` doesn't implement the `Default` trait"
Lukasz Anforowicz32ad9562023-03-10 14:48:25 -08005413 Point() = delete;
5414
Lukasz Anforowicz57c70142023-07-07 08:35:04 -07005415 __COMMENT__ "No custom `Drop` impl and no custom \"drop glue\" required"
5416 ~Point() = default;
Lukasz Anforowicz32ad9562023-03-10 14:48:25 -08005417 Point(Point&&) = default;
Lukasz Anforowiczc12fc182023-06-06 08:57:47 -07005418 Point& operator=(Point&&) = default;
Lukasz Anforowicz32ad9562023-03-10 14:48:25 -08005419
Lukasz Anforowicz57c70142023-07-07 08:35:04 -07005420 __COMMENT__ "`Point` doesn't implement the `Clone` trait"
5421 Point(const Point&) = delete;
5422 Point& operator=(const Point&) = delete;
Lukasz Anforowicz32ad9562023-03-10 14:48:25 -08005423 private:
Lukasz Anforowicz2eb24292023-03-14 09:38:21 -07005424 __COMMENT__ #no_fields_msg
5425 unsigned char __opaque_blob_of_bytes[12];
Lukasz Anforowicz68c88822023-06-01 16:06:15 -07005426 private:
Lukasz Anforowiczb9e515a2023-07-05 12:48:22 -07005427 static void __crubit_field_offset_assertions();
Lukasz Anforowicz32ad9562023-03-10 14:48:25 -08005428 };
5429 }
5430 );
5431 assert_cc_matches!(
Lukasz Anforowicz73a2c0d2023-03-22 14:07:00 -07005432 result.cc_details.tokens,
Lukasz Anforowicz32ad9562023-03-10 14:48:25 -08005433 quote! {
5434 static_assert(sizeof(Point) == 12, ...);
5435 static_assert(alignof(Point) == 4, ...);
5436 }
5437 );
5438 assert_rs_matches!(
Lukasz Anforowicz73a2c0d2023-03-22 14:07:00 -07005439 result.rs_details,
Lukasz Anforowicz32ad9562023-03-10 14:48:25 -08005440 quote! {
5441 const _: () = assert!(::std::mem::size_of::<::rust_out::Point>() == 12);
5442 const _: () = assert!(::std::mem::align_of::<::rust_out::Point>() == 4);
5443 }
5444 );
Lukasz Anforowicz75894832022-11-22 18:25:28 -08005445 });
5446 }
5447
5448 /// This test covers how zero-variant enums are handled. See also
5449 /// https://doc.rust-lang.org/reference/items/enumerations.html#zero-variant-enums
5450 #[test]
Lukasz Anforowicze465b9e2023-02-06 15:00:25 -08005451 fn test_format_item_unsupported_enum_zero_variants() {
Lukasz Anforowicz75894832022-11-22 18:25:28 -08005452 let test_src = r#"
5453 pub enum ZeroVariantEnum {}
5454 "#;
Lukasz Anforowicze465b9e2023-02-06 15:00:25 -08005455 test_format_item(test_src, "ZeroVariantEnum", |result| {
Lukasz Anforowicz4c19ad92022-12-16 15:23:14 -08005456 let err = result.unwrap_err();
Lukasz Anforowicz32ad9562023-03-10 14:48:25 -08005457 assert_eq!(err, "Zero-sized types (ZSTs) are not supported (b/258259459)");
Lukasz Anforowicz75894832022-11-22 18:25:28 -08005458 });
5459 }
5460
5461 /// This is a test for a `union`. See also
5462 /// https://doc.rust-lang.org/reference/items/unions.html
5463 #[test]
Lukasz Anforowicze465b9e2023-02-06 15:00:25 -08005464 fn test_format_item_union() {
Lukasz Anforowicz75894832022-11-22 18:25:28 -08005465 let test_src = r#"
5466 pub union SomeUnion {
5467 pub i: i32,
5468 pub f: f64,
5469 }
5470
5471 const _: () = assert!(std::mem::size_of::<SomeUnion>() == 8);
5472 const _: () = assert!(std::mem::align_of::<SomeUnion>() == 8);
5473 "#;
Lukasz Anforowicze465b9e2023-02-06 15:00:25 -08005474 test_format_item(test_src, "SomeUnion", |result| {
Lukasz Anforowicz73a2c0d2023-03-22 14:07:00 -07005475 let result = result.unwrap().unwrap();
5476 let main_api = &result.main_api;
Lukasz Anforowicz672c5152023-03-15 16:23:00 -07005477 let no_fields_msg = "Field type has been replaced with a blob of bytes: \
Lukasz Anforowicz2eb24292023-03-14 09:38:21 -07005478 No support for bindings of individual fields of \
5479 `union` (b/272801632) or `enum`";
Lukasz Anforowicz75894832022-11-22 18:25:28 -08005480 assert_cc_matches!(
Lukasz Anforowicz0b8a02f2023-02-10 15:55:40 -08005481 main_api.tokens,
Lukasz Anforowicz75894832022-11-22 18:25:28 -08005482 quote! {
Googler47fd9572023-01-20 09:57:32 -08005483 ...
Devin Jeanpierre64ac8ad2023-05-30 17:22:55 -07005484 union CRUBIT_INTERNAL_RUST_TYPE(...) alignas(8) SomeUnion final {
Lukasz Anforowicz75894832022-11-22 18:25:28 -08005485 public:
Lukasz Anforowicz9924c432023-04-04 12:51:22 -07005486 __COMMENT__ "`SomeUnion` doesn't implement the `Default` trait"
Lukasz Anforowicz75894832022-11-22 18:25:28 -08005487 SomeUnion() = delete;
5488
Lukasz Anforowicz57c70142023-07-07 08:35:04 -07005489 __COMMENT__ "No custom `Drop` impl and no custom \"drop glue\" required"
5490 ~SomeUnion() = default;
Lukasz Anforowicz75894832022-11-22 18:25:28 -08005491 SomeUnion(SomeUnion&&) = default;
Lukasz Anforowiczc12fc182023-06-06 08:57:47 -07005492 SomeUnion& operator=(SomeUnion&&) = default;
Lukasz Anforowicz75894832022-11-22 18:25:28 -08005493
Lukasz Anforowicz57c70142023-07-07 08:35:04 -07005494 __COMMENT__ "`SomeUnion` doesn't implement the `Clone` trait"
5495 SomeUnion(const SomeUnion&) = delete;
5496 SomeUnion& operator=(const SomeUnion&) = delete;
Lukasz Anforowicz75894832022-11-22 18:25:28 -08005497 private:
Lukasz Anforowicz2eb24292023-03-14 09:38:21 -07005498 __COMMENT__ #no_fields_msg
5499 unsigned char __opaque_blob_of_bytes[8];
Lukasz Anforowicz68c88822023-06-01 16:06:15 -07005500 private:
Lukasz Anforowiczb9e515a2023-07-05 12:48:22 -07005501 static void __crubit_field_offset_assertions();
Lukasz Anforowicz75894832022-11-22 18:25:28 -08005502 };
Lukasz Anforowicz0b8a02f2023-02-10 15:55:40 -08005503 }
5504 );
5505 assert_cc_matches!(
Lukasz Anforowicz73a2c0d2023-03-22 14:07:00 -07005506 result.cc_details.tokens,
Lukasz Anforowicz0b8a02f2023-02-10 15:55:40 -08005507 quote! {
Lukasz Anforowicz75894832022-11-22 18:25:28 -08005508 static_assert(sizeof(SomeUnion) == 8, ...);
5509 static_assert(alignof(SomeUnion) == 8, ...);
5510 }
5511 );
5512 assert_rs_matches!(
Lukasz Anforowicz73a2c0d2023-03-22 14:07:00 -07005513 result.rs_details,
Lukasz Anforowicz75894832022-11-22 18:25:28 -08005514 quote! {
Lukasz Anforowicz8b475a42022-11-29 09:33:02 -08005515 const _: () = assert!(::std::mem::size_of::<::rust_out::SomeUnion>() == 8);
5516 const _: () = assert!(::std::mem::align_of::<::rust_out::SomeUnion>() == 8);
Lukasz Anforowicz75894832022-11-22 18:25:28 -08005517 }
5518 );
5519 });
5520 }
5521
Lukasz Anforowicze4333062022-10-17 14:47:53 -07005522 #[test]
Lukasz Anforowicze465b9e2023-02-06 15:00:25 -08005523 fn test_format_item_doc_comments_union() {
Googler04329a12022-12-02 00:56:33 -08005524 let test_src = r#"
5525 /// Doc for some union.
5526 pub union SomeUnionWithDocs {
5527 /// Doc for a field in a union.
5528 pub i: i32,
5529 pub f: f64
5530 }
5531 "#;
Lukasz Anforowicze465b9e2023-02-06 15:00:25 -08005532 test_format_item(test_src, "SomeUnionWithDocs", |result| {
Lukasz Anforowicz73a2c0d2023-03-22 14:07:00 -07005533 let result = result.unwrap().unwrap();
5534 let main_api = &result.main_api;
Googler47fd9572023-01-20 09:57:32 -08005535 let comment = " Doc for some union.\n\n\
5536 Generated from: <crubit_unittests.rs>;l=3";
Googler04329a12022-12-02 00:56:33 -08005537 assert_cc_matches!(
Lukasz Anforowicz0b8a02f2023-02-10 15:55:40 -08005538 main_api.tokens,
Googler04329a12022-12-02 00:56:33 -08005539 quote! {
5540 __COMMENT__ #comment
Lukasz Anforowiczcc7a76b2023-02-28 14:19:42 -08005541 union ... SomeUnionWithDocs final {
Googler04329a12022-12-02 00:56:33 -08005542 ...
5543 }
5544 ...
5545 }
5546 );
5547 });
5548 }
5549
5550 #[test]
Lukasz Anforowicze465b9e2023-02-06 15:00:25 -08005551 fn test_format_item_doc_comments_enum() {
Googler04329a12022-12-02 00:56:33 -08005552 let test_src = r#"
5553 /** Doc for some enum. */
5554 pub enum SomeEnumWithDocs {
Lukasz Anforowicz32ad9562023-03-10 14:48:25 -08005555 Kind1(i32),
Googler04329a12022-12-02 00:56:33 -08005556 }
5557 "#;
Lukasz Anforowicze465b9e2023-02-06 15:00:25 -08005558 test_format_item(test_src, "SomeEnumWithDocs", |result| {
Lukasz Anforowicz73a2c0d2023-03-22 14:07:00 -07005559 let result = result.unwrap().unwrap();
5560 let main_api = &result.main_api;
Lukasz Anforowicz32ad9562023-03-10 14:48:25 -08005561 let comment = " Doc for some enum. \n\n\
5562 Generated from: <crubit_unittests.rs>;l=3";
5563 assert_cc_matches!(
5564 main_api.tokens,
5565 quote! {
5566 __COMMENT__ #comment
5567 struct ... SomeEnumWithDocs final {
5568 ...
5569 }
5570 ...
5571 }
5572 );
Googler04329a12022-12-02 00:56:33 -08005573 });
5574 }
5575
5576 #[test]
Lukasz Anforowicze465b9e2023-02-06 15:00:25 -08005577 fn test_format_item_doc_comments_struct() {
Googler04329a12022-12-02 00:56:33 -08005578 let test_src = r#"
5579 #![allow(dead_code)]
5580 #[doc = "Doc for some struct."]
5581 pub struct SomeStructWithDocs {
Lukasz Anforowiczcc7a76b2023-02-28 14:19:42 -08005582 #[doc = "Doc for first field."]
Googler04329a12022-12-02 00:56:33 -08005583 some_field : i32,
5584 }
5585 "#;
Lukasz Anforowicze465b9e2023-02-06 15:00:25 -08005586 test_format_item(test_src, "SomeStructWithDocs", |result| {
Lukasz Anforowicz73a2c0d2023-03-22 14:07:00 -07005587 let result = result.unwrap().unwrap();
5588 let main_api = &result.main_api;
Googler47fd9572023-01-20 09:57:32 -08005589 let comment = "Doc for some struct.\n\n\
5590 Generated from: <crubit_unittests.rs>;l=4";
Googler04329a12022-12-02 00:56:33 -08005591 assert_cc_matches!(
Lukasz Anforowicz0b8a02f2023-02-10 15:55:40 -08005592 main_api.tokens,
Googler04329a12022-12-02 00:56:33 -08005593 quote! {
5594 __COMMENT__ #comment
5595 struct ... SomeStructWithDocs final {
5596 ...
5597 }
5598 ...
5599 }
5600 );
5601 });
5602 }
5603
5604 #[test]
Lukasz Anforowicze465b9e2023-02-06 15:00:25 -08005605 fn test_format_item_doc_comments_tuple_struct() {
Googler04329a12022-12-02 00:56:33 -08005606 let test_src = r#"
5607 /// Doc for some tuple struct.
5608 pub struct SomeTupleStructWithDocs(i32);
5609 "#;
Lukasz Anforowicze465b9e2023-02-06 15:00:25 -08005610 test_format_item(test_src, "SomeTupleStructWithDocs", |result| {
Lukasz Anforowicz73a2c0d2023-03-22 14:07:00 -07005611 let result = result.unwrap().unwrap();
5612 let main_api = &result.main_api;
Googler47fd9572023-01-20 09:57:32 -08005613 let comment = " Doc for some tuple struct.\n\n\
5614 Generated from: <crubit_unittests.rs>;l=3";
Googler04329a12022-12-02 00:56:33 -08005615 assert_cc_matches!(
Lukasz Anforowicz0b8a02f2023-02-10 15:55:40 -08005616 main_api.tokens,
Googler04329a12022-12-02 00:56:33 -08005617 quote! {
5618 __COMMENT__ #comment
5619 struct ... SomeTupleStructWithDocs final {
5620 ...
5621 }
5622 ...
5623 },
5624 );
5625 });
5626 }
5627
5628 #[test]
Lukasz Anforowicze465b9e2023-02-06 15:00:25 -08005629 fn test_format_item_source_loc_macro_rules() {
Googler47fd9572023-01-20 09:57:32 -08005630 let test_src = r#"
5631 macro_rules! some_tuple_struct_macro_for_testing_source_loc {
5632 () => {
5633 /// Some doc on SomeTupleStructMacroForTesingSourceLoc.
5634 pub struct SomeTupleStructMacroForTesingSourceLoc(i32);
5635 };
5636 }
5637
5638 some_tuple_struct_macro_for_testing_source_loc!();
5639 "#;
Lukasz Anforowicze465b9e2023-02-06 15:00:25 -08005640 test_format_item(test_src, "SomeTupleStructMacroForTesingSourceLoc", |result| {
Lukasz Anforowicz73a2c0d2023-03-22 14:07:00 -07005641 let result = result.unwrap().unwrap();
5642 let main_api = &result.main_api;
Googler47fd9572023-01-20 09:57:32 -08005643 let source_loc_comment = " Some doc on SomeTupleStructMacroForTesingSourceLoc.\n\n\
5644 Generated from: <crubit_unittests.rs>;l=5";
5645 assert_cc_matches!(
Lukasz Anforowicz0b8a02f2023-02-10 15:55:40 -08005646 main_api.tokens,
Googler47fd9572023-01-20 09:57:32 -08005647 quote! {
5648 __COMMENT__ #source_loc_comment
5649 struct ... SomeTupleStructMacroForTesingSourceLoc final {
5650 ...
5651 }
5652 ...
5653 },
5654 );
5655 });
5656 }
5657
5658 #[test]
Lukasz Anforowicze465b9e2023-02-06 15:00:25 -08005659 fn test_format_item_source_loc_with_no_doc_comment() {
Googler47fd9572023-01-20 09:57:32 -08005660 let test_src = r#"
5661 pub struct SomeTupleStructWithNoDocComment(i32);
5662 "#;
Lukasz Anforowicze465b9e2023-02-06 15:00:25 -08005663 test_format_item(test_src, "SomeTupleStructWithNoDocComment", |result| {
Lukasz Anforowicz73a2c0d2023-03-22 14:07:00 -07005664 let result = result.unwrap().unwrap();
5665 let main_api = &result.main_api;
Googler47fd9572023-01-20 09:57:32 -08005666 let comment = "Generated from: <crubit_unittests.rs>;l=2";
5667 assert_cc_matches!(
Lukasz Anforowicz0b8a02f2023-02-10 15:55:40 -08005668 main_api.tokens,
Googler47fd9572023-01-20 09:57:32 -08005669 quote! {
5670 __COMMENT__ #comment
5671 struct ... SomeTupleStructWithNoDocComment final {
5672 ...
5673 }
5674 ...
5675 },
5676 );
5677 });
5678 }
5679
5680 #[test]
Lukasz Anforowicze465b9e2023-02-06 15:00:25 -08005681 fn test_format_item_unsupported_static_value() {
Lukasz Anforowicze4333062022-10-17 14:47:53 -07005682 let test_src = r#"
Lukasz Anforowicz75894832022-11-22 18:25:28 -08005683 #[no_mangle]
5684 pub static STATIC_VALUE: i32 = 42;
Lukasz Anforowicze4333062022-10-17 14:47:53 -07005685 "#;
Lukasz Anforowicze465b9e2023-02-06 15:00:25 -08005686 test_format_item(test_src, "STATIC_VALUE", |result| {
Lukasz Anforowicz4c19ad92022-12-16 15:23:14 -08005687 let err = result.unwrap_err();
Lukasz Anforowicz75894832022-11-22 18:25:28 -08005688 assert_eq!(err, "Unsupported rustc_hir::hir::ItemKind: static item");
Lukasz Anforowicz8a833412022-10-14 14:44:13 -07005689 });
5690 }
5691
Lukasz Anforowicz08b8ae12023-02-06 10:51:09 -08005692 #[test]
Lukasz Anforowicze465b9e2023-02-06 15:00:25 -08005693 fn test_format_item_unsupported_const_value() {
Lukasz Anforowicz08b8ae12023-02-06 10:51:09 -08005694 let test_src = r#"
5695 pub const CONST_VALUE: i32 = 42;
5696 "#;
Lukasz Anforowicze465b9e2023-02-06 15:00:25 -08005697 test_format_item(test_src, "CONST_VALUE", |result| {
Lukasz Anforowicz08b8ae12023-02-06 10:51:09 -08005698 let err = result.unwrap_err();
5699 assert_eq!(err, "Unsupported rustc_hir::hir::ItemKind: constant item");
5700 });
5701 }
5702
5703 #[test]
Lukasz Anforowicze465b9e2023-02-06 15:00:25 -08005704 fn test_format_item_unsupported_type_alias() {
Lukasz Anforowicz08b8ae12023-02-06 10:51:09 -08005705 let test_src = r#"
5706 pub type TypeAlias = i32;
5707 "#;
Lukasz Anforowicze465b9e2023-02-06 15:00:25 -08005708 test_format_item(test_src, "TypeAlias", |result| {
Lukasz Anforowicz08b8ae12023-02-06 10:51:09 -08005709 // TODO(b/254096006): Add support for type alias definitions.
5710 let err = result.unwrap_err();
5711 assert_eq!(err, "Unsupported rustc_hir::hir::ItemKind: type alias");
5712 });
5713 }
5714
Lukasz Anforowicz14229762023-02-10 15:28:33 -08005715 #[test]
5716 fn test_format_item_unsupported_impl_item_const_value() {
5717 let test_src = r#"
5718 pub struct SomeStruct(i32);
5719
5720 impl SomeStruct {
5721 pub const CONST_VALUE: i32 = 42;
5722 }
5723 "#;
5724 test_format_item(test_src, "SomeStruct", |result| {
Lukasz Anforowicz73a2c0d2023-03-22 14:07:00 -07005725 let result = result.unwrap().unwrap();
5726 let main_api = &result.main_api;
Lukasz Anforowicz2eb24292023-03-14 09:38:21 -07005727 assert!(!main_api.prereqs.is_empty());
Lukasz Anforowicz14229762023-02-10 15:28:33 -08005728 let unsupported_msg = "Error generating bindings for `SomeStruct::CONST_VALUE` \
5729 defined at <crubit_unittests.rs>;l=5: \
Lukasz Anforowicz56d94fc2023-02-17 12:23:20 -08005730 Unsupported `impl` item kind: Const";
Lukasz Anforowicz14229762023-02-10 15:28:33 -08005731 assert_cc_matches!(
Lukasz Anforowicz0b8a02f2023-02-10 15:55:40 -08005732 main_api.tokens,
Lukasz Anforowicz14229762023-02-10 15:28:33 -08005733 quote! {
5734 ...
Devin Jeanpierre64ac8ad2023-05-30 17:22:55 -07005735 struct CRUBIT_INTERNAL_RUST_TYPE(...) alignas(4) SomeStruct final {
Lukasz Anforowicz14229762023-02-10 15:28:33 -08005736 ...
5737 __COMMENT__ #unsupported_msg
5738 ...
5739 };
5740 ...
5741 }
5742 );
5743 });
5744 }
5745
Lukasz Anforowiczbe25c762023-01-17 12:43:52 -08005746 /// `test_format_ret_ty_for_cc_successes` provides test coverage for cases
Lukasz Anforowicz8574c9d2023-04-13 15:11:20 -07005747 /// where `format_ty_for_cc` takes `TypeLocation::FnReturn` and returns
5748 /// an `Ok(...)`. Additional testcases are covered by
5749 /// `test_format_ty_for_cc_successes`.
Lukasz Anforowicz4f9f5522022-10-17 15:03:37 -07005750 #[test]
Lukasz Anforowicz8a68f502022-11-15 08:43:43 -08005751 fn test_format_ret_ty_for_cc_successes() {
Lukasz Anforowicz903fc632022-10-25 08:55:33 -07005752 let testcases = [
5753 // ( <Rust type>, <expected C++ type> )
5754 ("bool", "bool"), // TyKind::Bool
5755 ("()", "void"),
5756 // TODO(b/254507801): Expect `crubit::Never` instead (see the bug for more
5757 // details).
5758 ("!", "void"),
Lukasz Anforowicz5c1b3ad2023-04-13 17:05:00 -07005759 (
5760 "extern \"C\" fn (f32, f32) -> f32",
5761 "crubit :: type_identity_t < float (float , float) > &",
5762 ),
Lukasz Anforowicz903fc632022-10-25 08:55:33 -07005763 ];
Lukasz Anforowicz0adaa3e2023-06-12 10:25:18 -07005764 test_ty(TypeLocation::FnReturn, &testcases, quote! {}, |desc, tcx, ty, expected| {
Lukasz Anforowiczed17d052022-11-02 12:07:28 -07005765 let actual = {
Lukasz Anforowiczbe25c762023-01-17 12:43:52 -08005766 let input = bindings_input_for_tests(tcx);
Lukasz Anforowicz8574c9d2023-04-13 15:11:20 -07005767 let cc_snippet = format_ty_for_cc(&input, ty, TypeLocation::FnReturn).unwrap();
Lukasz Anforowicz3744e502022-12-02 08:40:38 -08005768 cc_snippet.tokens.to_string()
Lukasz Anforowiczed17d052022-11-02 12:07:28 -07005769 };
Lukasz Anforowicz903fc632022-10-25 08:55:33 -07005770 let expected = expected.parse::<TokenStream>().unwrap().to_string();
5771 assert_eq!(actual, expected, "{desc}");
5772 });
5773 }
5774
Lukasz Anforowicz4c19ad92022-12-16 15:23:14 -08005775 /// `test_format_ty_for_cc_successes` provides test coverage for cases where
5776 /// `format_ty_for_cc` returns an `Ok(...)`.
5777 ///
5778 /// Note that using `std::int8_t` (instead of `::std::int8_t`) has been an
5779 /// explicit decision. The "Google C++ Style Guide" suggests to "avoid
5780 /// nested namespaces that match well-known top-level namespaces" and "in
5781 /// particular, [...] not create any nested std namespaces.". It
5782 /// seems desirable if the generated bindings conform to this aspect of the
5783 /// style guide, because it makes things easier for *users* of these
5784 /// bindings.
Lukasz Anforowicz903fc632022-10-25 08:55:33 -07005785 #[test]
Lukasz Anforowicz8a68f502022-11-15 08:43:43 -08005786 fn test_format_ty_for_cc_successes() {
Lukasz Anforowicz4f9f5522022-10-17 15:03:37 -07005787 let testcases = [
Lukasz Anforowiczf4501b42023-01-07 09:41:29 -08005788 // ( <Rust type>, (<expected C++ type>,
5789 // <expected #include>,
5790 // <expected prereq def>,
5791 // <expected prereq fwd decl>) )
5792 ("bool", ("bool", "", "", "")),
5793 ("f32", ("float", "", "", "")),
5794 ("f64", ("double", "", "", "")),
Lukasz Anforowicza782bda2023-01-17 14:04:50 -08005795 ("i8", ("std::int8_t", "<cstdint>", "", "")),
5796 ("i16", ("std::int16_t", "<cstdint>", "", "")),
5797 ("i32", ("std::int32_t", "<cstdint>", "", "")),
5798 ("i64", ("std::int64_t", "<cstdint>", "", "")),
5799 ("isize", ("std::intptr_t", "<cstdint>", "", "")),
5800 ("u8", ("std::uint8_t", "<cstdint>", "", "")),
5801 ("u16", ("std::uint16_t", "<cstdint>", "", "")),
5802 ("u32", ("std::uint32_t", "<cstdint>", "", "")),
5803 ("u64", ("std::uint64_t", "<cstdint>", "", "")),
5804 ("usize", ("std::uintptr_t", "<cstdint>", "", "")),
Lukasz Anforowiczec0b64e2023-02-17 14:31:12 -08005805 ("char", ("rs_std::rs_char", "\"crubit/support/for/tests/rs_std/rs_char.h\"", "", "")),
Lukasz Anforowiczf4501b42023-01-07 09:41:29 -08005806 ("SomeStruct", ("::rust_out::SomeStruct", "", "SomeStruct", "")),
Lukasz Anforowicz32ad9562023-03-10 14:48:25 -08005807 ("SomeEnum", ("::rust_out::SomeEnum", "", "SomeEnum", "")),
Lukasz Anforowiczf4501b42023-01-07 09:41:29 -08005808 ("SomeUnion", ("::rust_out::SomeUnion", "", "SomeUnion", "")),
Lukasz Anforowiczcf783022023-06-15 10:36:58 -07005809 ("*const i32", ("std :: int32_t const *", "<cstdint>", "", "")),
Lukasz Anforowicza782bda2023-01-17 14:04:50 -08005810 ("*mut i32", ("std::int32_t*", "<cstdint>", "", "")),
Lukasz Anforowiczaa2de7e2023-06-15 11:32:05 -07005811 (
5812 "&'static i32",
5813 (
5814 "std :: int32_t const & [[clang :: annotate_type (\"lifetime\" , \"static\")]]",
5815 "<cstdint>",
5816 "",
5817 "",
5818 ),
5819 ),
5820 (
5821 "&'static mut i32",
5822 (
5823 "std :: int32_t & [[clang :: annotate_type (\"lifetime\" , \"static\")]]",
5824 "<cstdint>",
5825 "",
5826 "",
5827 ),
5828 ),
Lukasz Anforowiczf4501b42023-01-07 09:41:29 -08005829 // `SomeStruct` is a `fwd_decls` prerequisite (not `defs` prerequisite):
5830 ("*mut SomeStruct", ("::rust_out::SomeStruct*", "", "", "SomeStruct")),
5831 // Testing propagation of deeper/nested `fwd_decls`:
5832 ("*mut *mut SomeStruct", (":: rust_out :: SomeStruct * *", "", "", "SomeStruct")),
Lukasz Anforowiczf75c3912023-06-12 11:27:16 -07005833 // Testing propagation of `const` / `mut` qualifiers:
Lukasz Anforowiczcf783022023-06-15 10:36:58 -07005834 ("*mut *const f32", ("float const * *", "", "", "")),
5835 ("*const *mut f32", ("float * const *", "", "", "")),
Lukasz Anforowicz5c1b3ad2023-04-13 17:05:00 -07005836 (
Lukasz Anforowicz0adaa3e2023-06-12 10:25:18 -07005837 // Rust function pointers are non-nullable, so when function pointers are used as a
5838 // parameter type (i.e. in `TypeLocation::FnParam`) then we can translate to
5839 // generate a C++ function *reference*, rather than a C++ function *pointer*.
Lukasz Anforowicz5c1b3ad2023-04-13 17:05:00 -07005840 "extern \"C\" fn (f32, f32) -> f32",
5841 (
Lukasz Anforowicz0adaa3e2023-06-12 10:25:18 -07005842 "crubit :: type_identity_t < float (float , float) > &",
5843 "\"crubit/support/for/tests/internal/cxx20_backports.h\"",
5844 "",
5845 "",
5846 ),
5847 ),
5848 (
5849 // Nested function pointer (i.e. `TypeLocation::Other`) means that
5850 // we need to generate a C++ function *pointer*, rather than a C++
5851 // function *reference*.
5852 "*const extern \"C\" fn (f32, f32) -> f32",
5853 (
Lukasz Anforowiczcf783022023-06-15 10:36:58 -07005854 "crubit :: type_identity_t < float (float , float) > * const *",
Lukasz Anforowicz5c1b3ad2023-04-13 17:05:00 -07005855 "\"crubit/support/for/tests/internal/cxx20_backports.h\"",
5856 "",
5857 "",
5858 ),
5859 ),
Lukasz Anforowicz4f9f5522022-10-17 15:03:37 -07005860 // Extra parens/sugar are expected to be ignored:
Lukasz Anforowiczf4501b42023-01-07 09:41:29 -08005861 ("(bool)", ("bool", "", "", "")),
Lukasz Anforowicz4f9f5522022-10-17 15:03:37 -07005862 ];
Lukasz Anforowicz40472722022-11-08 13:29:08 -08005863 let preamble = quote! {
5864 #![allow(unused_parens)]
Lukasz Anforowicz75894832022-11-22 18:25:28 -08005865
5866 pub struct SomeStruct {
5867 pub x: i32,
5868 pub y: i32,
5869 }
Lukasz Anforowicz32ad9562023-03-10 14:48:25 -08005870 pub enum SomeEnum {
5871 Cartesian{x: f64, y: f64},
5872 Polar{angle: f64, dist: f64},
5873 }
Lukasz Anforowicz75894832022-11-22 18:25:28 -08005874 pub union SomeUnion {
5875 pub x: i32,
5876 pub y: i32,
5877 }
Lukasz Anforowicz40472722022-11-08 13:29:08 -08005878 };
Lukasz Anforowicz5c9c00d2022-12-02 08:41:39 -08005879 test_ty(
Lukasz Anforowicz0adaa3e2023-06-12 10:25:18 -07005880 TypeLocation::FnParam,
Lukasz Anforowicz5c9c00d2022-12-02 08:41:39 -08005881 &testcases,
5882 preamble,
Lukasz Anforowiczf4501b42023-01-07 09:41:29 -08005883 |desc, tcx, ty,
5884 (expected_tokens, expected_include, expected_prereq_def, expected_prereq_fwd_decl)| {
5885 let (actual_tokens, actual_prereqs) = {
Lukasz Anforowiczbe25c762023-01-17 12:43:52 -08005886 let input = bindings_input_for_tests(tcx);
Lukasz Anforowicz0adaa3e2023-06-12 10:25:18 -07005887 let s = format_ty_for_cc(&input, ty, TypeLocation::FnParam).unwrap();
Lukasz Anforowiczf4501b42023-01-07 09:41:29 -08005888 (s.tokens.to_string(), s.prereqs)
5889 };
5890 let (actual_includes, actual_prereq_defs, actual_prereq_fwd_decls) =
5891 (actual_prereqs.includes, actual_prereqs.defs, actual_prereqs.fwd_decls);
Lukasz Anforowiczed17d052022-11-02 12:07:28 -07005892
Lukasz Anforowicz5c9c00d2022-12-02 08:41:39 -08005893 let expected_tokens = expected_tokens.parse::<TokenStream>().unwrap().to_string();
5894 assert_eq!(actual_tokens, expected_tokens, "{desc}");
Lukasz Anforowiczed17d052022-11-02 12:07:28 -07005895
Lukasz Anforowiczf4501b42023-01-07 09:41:29 -08005896 if expected_include.is_empty() {
Lukasz Anforowiczaa2de7e2023-06-15 11:32:05 -07005897 assert!(
5898 actual_includes.is_empty(),
5899 "{desc}: `actual_includes` is unexpectedly non-empty: {actual_includes:?}",
5900 );
Lukasz Anforowiczf4501b42023-01-07 09:41:29 -08005901 } else {
Lukasz Anforowicza782bda2023-01-17 14:04:50 -08005902 let expected_include: TokenStream = expected_include.parse().unwrap();
Lukasz Anforowiczf4501b42023-01-07 09:41:29 -08005903 assert_cc_matches!(
5904 format_cc_includes(&actual_includes),
Lukasz Anforowicza782bda2023-01-17 14:04:50 -08005905 quote! { __HASH_TOKEN__ include #expected_include }
Lukasz Anforowiczf4501b42023-01-07 09:41:29 -08005906 );
5907 }
Lukasz Anforowicz5c9c00d2022-12-02 08:41:39 -08005908
5909 if expected_prereq_def.is_empty() {
Lukasz Anforowiczaa2de7e2023-06-15 11:32:05 -07005910 assert!(
5911 actual_prereq_defs.is_empty(),
5912 "{desc}: `actual_prereq_defs` is unexpectedly non-empty",
5913 );
Lukasz Anforowicz5c9c00d2022-12-02 08:41:39 -08005914 } else {
5915 let expected_def_id = find_def_id_by_name(tcx, expected_prereq_def);
5916 assert_eq!(1, actual_prereq_defs.len());
5917 assert_eq!(expected_def_id, actual_prereq_defs.into_iter().next().unwrap());
5918 }
Lukasz Anforowiczf4501b42023-01-07 09:41:29 -08005919
5920 if expected_prereq_fwd_decl.is_empty() {
Lukasz Anforowiczaa2de7e2023-06-15 11:32:05 -07005921 assert!(
5922 actual_prereq_fwd_decls.is_empty(),
5923 "{desc}: `actual_prereq_fwd_decls` is unexpectedly non-empty",
5924 );
Lukasz Anforowiczf4501b42023-01-07 09:41:29 -08005925 } else {
5926 let expected_def_id = find_def_id_by_name(tcx, expected_prereq_fwd_decl);
5927 assert_eq!(1, actual_prereq_fwd_decls.len());
5928 assert_eq!(expected_def_id,
5929 actual_prereq_fwd_decls.into_iter().next().unwrap());
5930 }
Lukasz Anforowicz5c9c00d2022-12-02 08:41:39 -08005931 },
5932 );
Lukasz Anforowicz4f9f5522022-10-17 15:03:37 -07005933 }
5934
Lukasz Anforowicz4c19ad92022-12-16 15:23:14 -08005935 /// `test_format_ty_for_cc_failures` provides test coverage for cases where
5936 /// `format_ty_for_cc` returns an `Err(...)`.
5937 ///
5938 /// It seems okay to have no test coverage for now for the following types
5939 /// (which should never be encountered when generating bindings and where
5940 /// `format_ty_for_cc` should panic):
5941 /// - TyKind::Closure
5942 /// - TyKind::Error
5943 /// - TyKind::FnDef
5944 /// - TyKind::Infer
5945 ///
Lukasz Anforowicz0182c5c2022-12-29 10:08:50 -08005946 /// TODO(lukasza): Add test coverage (here and in the "for_rs" flavours)
5947 /// for:
Lukasz Anforowicz4c19ad92022-12-16 15:23:14 -08005948 /// - TyKind::Bound
5949 /// - TyKind::Dynamic (`dyn Eq`)
5950 /// - TyKind::Foreign (`extern type T`)
5951 /// - https://doc.rust-lang.org/beta/unstable-book/language-features/generators.html:
5952 /// TyKind::Generator, TyKind::GeneratorWitness
5953 /// - TyKind::Param
5954 /// - TyKind::Placeholder
Lukasz Anforowicz4f9f5522022-10-17 15:03:37 -07005955 #[test]
Lukasz Anforowicz8a68f502022-11-15 08:43:43 -08005956 fn test_format_ty_for_cc_failures() {
Lukasz Anforowicz4f9f5522022-10-17 15:03:37 -07005957 let testcases = [
5958 // ( <Rust type>, <expected error message> )
Lukasz Anforowicz4f9f5522022-10-17 15:03:37 -07005959 (
Lukasz Anforowicz903fc632022-10-25 08:55:33 -07005960 "()", // Empty TyKind::Tuple
Devin Jeanpierre81aec502023-04-04 15:33:39 -07005961 "`()` / `void` is only supported as a return type (b/254507801)",
Lukasz Anforowicz903fc632022-10-25 08:55:33 -07005962 ),
5963 (
5964 // TODO(b/254507801): Expect `crubit::Never` instead (see the bug for more
5965 // details).
5966 "!", // TyKind::Never
Devin Jeanpierre81aec502023-04-04 15:33:39 -07005967 "The never type `!` is only supported as a return type (b/254507801)",
Lukasz Anforowicz903fc632022-10-25 08:55:33 -07005968 ),
5969 (
5970 "(i32, i32)", // Non-empty TyKind::Tuple
Lukasz Anforowicz13794df2022-10-21 07:56:34 -07005971 "Tuples are not supported yet: (i32, i32) (b/254099023)",
Lukasz Anforowicz4f9f5522022-10-17 15:03:37 -07005972 ),
5973 (
Lukasz Anforowiczed96fcb2023-06-12 11:39:45 -07005974 "&'static &'static i32", // TyKind::Ref (nested reference - referent of reference)
Lukasz Anforowiczaa2de7e2023-06-15 11:32:05 -07005975 "Failed to format the referent of the reference type `&'static &'static i32`: \
5976 Can't format `&'static i32`, because references are only supported \
5977 in function parameter types and return types (b/286256327)",
Lukasz Anforowiczed96fcb2023-06-12 11:39:45 -07005978 ),
5979 (
5980 "extern \"C\" fn (&i32)", // TyKind::Ref (nested reference - underneath fn ptr)
5981 "Generic functions are not supported yet (b/259749023)",
5982 ),
5983 (
Lukasz Anforowicz4f9f5522022-10-17 15:03:37 -07005984 "[i32; 42]", // TyKind::Array
5985 "The following Rust type is not supported yet: [i32; 42]",
5986 ),
5987 (
5988 "&'static [i32]", // TyKind::Slice (nested underneath TyKind::Ref)
Lukasz Anforowiczaa2de7e2023-06-15 11:32:05 -07005989 "Failed to format the referent of the reference type `&'static [i32]`: \
5990 The following Rust type is not supported yet: [i32]",
Lukasz Anforowicz4f9f5522022-10-17 15:03:37 -07005991 ),
5992 (
5993 "&'static str", // TyKind::Str (nested underneath TyKind::Ref)
Lukasz Anforowiczaa2de7e2023-06-15 11:32:05 -07005994 "Failed to format the referent of the reference type `&'static str`: \
5995 The following Rust type is not supported yet: str",
Lukasz Anforowicz4f9f5522022-10-17 15:03:37 -07005996 ),
5997 (
Lukasz Anforowicz0182c5c2022-12-29 10:08:50 -08005998 "impl Eq", // TyKind::Alias
Lukasz Anforowicz0adaa3e2023-06-12 10:25:18 -07005999 "The following Rust type is not supported yet: impl Eq",
Lukasz Anforowicz4f9f5522022-10-17 15:03:37 -07006000 ),
6001 (
Lukasz Anforowicz5c1b3ad2023-04-13 17:05:00 -07006002 "fn(i32) -> i32", // TyKind::FnPtr (default ABI = "Rust")
6003 "Function pointers can't have a thunk: \
6004 Calling convention other than `extern \"C\"` requires a thunk",
6005 ),
6006 (
6007 "extern \"C\" fn (SomeStruct, f32) -> f32",
6008 "Function pointers can't have a thunk: Type of parameter #0 requires a thunk",
6009 ),
6010 (
6011 "extern \"C\" fn (f32, f32) -> SomeStruct",
6012 "Function pointers can't have a thunk: Return type requires a thunk",
6013 ),
6014 (
6015 "unsafe fn(i32) -> i32",
6016 "Bindings for `unsafe` functions are not fully designed yet (b/254095482)",
Lukasz Anforowicz4f9f5522022-10-17 15:03:37 -07006017 ),
6018 // TODO(b/254094650): Consider mapping this to Clang's (and GCC's) `__int128`
6019 // or to `absl::in128`.
6020 ("i128", "C++ doesn't have a standard equivalent of `i128` (b/254094650)"),
6021 ("u128", "C++ doesn't have a standard equivalent of `u128` (b/254094650)"),
Lukasz Anforowicz75894832022-11-22 18:25:28 -08006022 (
6023 "StructWithCustomDrop",
6024 "Failed to generate bindings for the definition of `StructWithCustomDrop`: \
Devin Jeanpierre81aec502023-04-04 15:33:39 -07006025 `Drop` trait and \"drop glue\" are not supported yet (b/258251148)",
Lukasz Anforowicz75894832022-11-22 18:25:28 -08006026 ),
Devin Jeanpierre81aec502023-04-04 15:33:39 -07006027 ("ConstGenericStruct<42>", "Generic types are not supported yet (b/259749095)"),
6028 ("TypeGenericStruct<u8>", "Generic types are not supported yet (b/259749095)"),
Lukasz Anforowicz75894832022-11-22 18:25:28 -08006029 (
6030 // This double-checks that TyKind::Adt(..., substs) are present
6031 // even if the type parameter argument is not explicitly specified
6032 // (here it comes from the default: `...Struct<T = u8>`).
6033 "TypeGenericStruct",
6034 "Generic types are not supported yet (b/259749095)",
6035 ),
Googler7a77a802023-04-21 08:32:50 -07006036 ("LifetimeGenericStruct<'static>", "Generic types are not supported yet (b/259749095)"),
Lukasz Anforowiczcfe84552023-04-20 13:38:54 -07006037 (
6038 "std::cmp::Ordering",
6039 "Type `std::cmp::Ordering` comes from the `core` crate, \
6040 but no `--other-crate-bindings` were specified for this crate",
6041 ),
Googler7a77a802023-04-21 08:32:50 -07006042 ("Option<i8>", "Generic types are not supported yet (b/259749095)"),
Lukasz Anforowiczf36762a2023-03-02 18:43:07 -08006043 (
6044 "PublicReexportOfStruct",
6045 "Not directly public type (re-exports are not supported yet - b/262052635)",
6046 ),
6047 (
6048 // This testcase is like `PublicReexportOfStruct`, but the private type and the
6049 // re-export are in another crate. When authoring this test
6050 // `core::alloc::LayoutError` was a public re-export of
6051 // `core::alloc::layout::LayoutError`:
6052 // `https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=d2b5528af9b33b25abe44cc4646d65e3`
6053 // TODO(b/258261328): Once cross-crate bindings are supported we should try
6054 // to test them via a test crate that we control (rather than testing via
6055 // implementation details of the std crate).
6056 "core::alloc::LayoutError",
6057 "Not directly public type (re-exports are not supported yet - b/262052635)",
6058 ),
Lukasz Anforowicz2eb24292023-03-14 09:38:21 -07006059 (
6060 "*const Option<i8>",
Lukasz Anforowiczaa2de7e2023-06-15 11:32:05 -07006061 "Failed to format the pointee \
6062 of the pointer type `*const std::option::Option<i8>`: \
Lukasz Anforowicz2eb24292023-03-14 09:38:21 -07006063 Generic types are not supported yet (b/259749095)",
6064 ),
Lukasz Anforowicz4f9f5522022-10-17 15:03:37 -07006065 ];
Lukasz Anforowicz40472722022-11-08 13:29:08 -08006066 let preamble = quote! {
Lukasz Anforowicz75894832022-11-22 18:25:28 -08006067 #![feature(never_type)]
6068
Lukasz Anforowicz5c1b3ad2023-04-13 17:05:00 -07006069 pub struct SomeStruct {
6070 pub x: i32,
6071 pub y: i32,
6072 }
6073
Lukasz Anforowicz75894832022-11-22 18:25:28 -08006074 pub struct StructWithCustomDrop {
Lukasz Anforowicz40472722022-11-08 13:29:08 -08006075 pub x: i32,
6076 pub y: i32,
6077 }
Lukasz Anforowicz75894832022-11-22 18:25:28 -08006078
6079 impl Drop for StructWithCustomDrop {
6080 fn drop(&mut self) {}
Lukasz Anforowicz40472722022-11-08 13:29:08 -08006081 }
Lukasz Anforowicz75894832022-11-22 18:25:28 -08006082
6083 pub struct ConstGenericStruct<const N: usize> {
6084 pub arr: [u8; N],
6085 }
6086
6087 pub struct TypeGenericStruct<T = u8> {
6088 pub t: T,
6089 }
6090
6091 pub struct LifetimeGenericStruct<'a> {
6092 pub reference: &'a u8,
Lukasz Anforowicz40472722022-11-08 13:29:08 -08006093 }
Lukasz Anforowiczf36762a2023-03-02 18:43:07 -08006094
6095 mod private_submodule {
6096 pub struct PublicStructInPrivateModule;
6097 }
6098 pub use private_submodule::PublicStructInPrivateModule
6099 as PublicReexportOfStruct;
Lukasz Anforowicz40472722022-11-08 13:29:08 -08006100 };
Lukasz Anforowicz0adaa3e2023-06-12 10:25:18 -07006101 test_ty(TypeLocation::FnParam, &testcases, preamble, |desc, tcx, ty, expected_msg| {
Lukasz Anforowiczbe25c762023-01-17 12:43:52 -08006102 let input = bindings_input_for_tests(tcx);
Lukasz Anforowicz0adaa3e2023-06-12 10:25:18 -07006103 let anyhow_err = format_ty_for_cc(&input, ty, TypeLocation::FnParam)
6104 .expect_err(&format!("Expecting error for: {desc}"));
Lukasz Anforowiczbe25c762023-01-17 12:43:52 -08006105 let actual_msg = format!("{anyhow_err:#}");
6106 assert_eq!(&actual_msg, *expected_msg, "{desc}");
Lukasz Anforowicz4f9f5522022-10-17 15:03:37 -07006107 });
6108 }
6109
Lukasz Anforowicz7860d0e2022-11-15 08:47:56 -08006110 #[test]
6111 fn test_format_ty_for_rs_successes() {
6112 // Test coverage for cases where `format_ty_for_rs` returns an `Ok(...)`.
6113 let testcases = [
6114 // ( <Rust type>, <expected Rust spelling for ..._cc_api_impl.rs> )
6115 ("bool", "bool"),
6116 ("f32", "f32"),
6117 ("f64", "f64"),
6118 ("i8", "i8"),
6119 ("i16", "i16"),
6120 ("i32", "i32"),
6121 ("i64", "i64"),
6122 ("i128", "i128"),
6123 ("isize", "isize"),
6124 ("u8", "u8"),
6125 ("u16", "u16"),
6126 ("u32", "u32"),
6127 ("u64", "u64"),
6128 ("u128", "u128"),
6129 ("usize", "usize"),
6130 ("char", "char"),
6131 ("!", "!"),
6132 ("()", "()"),
Lukasz Anforowiczeb58a492023-01-07 08:25:48 -08006133 // ADTs:
Lukasz Anforowicz8b475a42022-11-29 09:33:02 -08006134 ("SomeStruct", "::rust_out::SomeStruct"),
6135 ("SomeEnum", "::rust_out::SomeEnum"),
6136 ("SomeUnion", "::rust_out::SomeUnion"),
Lukasz Anforowiczeb58a492023-01-07 08:25:48 -08006137 // Type from another crate:
Lukasz Anforowicz8b475a42022-11-29 09:33:02 -08006138 ("std::cmp::Ordering", "::core::cmp::Ordering"),
Lukasz Anforowiczeb58a492023-01-07 08:25:48 -08006139 // `const` and `mut` pointers:
6140 ("*const i32", "*const i32"),
6141 ("*mut i32", "*mut i32"),
Lukasz Anforowiczaa2de7e2023-06-15 11:32:05 -07006142 // References:
6143 ("&i32", "& '__anon1 i32"),
6144 ("&mut i32", "& '__anon1 mut i32"),
6145 ("&'_ i32", "& '__anon1 i32"),
6146 ("&'static i32", "& 'static i32"),
Lukasz Anforowiczeb58a492023-01-07 08:25:48 -08006147 // Pointer to an ADT:
6148 ("*mut SomeStruct", "* mut :: rust_out :: SomeStruct"),
Lukasz Anforowicz5c1b3ad2023-04-13 17:05:00 -07006149 ("extern \"C\" fn(i32) -> i32", "extern \"C\" fn(i32) -> i32"),
Lukasz Anforowicz7860d0e2022-11-15 08:47:56 -08006150 ];
Lukasz Anforowicz75894832022-11-22 18:25:28 -08006151 let preamble = quote! {
6152 #![feature(never_type)]
6153
6154 pub struct SomeStruct {
6155 pub x: i32,
6156 pub y: i32,
6157 }
6158 pub enum SomeEnum {
6159 Cartesian{x: f64, y: f64},
6160 Polar{angle: f64, dist: f64},
6161 }
6162 pub union SomeUnion {
6163 pub x: i32,
6164 pub y: i32,
6165 }
6166 };
Lukasz Anforowicz0adaa3e2023-06-12 10:25:18 -07006167 test_ty(TypeLocation::FnParam, &testcases, preamble, |desc, tcx, ty, expected_tokens| {
Lukasz Anforowicz5c9c00d2022-12-02 08:41:39 -08006168 let actual_tokens = format_ty_for_rs(tcx, ty).unwrap().to_string();
6169 let expected_tokens = expected_tokens.parse::<TokenStream>().unwrap().to_string();
6170 assert_eq!(actual_tokens, expected_tokens, "{desc}");
Lukasz Anforowicz7860d0e2022-11-15 08:47:56 -08006171 });
6172 }
6173
6174 #[test]
6175 fn test_format_ty_for_rs_failures() {
6176 // This test provides coverage for cases where `format_ty_for_rs` returns an
6177 // `Err(...)`.
6178 let testcases = [
6179 // ( <Rust type>, <expected error message> )
6180 (
6181 "(i32, i32)", // Non-empty TyKind::Tuple
6182 "Tuples are not supported yet: (i32, i32) (b/254099023)",
6183 ),
6184 (
Lukasz Anforowicz7860d0e2022-11-15 08:47:56 -08006185 "[i32; 42]", // TyKind::Array
6186 "The following Rust type is not supported yet: [i32; 42]",
6187 ),
6188 (
6189 "&'static [i32]", // TyKind::Slice (nested underneath TyKind::Ref)
Lukasz Anforowiczaa2de7e2023-06-15 11:32:05 -07006190 "Failed to format the referent of the reference type `&'static [i32]`: \
6191 The following Rust type is not supported yet: [i32]",
Lukasz Anforowicz7860d0e2022-11-15 08:47:56 -08006192 ),
6193 (
6194 "&'static str", // TyKind::Str (nested underneath TyKind::Ref)
Lukasz Anforowiczaa2de7e2023-06-15 11:32:05 -07006195 "Failed to format the referent of the reference type `&'static str`: \
6196 The following Rust type is not supported yet: str",
Lukasz Anforowicz7860d0e2022-11-15 08:47:56 -08006197 ),
6198 (
Lukasz Anforowicz0182c5c2022-12-29 10:08:50 -08006199 "impl Eq", // TyKind::Alias
Lukasz Anforowicz0adaa3e2023-06-12 10:25:18 -07006200 "The following Rust type is not supported yet: impl Eq",
Lukasz Anforowicz7860d0e2022-11-15 08:47:56 -08006201 ),
6202 (
Lukasz Anforowicz8b475a42022-11-29 09:33:02 -08006203 "Option<i8>", // TyKind::Adt - generic + different crate
6204 "Generic types are not supported yet (b/259749095)",
6205 ),
Lukasz Anforowicz7860d0e2022-11-15 08:47:56 -08006206 ];
Lukasz Anforowicz75894832022-11-22 18:25:28 -08006207 let preamble = quote! {};
Lukasz Anforowicz0adaa3e2023-06-12 10:25:18 -07006208 test_ty(TypeLocation::FnParam, &testcases, preamble, |desc, tcx, ty, expected_err| {
6209 let anyhow_err =
6210 format_ty_for_rs(tcx, ty).expect_err(&format!("Expecting error for: {desc}"));
Lukasz Anforowicz7860d0e2022-11-15 08:47:56 -08006211 let actual_err = format!("{anyhow_err:#}");
6212 assert_eq!(&actual_err, *expected_err, "{desc}");
6213 });
6214 }
6215
Lukasz Anforowicz40472722022-11-08 13:29:08 -08006216 fn test_ty<TestFn, Expectation>(
Lukasz Anforowicz0adaa3e2023-06-12 10:25:18 -07006217 type_location: TypeLocation,
Lukasz Anforowicz40472722022-11-08 13:29:08 -08006218 testcases: &[(&str, Expectation)],
6219 preamble: TokenStream,
6220 test_fn: TestFn,
6221 ) where
Lukasz Anforowiczd16b6bf2022-11-22 18:35:08 -08006222 TestFn: for<'tcx> Fn(
6223 /* testcase_description: */ &str,
6224 TyCtxt<'tcx>,
6225 Ty<'tcx>,
6226 &Expectation,
6227 ) + Sync,
Lukasz Anforowicz4f9f5522022-10-17 15:03:37 -07006228 Expectation: Sync,
6229 {
Lukasz Anforowiczd0f0a842022-11-03 12:40:13 -07006230 for (index, (input, expected)) in testcases.iter().enumerate() {
Lukasz Anforowicz4f9f5522022-10-17 15:03:37 -07006231 let desc = format!("test #{index}: test input: `{input}`");
6232 let input = {
6233 let ty_tokens: TokenStream = input.parse().unwrap();
Lukasz Anforowicz0adaa3e2023-06-12 10:25:18 -07006234 let input = match type_location {
6235 TypeLocation::FnReturn => quote! {
6236 #preamble
6237 pub fn test_function() -> #ty_tokens { unimplemented!() }
6238 },
6239 TypeLocation::FnParam => quote! {
6240 #preamble
6241 pub fn test_function(_arg: #ty_tokens) { unimplemented!() }
6242 },
6243 TypeLocation::Other => unimplemented!(),
Lukasz Anforowicz4f9f5522022-10-17 15:03:37 -07006244 };
6245 input.to_string()
6246 };
Lukasz Anforowicz0bef2642023-01-05 09:20:31 -08006247 run_compiler_for_testing(input, |tcx| {
Lukasz Anforowicz4f9f5522022-10-17 15:03:37 -07006248 let def_id = find_def_id_by_name(tcx, "test_function");
Lukasz Anforowiczaa2de7e2023-06-15 11:32:05 -07006249 let sig = get_fn_sig(tcx, def_id);
Lukasz Anforowicz0adaa3e2023-06-12 10:25:18 -07006250 let ty = match type_location {
6251 TypeLocation::FnReturn => sig.output(),
6252 TypeLocation::FnParam => sig.inputs()[0],
6253 TypeLocation::Other => unimplemented!(),
6254 };
Lukasz Anforowicz75894832022-11-22 18:25:28 -08006255 test_fn(&desc, tcx, ty, expected);
Lukasz Anforowicz4f9f5522022-10-17 15:03:37 -07006256 });
6257 }
6258 }
6259
Lukasz Anforowicze465b9e2023-02-06 15:00:25 -08006260 /// Tests invoking `format_item` on the item with the specified `name` from
Lukasz Anforowicz8a833412022-10-14 14:44:13 -07006261 /// the given Rust `source`. Returns the result of calling
Lukasz Anforowicze465b9e2023-02-06 15:00:25 -08006262 /// `test_function` with `format_item`'s result as an argument.
Lukasz Anforowicz8a833412022-10-14 14:44:13 -07006263 /// (`test_function` should typically `assert!` that it got the expected
Lukasz Anforowicze465b9e2023-02-06 15:00:25 -08006264 /// result from `format_item`.)
6265 fn test_format_item<F, T>(source: &str, name: &str, test_function: F) -> T
Lukasz Anforowicz8a833412022-10-14 14:44:13 -07006266 where
Lukasz Anforowicz73a2c0d2023-03-22 14:07:00 -07006267 F: FnOnce(Result<Option<ApiSnippets>, String>) -> T + Send,
Lukasz Anforowicz8a833412022-10-14 14:44:13 -07006268 T: Send,
6269 {
Lukasz Anforowicz0bef2642023-01-05 09:20:31 -08006270 run_compiler_for_testing(source, |tcx| {
Lukasz Anforowicz8a833412022-10-14 14:44:13 -07006271 let def_id = find_def_id_by_name(tcx, name);
Lukasz Anforowicze465b9e2023-02-06 15:00:25 -08006272 let result = format_item(&bindings_input_for_tests(tcx), def_id);
Lukasz Anforowicze4333062022-10-17 14:47:53 -07006273
6274 // https://docs.rs/anyhow/latest/anyhow/struct.Error.html#display-representations says:
6275 // To print causes as well [...], use the alternate selector “{:#}”.
6276 let result = result.map_err(|anyhow_err| format!("{anyhow_err:#}"));
6277
6278 test_function(result)
Lukasz Anforowicz8a833412022-10-14 14:44:13 -07006279 })
6280 }
6281
6282 /// Finds the definition id of a Rust item with the specified `name`.
6283 /// Panics if no such item is found, or if there is more than one match.
6284 fn find_def_id_by_name(tcx: TyCtxt, name: &str) -> LocalDefId {
6285 let hir_items = || tcx.hir().items().map(|item_id| tcx.hir().item(item_id));
6286 let items_with_matching_name =
6287 hir_items().filter(|item| item.ident.name.as_str() == name).collect_vec();
Lukasz Anforowiczd0f0a842022-11-03 12:40:13 -07006288 match *items_with_matching_name.as_slice() {
6289 [] => {
Lukasz Anforowicz8a833412022-10-14 14:44:13 -07006290 let found_names = hir_items()
6291 .map(|item| item.ident.name.as_str())
6292 .filter(|s| !s.is_empty())
6293 .sorted()
6294 .dedup()
6295 .map(|name| format!("`{name}`"))
Lukasz Anforowiczd0f0a842022-11-03 12:40:13 -07006296 .join(",\n");
6297 panic!("No items named `{name}`.\nInstead found:\n{found_names}");
Lukasz Anforowicz8a833412022-10-14 14:44:13 -07006298 }
Lukasz Anforowicz61cb1e32022-11-04 09:08:35 -07006299 [item] => item.owner_id.def_id,
Lukasz Anforowicz8a833412022-10-14 14:44:13 -07006300 _ => panic!("More than one item named `{name}`"),
6301 }
6302 }
6303
Lukasz Anforowiczbe25c762023-01-17 12:43:52 -08006304 fn bindings_input_for_tests(tcx: TyCtxt) -> Input {
Lukasz Anforowicza782bda2023-01-17 14:04:50 -08006305 Input {
6306 tcx,
6307 crubit_support_path: "crubit/support/for/tests".into(),
Lukasz Anforowiczcfe84552023-04-20 13:38:54 -07006308 crate_name_to_include_path: Default::default(),
Lukasz Anforowicza782bda2023-01-17 14:04:50 -08006309 _features: (),
Lukasz Anforowicza782bda2023-01-17 14:04:50 -08006310 }
Lukasz Anforowiczbe25c762023-01-17 12:43:52 -08006311 }
6312
6313 /// Tests invoking `generate_bindings` on the given Rust `source`.
Lukasz Anforowicz8a833412022-10-14 14:44:13 -07006314 /// Returns the result of calling `test_function` with the generated
6315 /// bindings as an argument. (`test_function` should typically `assert!`
6316 /// that it got the expected `GeneratedBindings`.)
6317 fn test_generated_bindings<F, T>(source: &str, test_function: F) -> T
Lukasz Anforowicz581fd752022-09-21 11:30:15 -07006318 where
Lukasz Anforowiczbe25c762023-01-17 12:43:52 -08006319 F: FnOnce(Result<Output>) -> T + Send,
Lukasz Anforowicz581fd752022-09-21 11:30:15 -07006320 T: Send,
6321 {
Lukasz Anforowiczbe25c762023-01-17 12:43:52 -08006322 run_compiler_for_testing(source, |tcx| {
6323 test_function(generate_bindings(&bindings_input_for_tests(tcx)))
6324 })
Lukasz Anforowiczbda1cfe2022-09-20 06:25:43 -07006325 }
Lukasz Anforowiczbda1cfe2022-09-20 06:25:43 -07006326}