Lukasz Anforowicz | bda1cfe | 2022-09-20 06:25:43 -0700 | [diff] [blame] | 1 | // 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 Anforowicz | 7589483 | 2022-11-22 18:25:28 -0800 | [diff] [blame] | 5 | use anyhow::{anyhow, bail, ensure, Context, Result}; |
Lukasz Anforowicz | 8b475a4 | 2022-11-29 09:33:02 -0800 | [diff] [blame] | 6 | use code_gen_utils::{ |
Lukasz Anforowicz | 9924c43 | 2023-04-04 12:51:22 -0700 | [diff] [blame] | 7 | escape_non_identifier_chars, format_cc_ident, format_cc_includes, |
| 8 | format_namespace_bound_cc_tokens, make_rs_ident, CcInclude, NamespaceQualifier, |
Lukasz Anforowicz | 8b475a4 | 2022-11-29 09:33:02 -0800 | [diff] [blame] | 9 | }; |
Lukasz Anforowicz | 903fc63 | 2022-10-25 08:55:33 -0700 | [diff] [blame] | 10 | use itertools::Itertools; |
Lukasz Anforowicz | a691cf5 | 2023-03-08 12:24:33 -0800 | [diff] [blame] | 11 | use proc_macro2::{Ident, Literal, TokenStream}; |
Lukasz Anforowicz | 2eb2429 | 2023-03-14 09:38:21 -0700 | [diff] [blame] | 12 | use quote::{format_ident, quote, ToTokens}; |
Lukasz Anforowicz | ae1ae17 | 2023-07-05 12:11:24 -0700 | [diff] [blame] | 13 | use rustc_hir::{AssocItemKind, Item, ItemKind, Node, Unsafety}; |
Lukasz Anforowicz | 36de572 | 2023-06-16 08:17:44 -0700 | [diff] [blame] | 14 | use rustc_infer::infer::TyCtxtInferExt; |
Lukasz Anforowicz | ed1c4f0 | 2022-09-29 11:11:20 -0700 | [diff] [blame] | 15 | use rustc_middle::dep_graph::DepContext; |
Lukasz Anforowicz | eb58a49 | 2023-01-07 08:25:48 -0800 | [diff] [blame] | 16 | use rustc_middle::mir::Mutability; |
Lukasz Anforowicz | 4f9f552 | 2022-10-17 15:03:37 -0700 | [diff] [blame] | 17 | use rustc_middle::ty::{self, Ty, TyCtxt}; // See <internal link>/ty.html#import-conventions |
Lukasz Anforowicz | 903fc63 | 2022-10-25 08:55:33 -0700 | [diff] [blame] | 18 | use rustc_span::def_id::{DefId, LocalDefId, LOCAL_CRATE}; |
Lukasz Anforowicz | 1b665a4 | 2023-06-20 13:04:54 -0700 | [diff] [blame] | 19 | use rustc_span::symbol::{kw, sym, Symbol}; |
Lukasz Anforowicz | 735bfb8 | 2023-03-15 16:18:44 -0700 | [diff] [blame] | 20 | use rustc_target::abi::{Abi, FieldsShape, Integer, Layout, Primitive, Scalar}; |
Lukasz Anforowicz | 24160d5 | 2022-10-19 06:45:45 -0700 | [diff] [blame] | 21 | use rustc_target::spec::PanicStrategy; |
Lukasz Anforowicz | 36de572 | 2023-06-16 08:17:44 -0700 | [diff] [blame] | 22 | use rustc_trait_selection::infer::InferCtxtExt; |
Lukasz Anforowicz | aa2de7e | 2023-06-15 11:32:05 -0700 | [diff] [blame] | 23 | use rustc_type_ir::sty::RegionKind; |
Lukasz Anforowicz | 816cbaa | 2022-12-07 09:31:30 -0800 | [diff] [blame] | 24 | use std::collections::{BTreeSet, HashMap, HashSet}; |
Googler | 47fd957 | 2023-01-20 09:57:32 -0800 | [diff] [blame] | 25 | use std::iter::once; |
Lukasz Anforowicz | 7f31f80 | 2022-12-16 08:24:13 -0800 | [diff] [blame] | 26 | use std::ops::AddAssign; |
Lukasz Anforowicz | 8e5042c | 2023-01-03 11:19:07 -0800 | [diff] [blame] | 27 | use std::rc::Rc; |
Lukasz Anforowicz | bda1cfe | 2022-09-20 06:25:43 -0700 | [diff] [blame] | 28 | |
Lukasz Anforowicz | be25c76 | 2023-01-17 12:43:52 -0800 | [diff] [blame] | 29 | pub struct Input<'tcx> { |
| 30 | /// Compilation context for the crate that the bindings should be generated |
| 31 | /// for. |
| 32 | pub tcx: TyCtxt<'tcx>, |
| 33 | |
Lukasz Anforowicz | a782bda | 2023-01-17 14:04:50 -0800 | [diff] [blame] | 34 | /// 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 Anforowicz | be25c76 | 2023-01-17 12:43:52 -0800 | [diff] [blame] | 38 | |
Lukasz Anforowicz | cfe8455 | 2023-04-20 13:38:54 -0700 | [diff] [blame] | 39 | /// 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 Anforowicz | be25c76 | 2023-01-17 12:43:52 -0800 | [diff] [blame] | 46 | // TODO(b/262878759): Provide a set of enabled/disabled Crubit features. |
| 47 | pub _features: (), |
Lukasz Anforowicz | be25c76 | 2023-01-17 12:43:52 -0800 | [diff] [blame] | 48 | } |
| 49 | |
Lukasz Anforowicz | a3b7db0 | 2023-03-09 17:34:05 -0800 | [diff] [blame] | 50 | impl<'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 Anforowicz | be25c76 | 2023-01-17 12:43:52 -0800 | [diff] [blame] | 61 | pub struct Output { |
Lukasz Anforowicz | 2b38d27 | 2022-09-23 08:08:18 -0700 | [diff] [blame] | 62 | pub h_body: TokenStream, |
Lukasz Anforowicz | e7a2500 | 2022-11-10 06:21:42 -0800 | [diff] [blame] | 63 | pub rs_body: TokenStream, |
Lukasz Anforowicz | 581fd75 | 2022-09-21 11:30:15 -0700 | [diff] [blame] | 64 | } |
| 65 | |
Lukasz Anforowicz | be25c76 | 2023-01-17 12:43:52 -0800 | [diff] [blame] | 66 | pub 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 Anforowicz | 24160d5 | 2022-10-19 06:45:45 -0700 | [diff] [blame] | 71 | |
Lukasz Anforowicz | be25c76 | 2023-01-17 12:43:52 -0800 | [diff] [blame] | 72 | 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 Anforowicz | d9ff4ab | 2022-09-23 08:11:18 -0700 | [diff] [blame] | 80 | |
Lukasz Anforowicz | be25c76 | 2023-01-17 12:43:52 -0800 | [diff] [blame] | 81 | 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 Anforowicz | 2f849cf | 2022-11-17 15:52:39 -0800 | [diff] [blame] | 86 | |
Lukasz Anforowicz | be25c76 | 2023-01-17 12:43:52 -0800 | [diff] [blame] | 87 | let h_body = quote! { |
| 88 | #top_comment |
Lukasz Anforowicz | 2f849cf | 2022-11-17 15:52:39 -0800 | [diff] [blame] | 89 | |
Lukasz Anforowicz | be25c76 | 2023-01-17 12:43:52 -0800 | [diff] [blame] | 90 | // TODO(b/251445877): Replace `#pragma once` with include guards. |
| 91 | __HASH_TOKEN__ pragma once __NEWLINE__ |
| 92 | __NEWLINE__ |
Lukasz Anforowicz | 2f849cf | 2022-11-17 15:52:39 -0800 | [diff] [blame] | 93 | |
Lukasz Anforowicz | be25c76 | 2023-01-17 12:43:52 -0800 | [diff] [blame] | 94 | #h_body |
| 95 | }; |
Lukasz Anforowicz | 581fd75 | 2022-09-21 11:30:15 -0700 | [diff] [blame] | 96 | |
Lukasz Anforowicz | be25c76 | 2023-01-17 12:43:52 -0800 | [diff] [blame] | 97 | let rs_body = quote! { |
| 98 | #top_comment |
Lukasz Anforowicz | d16b6bf | 2022-11-22 18:35:08 -0800 | [diff] [blame] | 99 | |
Lukasz Anforowicz | 843d161 | 2023-03-02 16:52:53 -0800 | [diff] [blame] | 100 | // `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 Anforowicz | be25c76 | 2023-01-17 12:43:52 -0800 | [diff] [blame] | 103 | #![allow(improper_ctypes_definitions)] __NEWLINE__ |
| 104 | __NEWLINE__ |
Lukasz Anforowicz | d16b6bf | 2022-11-22 18:35:08 -0800 | [diff] [blame] | 105 | |
Lukasz Anforowicz | be25c76 | 2023-01-17 12:43:52 -0800 | [diff] [blame] | 106 | #rs_body |
| 107 | }; |
Lukasz Anforowicz | e7a2500 | 2022-11-10 06:21:42 -0800 | [diff] [blame] | 108 | |
Lukasz Anforowicz | be25c76 | 2023-01-17 12:43:52 -0800 | [diff] [blame] | 109 | Ok(Output { h_body, rs_body }) |
Lukasz Anforowicz | 581fd75 | 2022-09-21 11:30:15 -0700 | [diff] [blame] | 110 | } |
| 111 | |
Lukasz Anforowicz | 0b8a02f | 2023-02-10 15:55:40 -0800 | [diff] [blame] | 112 | #[derive(Clone, Debug, Default)] |
Lukasz Anforowicz | 3744e50 | 2022-12-02 08:40:38 -0800 | [diff] [blame] | 113 | struct 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 Anforowicz | ed17d05 | 2022-11-02 12:07:28 -0700 | [diff] [blame] | 117 | includes: BTreeSet<CcInclude>, |
Lukasz Anforowicz | 5c9c00d | 2022-12-02 08:41:39 -0800 | [diff] [blame] | 118 | |
| 119 | /// Set of local definitions that a `CcSnippet` depends on. For example if |
Lukasz Anforowicz | 73a2c0d | 2023-03-22 14:07:00 -0700 | [diff] [blame] | 120 | /// `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 Anforowicz | 5c9c00d | 2022-12-02 08:41:39 -0800 | [diff] [blame] | 126 | 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 Anforowicz | f4501b4 | 2023-01-07 09:41:29 -0800 | [diff] [blame] | 135 | fwd_decls: HashSet<LocalDefId>, |
Lukasz Anforowicz | 3744e50 | 2022-12-02 08:40:38 -0800 | [diff] [blame] | 136 | } |
| 137 | |
| 138 | impl CcPrerequisites { |
| 139 | #[cfg(test)] |
| 140 | fn is_empty(&self) -> bool { |
Lukasz Anforowicz | f4501b4 | 2023-01-07 09:41:29 -0800 | [diff] [blame] | 141 | let &Self { ref includes, ref defs, ref fwd_decls } = self; |
| 142 | includes.is_empty() && defs.is_empty() && fwd_decls.is_empty() |
Lukasz Anforowicz | 3744e50 | 2022-12-02 08:40:38 -0800 | [diff] [blame] | 143 | } |
Lukasz Anforowicz | 7a7b81f | 2023-01-12 15:50:46 -0800 | [diff] [blame] | 144 | |
| 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 Anforowicz | 3744e50 | 2022-12-02 08:40:38 -0800 | [diff] [blame] | 154 | } |
| 155 | |
| 156 | impl AddAssign for CcPrerequisites { |
Lukasz Anforowicz | f4501b4 | 2023-01-07 09:41:29 -0800 | [diff] [blame] | 157 | fn add_assign(&mut self, rhs: Self) { |
| 158 | let Self { mut includes, defs, fwd_decls } = rhs; |
| 159 | |
Lukasz Anforowicz | 5c9c00d | 2022-12-02 08:41:39 -0800 | [diff] [blame] | 160 | // `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 Anforowicz | df363ee | 2022-12-16 14:56:38 -0800 | [diff] [blame] | 165 | // not documented at |
| 166 | // https://doc.rust-lang.org/std/collections/struct.BTreeSet.html#method.append |
Lukasz Anforowicz | f4501b4 | 2023-01-07 09:41:29 -0800 | [diff] [blame] | 167 | self.includes.append(&mut includes); |
Lukasz Anforowicz | 5c9c00d | 2022-12-02 08:41:39 -0800 | [diff] [blame] | 168 | |
Lukasz Anforowicz | f4501b4 | 2023-01-07 09:41:29 -0800 | [diff] [blame] | 169 | self.defs.extend(defs); |
| 170 | self.fwd_decls.extend(fwd_decls); |
Lukasz Anforowicz | 3744e50 | 2022-12-02 08:40:38 -0800 | [diff] [blame] | 171 | } |
| 172 | } |
| 173 | |
| 174 | #[derive(Debug, Default)] |
| 175 | struct CcSnippet { |
| 176 | tokens: TokenStream, |
| 177 | prereqs: CcPrerequisites, |
Lukasz Anforowicz | ed17d05 | 2022-11-02 12:07:28 -0700 | [diff] [blame] | 178 | } |
| 179 | |
| 180 | impl CcSnippet { |
Lukasz Anforowicz | 3744e50 | 2022-12-02 08:40:38 -0800 | [diff] [blame] | 181 | /// Consumes `self` and returns its `tokens`, while preserving |
Lukasz Anforowicz | df363ee | 2022-12-16 14:56:38 -0800 | [diff] [blame] | 182 | /// its `prereqs` into `prereqs_accumulator`. |
Lukasz Anforowicz | 3744e50 | 2022-12-02 08:40:38 -0800 | [diff] [blame] | 183 | fn into_tokens(self, prereqs_accumulator: &mut CcPrerequisites) -> TokenStream { |
| 184 | let Self { tokens, prereqs } = self; |
| 185 | *prereqs_accumulator += prereqs; |
| 186 | tokens |
Lukasz Anforowicz | ed17d05 | 2022-11-02 12:07:28 -0700 | [diff] [blame] | 187 | } |
Lukasz Anforowicz | a2f1cae | 2022-12-15 10:42:25 -0800 | [diff] [blame] | 188 | |
| 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 Anforowicz | ed17d05 | 2022-11-02 12:07:28 -0700 | [diff] [blame] | 200 | } |
| 201 | |
Lukasz Anforowicz | 73a2c0d | 2023-03-22 14:07:00 -0700 | [diff] [blame] | 202 | impl 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 Anforowicz | df363ee | 2022-12-16 14:56:38 -0800 | [diff] [blame] | 208 | /// Represents the fully qualified name of a Rust item (e.g. of a `struct` or a |
Lukasz Anforowicz | 8b475a4 | 2022-11-29 09:33:02 -0800 | [diff] [blame] | 209 | /// function). |
| 210 | struct 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 Anforowicz | df363ee | 2022-12-16 14:56:38 -0800 | [diff] [blame] | 217 | /// The path may contain multiple modules - e.g. `foo::bar::baz`. |
Lukasz Anforowicz | 8b475a4 | 2022-11-29 09:33:02 -0800 | [diff] [blame] | 218 | mod_path: NamespaceQualifier, |
| 219 | |
| 220 | /// Name of the item. |
Lukasz Anforowicz | ce17f3f | 2023-02-27 11:32:14 -0800 | [diff] [blame] | 221 | /// 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 Anforowicz | 8b475a4 | 2022-11-29 09:33:02 -0800 | [diff] [blame] | 225 | } |
| 226 | |
| 227 | impl FullyQualifiedName { |
Lukasz Anforowicz | 43f06c6 | 2023-02-10 17:14:19 -0800 | [diff] [blame] | 228 | /// Computes a `FullyQualifiedName` for `def_id`. |
| 229 | /// |
Lukasz Anforowicz | ce17f3f | 2023-02-27 11:32:14 -0800 | [diff] [blame] | 230 | /// May panic if `def_id` is an invalid id. |
Lukasz Anforowicz | 1ab5e87 | 2022-12-05 10:07:00 -0800 | [diff] [blame] | 231 | // TODO(b/259724276): This function's results should be memoized. |
Lukasz Anforowicz | 8b475a4 | 2022-11-29 09:33:02 -0800 | [diff] [blame] | 232 | fn new(tcx: TyCtxt, def_id: DefId) -> Self { |
Lukasz Anforowicz | 8b475a4 | 2022-11-29 09:33:02 -0800 | [diff] [blame] | 233 | 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 Anforowicz | ce17f3f | 2023-02-27 11:32:14 -0800 | [diff] [blame] | 237 | let name = name.data.get_opt_name(); |
Lukasz Anforowicz | 8b475a4 | 2022-11-29 09:33:02 -0800 | [diff] [blame] | 238 | |
Lukasz Anforowicz | 8e5042c | 2023-01-03 11:19:07 -0800 | [diff] [blame] | 239 | let mod_path = NamespaceQualifier::new( |
Lukasz Anforowicz | ce17f3f | 2023-02-27 11:32:14 -0800 | [diff] [blame] | 240 | full_path |
| 241 | .into_iter() |
| 242 | .filter_map(|p| p.data.get_opt_name()) |
| 243 | .map(|s| Rc::<str>::from(s.as_str())), |
Lukasz Anforowicz | 8e5042c | 2023-01-03 11:19:07 -0800 | [diff] [blame] | 244 | ); |
Lukasz Anforowicz | 8b475a4 | 2022-11-29 09:33:02 -0800 | [diff] [blame] | 245 | |
| 246 | Self { krate, mod_path, name } |
| 247 | } |
| 248 | |
| 249 | fn format_for_cc(&self) -> Result<TokenStream> { |
Lukasz Anforowicz | ce17f3f | 2023-02-27 11:32:14 -0800 | [diff] [blame] | 250 | let name = |
| 251 | self.name.as_ref().expect("`format_for_cc` can't be called on name-less item kinds"); |
| 252 | |
Lukasz Anforowicz | 8b475a4 | 2022-11-29 09:33:02 -0800 | [diff] [blame] | 253 | let top_level_ns = format_cc_ident(self.krate.as_str())?; |
| 254 | let ns_path = self.mod_path.format_for_cc()?; |
Lukasz Anforowicz | ce17f3f | 2023-02-27 11:32:14 -0800 | [diff] [blame] | 255 | let name = format_cc_ident(name.as_str())?; |
Lukasz Anforowicz | 8b475a4 | 2022-11-29 09:33:02 -0800 | [diff] [blame] | 256 | Ok(quote! { :: #top_level_ns :: #ns_path #name }) |
| 257 | } |
| 258 | |
| 259 | fn format_for_rs(&self) -> TokenStream { |
Lukasz Anforowicz | ce17f3f | 2023-02-27 11:32:14 -0800 | [diff] [blame] | 260 | let name = |
| 261 | self.name.as_ref().expect("`format_for_cc` can't be called on name-less item kinds"); |
| 262 | |
Lukasz Anforowicz | df363ee | 2022-12-16 14:56:38 -0800 | [diff] [blame] | 263 | let krate = make_rs_ident(self.krate.as_str()); |
| 264 | let mod_path = self.mod_path.format_for_rs(); |
Lukasz Anforowicz | ce17f3f | 2023-02-27 11:32:14 -0800 | [diff] [blame] | 265 | let name = make_rs_ident(name.as_str()); |
Lukasz Anforowicz | df363ee | 2022-12-16 14:56:38 -0800 | [diff] [blame] | 266 | quote! { :: #krate :: #mod_path #name } |
Lukasz Anforowicz | 8b475a4 | 2022-11-29 09:33:02 -0800 | [diff] [blame] | 267 | } |
| 268 | } |
| 269 | |
Lukasz Anforowicz | a3b7db0 | 2023-03-09 17:34:05 -0800 | [diff] [blame] | 270 | /// 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). |
| 272 | fn 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 Anforowicz | aa2de7e | 2023-06-15 11:32:05 -0700 | [diff] [blame] | 281 | ty::TyKind::Ref{..} | |
Lukasz Anforowicz | a3b7db0 | 2023-03-09 17:34:05 -0800 | [diff] [blame] | 282 | 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 Anforowicz | a3b7db0 | 2023-03-09 17:34:05 -0800 | [diff] [blame] | 316 | 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 Anforowicz | 8574c9d | 2023-04-13 15:11:20 -0700 | [diff] [blame] | 328 | /// Location where a type is used. |
| 329 | enum 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 Anforowicz | 5c1b3ad | 2023-04-13 17:05:00 -0700 | [diff] [blame] | 338 | /// 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 Anforowicz | 8574c9d | 2023-04-13 15:11:20 -0700 | [diff] [blame] | 350 | Other, |
| 351 | } |
| 352 | |
Lukasz Anforowicz | aa2de7e | 2023-06-15 11:32:05 -0700 | [diff] [blame] | 353 | fn 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 Anforowicz | ed17d05 | 2022-11-02 12:07:28 -0700 | [diff] [blame] | 368 | /// Formats `ty` into a `CcSnippet` that represents how the type should be |
Lukasz Anforowicz | df363ee | 2022-12-16 14:56:38 -0800 | [diff] [blame] | 369 | /// spelled in a C++ declaration of a function parameter or field. |
Lukasz Anforowicz | 1ab5e87 | 2022-12-05 10:07:00 -0800 | [diff] [blame] | 370 | // |
| 371 | // TODO(b/259724276): This function's results should be memoized. |
Lukasz Anforowicz | 8574c9d | 2023-04-13 15:11:20 -0700 | [diff] [blame] | 372 | fn format_ty_for_cc<'tcx>( |
| 373 | input: &Input<'tcx>, |
| 374 | ty: Ty<'tcx>, |
| 375 | location: TypeLocation, |
| 376 | ) -> Result<CcSnippet> { |
Lukasz Anforowicz | 3744e50 | 2022-12-02 08:40:38 -0800 | [diff] [blame] | 377 | fn cstdint(tokens: TokenStream) -> CcSnippet { |
Lukasz Anforowicz | a2f1cae | 2022-12-15 10:42:25 -0800 | [diff] [blame] | 378 | CcSnippet::with_include(tokens, CcInclude::cstdint()) |
Lukasz Anforowicz | ed17d05 | 2022-11-02 12:07:28 -0700 | [diff] [blame] | 379 | } |
Lukasz Anforowicz | 3744e50 | 2022-12-02 08:40:38 -0800 | [diff] [blame] | 380 | fn keyword(tokens: TokenStream) -> CcSnippet { |
Lukasz Anforowicz | a2f1cae | 2022-12-15 10:42:25 -0800 | [diff] [blame] | 381 | CcSnippet::new(tokens) |
Lukasz Anforowicz | ed17d05 | 2022-11-02 12:07:28 -0700 | [diff] [blame] | 382 | } |
Lukasz Anforowicz | 4f9f552 | 2022-10-17 15:03:37 -0700 | [diff] [blame] | 383 | Ok(match ty.kind() { |
Lukasz Anforowicz | 8574c9d | 2023-04-13 15:11:20 -0700 | [diff] [blame] | 384 | ty::TyKind::Never => match location { |
| 385 | TypeLocation::FnReturn => keyword(quote! { void }), |
Googler | 7a77a80 | 2023-04-21 08:32:50 -0700 | [diff] [blame] | 386 | _ => { |
Lukasz Anforowicz | 8574c9d | 2023-04-13 15:11:20 -0700 | [diff] [blame] | 387 | // TODO(b/254507801): Maybe translate into `crubit::Never`? |
| 388 | bail!("The never type `!` is only supported as a return type (b/254507801)"); |
Googler | 7a77a80 | 2023-04-21 08:32:50 -0700 | [diff] [blame] | 389 | } |
| 390 | }, |
Lukasz Anforowicz | 4f9f552 | 2022-10-17 15:03:37 -0700 | [diff] [blame] | 391 | ty::TyKind::Tuple(types) => { |
| 392 | if types.len() == 0 { |
Lukasz Anforowicz | 8574c9d | 2023-04-13 15:11:20 -0700 | [diff] [blame] | 393 | match location { |
| 394 | TypeLocation::FnReturn => keyword(quote! { void }), |
Googler | 7a77a80 | 2023-04-21 08:32:50 -0700 | [diff] [blame] | 395 | _ => { |
Lukasz Anforowicz | 8574c9d | 2023-04-13 15:11:20 -0700 | [diff] [blame] | 396 | // TODO(b/254507801): Maybe translate into `crubit::Unit`? |
| 397 | bail!("`()` / `void` is only supported as a return type (b/254507801)"); |
Googler | 7a77a80 | 2023-04-21 08:32:50 -0700 | [diff] [blame] | 398 | } |
Lukasz Anforowicz | 8574c9d | 2023-04-13 15:11:20 -0700 | [diff] [blame] | 399 | } |
Lukasz Anforowicz | 4f9f552 | 2022-10-17 15:03:37 -0700 | [diff] [blame] | 400 | } else { |
Lukasz Anforowicz | 13794df | 2022-10-21 07:56:34 -0700 | [diff] [blame] | 401 | // TODO(b/254099023): Add support for tuples. |
| 402 | bail!("Tuples are not supported yet: {} (b/254099023)", ty); |
Lukasz Anforowicz | 4f9f552 | 2022-10-17 15:03:37 -0700 | [diff] [blame] | 403 | } |
| 404 | } |
Lukasz Anforowicz | 4f9f552 | 2022-10-17 15:03:37 -0700 | [diff] [blame] | 405 | |
Lukasz Anforowicz | e3c3947 | 2023-01-12 15:07:38 -0800 | [diff] [blame] | 406 | // 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 Anforowicz | ed17d05 | 2022-11-02 12:07:28 -0700 | [diff] [blame] | 410 | 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 Anforowicz | a782bda | 2023-01-17 14:04:50 -0800 | [diff] [blame] | 421 | // ABI compatibility and other details are described in the doc comments in |
Lukasz Anforowicz | ec0b64e | 2023-02-17 14:31:12 -0800 | [diff] [blame] | 422 | // `crubit/support/rs_std/rs_char.h` and `crubit/support/rs_std/char_test.cc` (search for |
Lukasz Anforowicz | d71686c | 2023-02-17 14:29:55 -0800 | [diff] [blame] | 423 | // "Layout tests"). |
Lukasz Anforowicz | ed17d05 | 2022-11-02 12:07:28 -0700 | [diff] [blame] | 424 | ty::TyKind::Char => { |
Lukasz Anforowicz | b06e081 | 2023-03-02 15:54:32 -0800 | [diff] [blame] | 425 | // Asserting that the target architecture meets the assumption from Crubit's |
Devin Jeanpierre | 81aec50 | 2023-04-04 15:33:39 -0700 | [diff] [blame] | 426 | // `rust_builtin_type_abi_assumptions.md` - we assume that Rust's `char` has the |
| 427 | // same ABI as `u32`. |
Lukasz Anforowicz | b06e081 | 2023-03-02 15:54:32 -0800 | [diff] [blame] | 428 | let layout = input |
| 429 | .tcx |
Lukasz Anforowicz | 2aab019 | 2023-06-15 12:05:39 -0700 | [diff] [blame] | 430 | .layout_of(ty::ParamEnv::empty().and(ty)) |
Lukasz Anforowicz | b06e081 | 2023-03-02 15:54:32 -0800 | [diff] [blame] | 431 | .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 Jeanpierre | 81aec50 | 2023-04-04 15:33:39 -0700 | [diff] [blame] | 435 | assert!(matches!( |
| 436 | layout.abi(), |
| 437 | Abi::Scalar(Scalar::Initialized { |
| 438 | value: Primitive::Int(Integer::I32, /* signedness = */ false), |
| 439 | .. |
| 440 | }) |
| 441 | )); |
Lukasz Anforowicz | b06e081 | 2023-03-02 15:54:32 -0800 | [diff] [blame] | 442 | |
Lukasz Anforowicz | a782bda | 2023-01-17 14:04:50 -0800 | [diff] [blame] | 443 | CcSnippet::with_include( |
Lukasz Anforowicz | ec0b64e | 2023-02-17 14:31:12 -0800 | [diff] [blame] | 444 | quote! { rs_std::rs_char }, |
Lukasz Anforowicz | a3b7db0 | 2023-03-09 17:34:05 -0800 | [diff] [blame] | 445 | input.support_header("rs_std/rs_char.h"), |
Lukasz Anforowicz | a782bda | 2023-01-17 14:04:50 -0800 | [diff] [blame] | 446 | ) |
Devin Jeanpierre | 81aec50 | 2023-04-04 15:33:39 -0700 | [diff] [blame] | 447 | } |
Lukasz Anforowicz | ed17d05 | 2022-11-02 12:07:28 -0700 | [diff] [blame] | 448 | |
| 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 Jeanpierre | 81aec50 | 2023-04-04 15:33:39 -0700 | [diff] [blame] | 459 | 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 Anforowicz | ed17d05 | 2022-11-02 12:07:28 -0700 | [diff] [blame] | 467 | |
| 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 Jeanpierre | 81aec50 | 2023-04-04 15:33:39 -0700 | [diff] [blame] | 471 | ty::TyKind::Int(ty::IntTy::Isize) => cstdint(quote! { std::intptr_t }), |
| 472 | ty::TyKind::Uint(ty::UintTy::Usize) => cstdint(quote! { std::uintptr_t }), |
Lukasz Anforowicz | 4f9f552 | 2022-10-17 15:03:37 -0700 | [diff] [blame] | 473 | |
| 474 | ty::TyKind::Int(ty::IntTy::I128) | ty::TyKind::Uint(ty::UintTy::U128) => { |
Lukasz Anforowicz | ed17d05 | 2022-11-02 12:07:28 -0700 | [diff] [blame] | 475 | // 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 Anforowicz | 4f9f552 | 2022-10-17 15:03:37 -0700 | [diff] [blame] | 479 | // 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 Anforowicz | 7589483 | 2022-11-22 18:25:28 -0800 | [diff] [blame] | 484 | ty::TyKind::Adt(adt, substs) => { |
Lukasz Anforowicz | df363ee | 2022-12-16 14:56:38 -0800 | [diff] [blame] | 485 | ensure!(substs.len() == 0, "Generic types are not supported yet (b/259749095)"); |
Lukasz Anforowicz | f36762a | 2023-03-02 18:43:07 -0800 | [diff] [blame] | 486 | ensure!( |
| 487 | is_directly_public(input.tcx, adt.did()), |
Devin Jeanpierre | 81aec50 | 2023-04-04 15:33:39 -0700 | [diff] [blame] | 488 | "Not directly public type (re-exports are not supported yet - b/262052635)" |
| 489 | ); |
Lukasz Anforowicz | 7589483 | 2022-11-22 18:25:28 -0800 | [diff] [blame] | 490 | |
Lukasz Anforowicz | 7589483 | 2022-11-22 18:25:28 -0800 | [diff] [blame] | 491 | let def_id = adt.did(); |
Lukasz Anforowicz | 5c9c00d | 2022-12-02 08:41:39 -0800 | [diff] [blame] | 492 | let mut prereqs = CcPrerequisites::default(); |
| 493 | if def_id.krate == LOCAL_CRATE { |
| 494 | prereqs.defs.insert(def_id.expect_local()); |
Lukasz Anforowicz | 7589483 | 2022-11-22 18:25:28 -0800 | [diff] [blame] | 495 | } else { |
Lukasz Anforowicz | cfe8455 | 2023-04-20 13:38:54 -0700 | [diff] [blame] | 496 | 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()) |
Googler | 7a77a80 | 2023-04-21 08:32:50 -0700 | [diff] [blame] | 500 | .ok_or_else(|| { |
| 501 | anyhow!( |
Lukasz Anforowicz | cfe8455 | 2023-04-20 13:38:54 -0700 | [diff] [blame] | 502 | "Type `{ty}` comes from the `{other_crate_name}` crate, \ |
Googler | 7a77a80 | 2023-04-21 08:32:50 -0700 | [diff] [blame] | 503 | but no `--other-crate-bindings` were specified for this crate" |
| 504 | ) |
| 505 | })?; |
Lukasz Anforowicz | cfe8455 | 2023-04-20 13:38:54 -0700 | [diff] [blame] | 506 | prereqs.includes.insert(include.clone()); |
| 507 | } |
Lukasz Anforowicz | 7589483 | 2022-11-22 18:25:28 -0800 | [diff] [blame] | 508 | |
Lukasz Anforowicz | cc7a76b | 2023-02-28 14:19:42 -0800 | [diff] [blame] | 509 | // Verify if definition of `ty` can be succesfully imported and bail otherwise. |
Devin Jeanpierre | 81aec50 | 2023-04-04 15:33:39 -0700 | [diff] [blame] | 510 | format_adt_core(input.tcx, def_id).with_context(|| { |
| 511 | format!("Failed to generate bindings for the definition of `{ty}`") |
| 512 | })?; |
Lukasz Anforowicz | cc7a76b | 2023-02-28 14:19:42 -0800 | [diff] [blame] | 513 | |
Lukasz Anforowicz | 7589483 | 2022-11-22 18:25:28 -0800 | [diff] [blame] | 514 | CcSnippet { |
Lukasz Anforowicz | be25c76 | 2023-01-17 12:43:52 -0800 | [diff] [blame] | 515 | tokens: FullyQualifiedName::new(input.tcx, def_id).format_for_cc()?, |
Lukasz Anforowicz | eb58a49 | 2023-01-07 08:25:48 -0800 | [diff] [blame] | 516 | prereqs, |
Lukasz Anforowicz | eb58a49 | 2023-01-07 08:25:48 -0800 | [diff] [blame] | 517 | } |
Devin Jeanpierre | 81aec50 | 2023-04-04 15:33:39 -0700 | [diff] [blame] | 518 | } |
| 519 | |
Lukasz Anforowicz | aa2de7e | 2023-06-15 11:32:05 -0700 | [diff] [blame] | 520 | 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 Jeanpierre | 81aec50 | 2023-04-04 15:33:39 -0700 | [diff] [blame] | 523 | format!("Failed to format the pointee of the pointer type `{ty}`") |
Lukasz Anforowicz | aa2de7e | 2023-06-15 11:32:05 -0700 | [diff] [blame] | 524 | })? |
| 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 Jeanpierre | 81aec50 | 2023-04-04 15:33:39 -0700 | [diff] [blame] | 545 | } |
Lukasz Anforowicz | eb58a49 | 2023-01-07 08:25:48 -0800 | [diff] [blame] | 546 | |
Lukasz Anforowicz | 5c1b3ad | 2023-04-13 17:05:00 -0700 | [diff] [blame] | 547 | 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 | |
Googler | 7a77a80 | 2023-04-21 08:32:50 -0700 | [diff] [blame] | 560 | // 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 Anforowicz | 5c1b3ad | 2023-04-13 17:05:00 -0700 | [diff] [blame] | 565 | let ptr_or_ref_sigil = match location { |
Googler | 7a77a80 | 2023-04-21 08:32:50 -0700 | [diff] [blame] | 566 | TypeLocation::FnReturn | TypeLocation::FnParam => quote! { & }, |
| 567 | TypeLocation::Other => quote! { * }, |
Lukasz Anforowicz | 5c1b3ad | 2023-04-13 17:05:00 -0700 | [diff] [blame] | 568 | }; |
| 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 } |
Googler | 7a77a80 | 2023-04-21 08:32:50 -0700 | [diff] [blame] | 583 | } |
Lukasz Anforowicz | 5c1b3ad | 2023-04-13 17:05:00 -0700 | [diff] [blame] | 584 | |
Lukasz Anforowicz | df363ee | 2022-12-16 14:56:38 -0800 | [diff] [blame] | 585 | // TODO(b/260268230, b/260729464): When recursively processing nested types (e.g. an |
Lukasz Anforowicz | 7a7b81f | 2023-01-12 15:50:46 -0800 | [diff] [blame] | 586 | // 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 Anforowicz | 087dff7 | 2023-02-17 12:13:32 -0800 | [diff] [blame] | 590 | _ => bail!("The following Rust type is not supported yet: {ty}"), |
Lukasz Anforowicz | 4f9f552 | 2022-10-17 15:03:37 -0700 | [diff] [blame] | 591 | }) |
Lukasz Anforowicz | ed1c4f0 | 2022-09-29 11:11:20 -0700 | [diff] [blame] | 592 | } |
| 593 | |
Lukasz Anforowicz | 8574c9d | 2023-04-13 15:11:20 -0700 | [diff] [blame] | 594 | fn 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 Anforowicz | c6ff453 | 2023-04-13 15:16:30 -0700 | [diff] [blame] | 599 | fn 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 Anforowicz | e721208 | 2023-07-05 13:50:10 -0700 | [diff] [blame] | 607 | format_ty_for_cc(input, ty, TypeLocation::FnParam) |
| 608 | .with_context(|| format!("Error handling parameter #{i}")) |
Lukasz Anforowicz | c6ff453 | 2023-04-13 15:16:30 -0700 | [diff] [blame] | 609 | }) |
| 610 | .collect() |
| 611 | } |
| 612 | |
Lukasz Anforowicz | 7589483 | 2022-11-22 18:25:28 -0800 | [diff] [blame] | 613 | /// 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 Anforowicz | 1ab5e87 | 2022-12-05 10:07:00 -0800 | [diff] [blame] | 618 | // |
| 619 | // TODO(b/259724276): This function's results should be memoized. |
Lukasz Anforowicz | 7589483 | 2022-11-22 18:25:28 -0800 | [diff] [blame] | 620 | fn format_ty_for_rs(tcx: TyCtxt, ty: Ty) -> Result<TokenStream> { |
Lukasz Anforowicz | 7860d0e | 2022-11-15 08:47:56 -0800 | [diff] [blame] | 621 | 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 Anforowicz | 5c1b3ad | 2023-04-13 17:05:00 -0700 | [diff] [blame] | 627 | | ty::TyKind::FnPtr(_) |
Lukasz Anforowicz | 7860d0e | 2022-11-15 08:47:56 -0800 | [diff] [blame] | 628 | | 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 Anforowicz | 8b475a4 | 2022-11-29 09:33:02 -0800 | [diff] [blame] | 640 | ty::TyKind::Adt(adt, substs) => { |
Lukasz Anforowicz | df363ee | 2022-12-16 14:56:38 -0800 | [diff] [blame] | 641 | ensure!(substs.len() == 0, "Generic types are not supported yet (b/259749095)"); |
Lukasz Anforowicz | 8b475a4 | 2022-11-29 09:33:02 -0800 | [diff] [blame] | 642 | FullyQualifiedName::new(tcx, adt.did()).format_for_rs() |
Devin Jeanpierre | 81aec50 | 2023-04-04 15:33:39 -0700 | [diff] [blame] | 643 | } |
Lukasz Anforowicz | aa2de7e | 2023-06-15 11:32:05 -0700 | [diff] [blame] | 644 | ty::TyKind::RawPtr(ty::TypeAndMut { ty: pointee_ty, mutbl }) => { |
Lukasz Anforowicz | eb58a49 | 2023-01-07 08:25:48 -0800 | [diff] [blame] | 645 | let qualifier = match mutbl { |
Devin Jeanpierre | 81aec50 | 2023-04-04 15:33:39 -0700 | [diff] [blame] | 646 | Mutability::Mut => quote! { mut }, |
| 647 | Mutability::Not => quote! { const }, |
Lukasz Anforowicz | eb58a49 | 2023-01-07 08:25:48 -0800 | [diff] [blame] | 648 | }; |
Lukasz Anforowicz | aa2de7e | 2023-06-15 11:32:05 -0700 | [diff] [blame] | 649 | let ty = format_ty_for_rs(tcx, *pointee_ty).with_context(|| { |
Devin Jeanpierre | 81aec50 | 2023-04-04 15:33:39 -0700 | [diff] [blame] | 650 | format!("Failed to format the pointee of the pointer type `{ty}`") |
| 651 | })?; |
| 652 | quote! { * #qualifier #ty } |
| 653 | } |
Lukasz Anforowicz | aa2de7e | 2023-06-15 11:32:05 -0700 | [diff] [blame] | 654 | 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 Anforowicz | 087dff7 | 2023-02-17 12:13:32 -0800 | [diff] [blame] | 665 | _ => bail!("The following Rust type is not supported yet: {ty}"), |
Lukasz Anforowicz | 7860d0e | 2022-11-15 08:47:56 -0800 | [diff] [blame] | 666 | }) |
| 667 | } |
| 668 | |
Lukasz Anforowicz | aa2de7e | 2023-06-15 11:32:05 -0700 | [diff] [blame] | 669 | fn format_region_as_cc_lifetime(region: &ty::Region) -> TokenStream { |
Lukasz Anforowicz | 36de572 | 2023-06-16 08:17:44 -0700 | [diff] [blame] | 670 | let name = |
| 671 | region.get_name().expect("Caller should use `liberate_and_deanonymize_late_bound_regions`"); |
Lukasz Anforowicz | aa2de7e | 2023-06-15 11:32:05 -0700 | [diff] [blame] | 672 | 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 | |
| 681 | fn format_region_as_rs_lifetime(region: &ty::Region) -> TokenStream { |
Lukasz Anforowicz | 36de572 | 2023-06-16 08:17:44 -0700 | [diff] [blame] | 682 | let name = |
| 683 | region.get_name().expect("Caller should use `liberate_and_deanonymize_late_bound_regions`"); |
Lukasz Anforowicz | aa2de7e | 2023-06-15 11:32:05 -0700 | [diff] [blame] | 684 | let lifetime = syn::Lifetime::new(name.as_str(), proc_macro2::Span::call_site()); |
| 685 | quote! { #lifetime } |
| 686 | } |
| 687 | |
Lukasz Anforowicz | 73a2c0d | 2023-03-22 14:07:00 -0700 | [diff] [blame] | 688 | #[derive(Debug, Default)] |
| 689 | struct ApiSnippets { |
Lukasz Anforowicz | 33f4630 | 2023-02-10 15:38:05 -0800 | [diff] [blame] | 690 | /// 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 Anforowicz | 73a2c0d | 2023-03-22 14:07:00 -0700 | [diff] [blame] | 693 | main_api: CcSnippet, |
Lukasz Anforowicz | 33f4630 | 2023-02-10 15:38:05 -0800 | [diff] [blame] | 694 | |
Lukasz Anforowicz | 73a2c0d | 2023-03-22 14:07:00 -0700 | [diff] [blame] | 695 | /// C++ implementation details - for example: |
Lukasz Anforowicz | 33f4630 | 2023-02-10 15:38:05 -0800 | [diff] [blame] | 696 | /// - A C++ declaration of an `extern "C"` thunk, |
Lukasz Anforowicz | 73a2c0d | 2023-03-22 14:07:00 -0700 | [diff] [blame] | 697 | /// - C++ `static_assert`s about struct size, aligment, and field offsets. |
| 698 | cc_details: CcSnippet, |
| 699 | |
| 700 | /// Rust implementation details - for exmaple: |
Lukasz Anforowicz | 33f4630 | 2023-02-10 15:38:05 -0800 | [diff] [blame] | 701 | /// - A Rust implementation of an `extern "C"` thunk, |
Lukasz Anforowicz | 73a2c0d | 2023-03-22 14:07:00 -0700 | [diff] [blame] | 702 | /// - Rust `assert!`s about struct size, aligment, and field offsets. |
| 703 | rs_details: TokenStream, |
Lukasz Anforowicz | 33f4630 | 2023-02-10 15:38:05 -0800 | [diff] [blame] | 704 | } |
| 705 | |
Lukasz Anforowicz | 73a2c0d | 2023-03-22 14:07:00 -0700 | [diff] [blame] | 706 | impl 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 Anforowicz | 0b8a02f | 2023-02-10 15:55:40 -0800 | [diff] [blame] | 715 | } |
| 716 | } |
| 717 | |
Lukasz Anforowicz | 36de572 | 2023-06-16 08:17:44 -0700 | [diff] [blame] | 718 | /// Similar to `TyCtxt::liberate_and_name_late_bound_regions` but also replaces |
| 719 | /// anonymous regions with new names. |
| 720 | fn 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 Anforowicz | aa2de7e | 2023-06-15 11:32:05 -0700 | [diff] [blame] | 725 | 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 Anforowicz | 6b1ee8c | 2023-04-04 11:58:29 -0700 | [diff] [blame] | 738 | } |
| 739 | |
Lukasz Anforowicz | 36de572 | 2023-06-16 08:17:44 -0700 | [diff] [blame] | 740 | fn 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 Anforowicz | 73edf15 | 2023-04-04 12:05:00 -0700 | [diff] [blame] | 746 | /// 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 Anforowicz | 73a79ab | 2023-06-15 13:42:20 -0700 | [diff] [blame] | 749 | fn format_thunk_decl<'tcx>( |
| 750 | input: &Input<'tcx>, |
| 751 | fn_def_id: DefId, |
| 752 | sig: &ty::FnSig<'tcx>, |
Lukasz Anforowicz | 9924c43 | 2023-04-04 12:51:22 -0700 | [diff] [blame] | 753 | thunk_name: &TokenStream, |
| 754 | ) -> Result<CcSnippet> { |
Lukasz Anforowicz | 73edf15 | 2023-04-04 12:05:00 -0700 | [diff] [blame] | 755 | let tcx = input.tcx; |
Lukasz Anforowicz | 73edf15 | 2023-04-04 12:05:00 -0700 | [diff] [blame] | 756 | |
| 757 | let mut prereqs = CcPrerequisites::default(); |
Lukasz Anforowicz | e721208 | 2023-07-05 13:50:10 -0700 | [diff] [blame] | 758 | let main_api_ret_type = format_ret_ty_for_cc(input, sig)?.into_tokens(&mut prereqs); |
Lukasz Anforowicz | 73edf15 | 2023-04-04 12:05:00 -0700 | [diff] [blame] | 759 | |
Lukasz Anforowicz | c6ff453 | 2023-04-13 15:16:30 -0700 | [diff] [blame] | 760 | let mut thunk_params = { |
Lukasz Anforowicz | e721208 | 2023-07-05 13:50:10 -0700 | [diff] [blame] | 761 | let cc_types = format_param_types_for_cc(input, sig)?; |
Lukasz Anforowicz | c6ff453 | 2023-04-13 15:16:30 -0700 | [diff] [blame] | 762 | 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 Anforowicz | c6ff453 | 2023-04-13 15:16:30 -0700 | [diff] [blame] | 772 | ensure!( |
Lukasz Anforowicz | 73a79ab | 2023-06-15 13:42:20 -0700 | [diff] [blame] | 773 | !ty.needs_drop(tcx, tcx.param_env(fn_def_id)), |
Lukasz Anforowicz | c6ff453 | 2023-04-13 15:16:30 -0700 | [diff] [blame] | 774 | "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 Anforowicz | 73edf15 | 2023-04-04 12:05:00 -0700 | [diff] [blame] | 782 | |
| 783 | let thunk_ret_type: TokenStream; |
| 784 | if is_c_abi_compatible_by_value(sig.output()) { |
Lukasz Anforowicz | e721208 | 2023-07-05 13:50:10 -0700 | [diff] [blame] | 785 | thunk_ret_type = main_api_ret_type; |
Lukasz Anforowicz | 73edf15 | 2023-04-04 12:05:00 -0700 | [diff] [blame] | 786 | } 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 Anforowicz | 6b1ee8c | 2023-04-04 11:58:29 -0700 | [diff] [blame] | 802 | /// 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 Anforowicz | 73a79ab | 2023-06-15 13:42:20 -0700 | [diff] [blame] | 812 | fn format_thunk_impl<'tcx>( |
| 813 | tcx: TyCtxt<'tcx>, |
| 814 | fn_def_id: DefId, |
| 815 | sig: &ty::FnSig<'tcx>, |
Lukasz Anforowicz | 6b1ee8c | 2023-04-04 11:58:29 -0700 | [diff] [blame] | 816 | thunk_name: &str, |
| 817 | fully_qualified_fn_name: TokenStream, |
| 818 | ) -> Result<TokenStream> { |
Lukasz Anforowicz | 6b1ee8c | 2023-04-04 11:58:29 -0700 | [diff] [blame] | 819 | let param_names_and_types: Vec<(Ident, Ty)> = { |
Lukasz Anforowicz | 1b665a4 | 2023-06-20 13:04:54 -0700 | [diff] [blame] | 820 | let param_names = tcx.fn_arg_names(fn_def_id).iter().enumerate().map(|(i, ident)| { |
| 821 | if ident.as_str().is_empty() { |
Lukasz Anforowicz | 6b1ee8c | 2023-04-04 11:58:29 -0700 | [diff] [blame] | 822 | format_ident!("__param_{i}") |
Lukasz Anforowicz | 1b665a4 | 2023-06-20 13:04:54 -0700 | [diff] [blame] | 823 | } else if ident.name == kw::SelfLower { |
| 824 | format_ident!("__self") |
Lukasz Anforowicz | 6b1ee8c | 2023-04-04 11:58:29 -0700 | [diff] [blame] | 825 | } else { |
Lukasz Anforowicz | 1b665a4 | 2023-06-20 13:04:54 -0700 | [diff] [blame] | 826 | make_rs_ident(ident.as_str()) |
Lukasz Anforowicz | 6b1ee8c | 2023-04-04 11:58:29 -0700 | [diff] [blame] | 827 | } |
| 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 Anforowicz | aa2de7e | 2023-06-15 11:32:05 -0700 | [diff] [blame] | 867 | 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 Anforowicz | 36de572 | 2023-06-16 08:17:44 -0700 | [diff] [blame] | 885 | region |
| 886 | .get_name() |
| 887 | .expect("Caller should use `liberate_and_deanonymize_late_bound_regions`") |
Lukasz Anforowicz | aa2de7e | 2023-06-15 11:32:05 -0700 | [diff] [blame] | 888 | }) |
| 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(®ion)); |
| 895 | quote! { < #( #lifetimes ),* > } |
| 896 | } |
| 897 | }; |
| 898 | |
Lukasz Anforowicz | 6b1ee8c | 2023-04-04 11:58:29 -0700 | [diff] [blame] | 899 | let thunk_name = make_rs_ident(thunk_name); |
| 900 | Ok(quote! { |
| 901 | #[no_mangle] |
Lukasz Anforowicz | aa2de7e | 2023-06-15 11:32:05 -0700 | [diff] [blame] | 902 | extern "C" fn #thunk_name #generic_params ( #( #thunk_params ),* ) -> #thunk_ret_type { |
Lukasz Anforowicz | 6b1ee8c | 2023-04-04 11:58:29 -0700 | [diff] [blame] | 903 | #thunk_body |
| 904 | } |
| 905 | }) |
| 906 | } |
| 907 | |
Lukasz Anforowicz | 5c1b3ad | 2023-04-13 17:05:00 -0700 | [diff] [blame] | 908 | fn 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. |
| 927 | fn 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 Anforowicz | ae1ae17 | 2023-07-05 12:11:24 -0700 | [diff] [blame] | 958 | #[derive(Debug, Eq, PartialEq)] |
| 959 | enum 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 Anforowicz | ec4c8c7 | 2023-07-05 12:30:57 -0700 | [diff] [blame] | 968 | |
| 969 | /// Instance method taking `self` by reference (i.e. `&self` or `&mut |
| 970 | /// self`). |
| 971 | MethodTakingSelfByRef, |
Lukasz Anforowicz | ae1ae17 | 2023-07-05 12:11:24 -0700 | [diff] [blame] | 972 | } |
| 973 | |
| 974 | impl FunctionKind { |
| 975 | fn has_self_param(&self) -> bool { |
| 976 | match self { |
Lukasz Anforowicz | ec4c8c7 | 2023-07-05 12:30:57 -0700 | [diff] [blame] | 977 | FunctionKind::MethodTakingSelfByValue | FunctionKind::MethodTakingSelfByRef => true, |
Lukasz Anforowicz | ae1ae17 | 2023-07-05 12:11:24 -0700 | [diff] [blame] | 978 | FunctionKind::Free | FunctionKind::StaticMethod => false, |
| 979 | } |
| 980 | } |
| 981 | } |
| 982 | |
Googler | fb20427 | 2022-12-02 00:52:05 -0800 | [diff] [blame] | 983 | /// Formats a function with the given `local_def_id`. |
Lukasz Anforowicz | e433306 | 2022-10-17 14:47:53 -0700 | [diff] [blame] | 984 | /// |
Googler | fb20427 | 2022-12-02 00:52:05 -0800 | [diff] [blame] | 985 | /// Will panic if `local_def_id` |
Lukasz Anforowicz | 7123f21 | 2022-10-20 08:51:04 -0700 | [diff] [blame] | 986 | /// - is invalid |
| 987 | /// - doesn't identify a function, |
Lukasz Anforowicz | 73a2c0d | 2023-03-22 14:07:00 -0700 | [diff] [blame] | 988 | fn format_fn(input: &Input, local_def_id: LocalDefId) -> Result<ApiSnippets> { |
Lukasz Anforowicz | be25c76 | 2023-01-17 12:43:52 -0800 | [diff] [blame] | 989 | let tcx = input.tcx; |
Googler | 624580b | 2022-12-01 01:23:42 -0800 | [diff] [blame] | 990 | let def_id: DefId = local_def_id.to_def_id(); // Convert LocalDefId to DefId. |
Lukasz Anforowicz | 903fc63 | 2022-10-25 08:55:33 -0700 | [diff] [blame] | 991 | |
Lukasz Anforowicz | 56d94fc | 2023-02-17 12:23:20 -0800 | [diff] [blame] | 992 | ensure!( |
| 993 | tcx.generics_of(def_id).count() == 0, |
| 994 | "Generic functions are not supported yet (b/259749023)" |
| 995 | ); |
Lukasz Anforowicz | 56d94fc | 2023-02-17 12:23:20 -0800 | [diff] [blame] | 996 | |
Lukasz Anforowicz | aa2de7e | 2023-06-15 11:32:05 -0700 | [diff] [blame] | 997 | let sig = get_fn_sig(tcx, local_def_id); |
Lukasz Anforowicz | 5c1b3ad | 2023-04-13 17:05:00 -0700 | [diff] [blame] | 998 | check_fn_sig(&sig)?; |
| 999 | let needs_thunk = is_thunk_required(&sig).is_err(); |
Lukasz Anforowicz | 9924c43 | 2023-04-04 12:51:22 -0700 | [diff] [blame] | 1000 | let thunk_name = { |
Lukasz Anforowicz | 36de572 | 2023-06-16 08:17:44 -0700 | [diff] [blame] | 1001 | 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 Anforowicz | 9924c43 | 2023-04-04 12:51:22 -0700 | [diff] [blame] | 1011 | }; |
Googler | 624580b | 2022-12-01 01:23:42 -0800 | [diff] [blame] | 1012 | |
Lukasz Anforowicz | 954696e | 2023-05-10 12:18:48 -0700 | [diff] [blame] | 1013 | 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 Anforowicz | a691cf5 | 2023-03-08 12:24:33 -0800 | [diff] [blame] | 1016 | let main_api_fn_name = |
Lukasz Anforowicz | 954696e | 2023-05-10 12:18:48 -0700 | [diff] [blame] | 1017 | format_cc_ident(short_fn_name.as_str()).context("Error formatting function name")?; |
Lukasz Anforowicz | a577d82 | 2022-12-12 15:00:46 -0800 | [diff] [blame] | 1018 | |
Lukasz Anforowicz | a691cf5 | 2023-03-08 12:24:33 -0800 | [diff] [blame] | 1019 | let mut main_api_prereqs = CcPrerequisites::default(); |
Lukasz Anforowicz | 8574c9d | 2023-04-13 15:11:20 -0700 | [diff] [blame] | 1020 | let main_api_ret_type = format_ret_ty_for_cc(input, &sig)?.into_tokens(&mut main_api_prereqs); |
Lukasz Anforowicz | a691cf5 | 2023-03-08 12:24:33 -0800 | [diff] [blame] | 1021 | |
| 1022 | struct Param<'tcx> { |
| 1023 | cc_name: TokenStream, |
| 1024 | cc_type: TokenStream, |
Lukasz Anforowicz | a691cf5 | 2023-03-08 12:24:33 -0800 | [diff] [blame] | 1025 | ty: Ty<'tcx>, |
| 1026 | } |
| 1027 | let params = { |
| 1028 | let names = tcx.fn_arg_names(def_id).iter(); |
Lukasz Anforowicz | c6ff453 | 2023-04-13 15:16:30 -0700 | [diff] [blame] | 1029 | let cc_types = format_param_types_for_cc(input, &sig)?; |
Lukasz Anforowicz | a691cf5 | 2023-03-08 12:24:33 -0800 | [diff] [blame] | 1030 | names |
Lukasz Anforowicz | a691cf5 | 2023-03-08 12:24:33 -0800 | [diff] [blame] | 1031 | .enumerate() |
Lukasz Anforowicz | c6ff453 | 2023-04-13 15:16:30 -0700 | [diff] [blame] | 1032 | .zip(sig.inputs().iter()) |
| 1033 | .zip(cc_types.into_iter()) |
| 1034 | .map(|(((i, name), &ty), cc_type)| { |
Lukasz Anforowicz | a691cf5 | 2023-03-08 12:24:33 -0800 | [diff] [blame] | 1035 | let cc_name = format_cc_ident(name.as_str()) |
| 1036 | .unwrap_or_else(|_err| format_cc_ident(&format!("__param_{i}")).unwrap()); |
Lukasz Anforowicz | c6ff453 | 2023-04-13 15:16:30 -0700 | [diff] [blame] | 1037 | let cc_type = cc_type.into_tokens(&mut main_api_prereqs); |
| 1038 | Param { cc_name, cc_type, ty } |
Lukasz Anforowicz | a691cf5 | 2023-03-08 12:24:33 -0800 | [diff] [blame] | 1039 | }) |
Lukasz Anforowicz | c6ff453 | 2023-04-13 15:16:30 -0700 | [diff] [blame] | 1040 | .collect_vec() |
Lukasz Anforowicz | a691cf5 | 2023-03-08 12:24:33 -0800 | [diff] [blame] | 1041 | }; |
Lukasz Anforowicz | 6753d40 | 2023-02-10 16:41:23 -0800 | [diff] [blame] | 1042 | |
Lukasz Anforowicz | ae1ae17 | 2023-07-05 12:11:24 -0700 | [diff] [blame] | 1043 | let self_ty: Option<Ty> = match tcx.impl_of_method(def_id) { |
Googler | e158565 | 2023-04-24 06:02:39 -0700 | [diff] [blame] | 1044 | Some(impl_id) => match tcx.impl_subject(impl_id).subst_identity() { |
Lukasz Anforowicz | ae1ae17 | 2023-07-05 12:11:24 -0700 | [diff] [blame] | 1045 | ty::ImplSubject::Inherent(ty) => Some(ty), |
Lukasz Anforowicz | 43f06c6 | 2023-02-10 17:14:19 -0800 | [diff] [blame] | 1046 | ty::ImplSubject::Trait(_) => panic!("Trait methods should be filtered by caller"), |
| 1047 | }, |
| 1048 | None => None, |
| 1049 | }; |
Lukasz Anforowicz | ae1ae17 | 2023-07-05 12:11:24 -0700 | [diff] [blame] | 1050 | |
| 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 Anforowicz | ec4c8c7 | 2023-07-05 12:30:57 -0700 | [diff] [blame] | 1059 | 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 Anforowicz | ae1ae17 | 2023-07-05 12:11:24 -0700 | [diff] [blame] | 1065 | } |
| 1066 | } |
| 1067 | _ => FunctionKind::StaticMethod, |
| 1068 | }, |
| 1069 | other => panic!("Unexpected HIR node kind: {other:?}"), |
| 1070 | }; |
Lukasz Anforowicz | ec4c8c7 | 2023-07-05 12:30:57 -0700 | [diff] [blame] | 1071 | 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 Anforowicz | ae1ae17 | 2023-07-05 12:11:24 -0700 | [diff] [blame] | 1085 | }; |
| 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 Anforowicz | 954696e | 2023-05-10 12:18:48 -0700 | [diff] [blame] | 1097 | let needs_definition = short_fn_name.as_str() != thunk_name; |
Lukasz Anforowicz | ae1ae17 | 2023-07-05 12:11:24 -0700 | [diff] [blame] | 1098 | 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 Anforowicz | 6753d40 | 2023-02-10 16:41:23 -0800 | [diff] [blame] | 1103 | 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 Anforowicz | a691cf5 | 2023-03-08 12:24:33 -0800 | [diff] [blame] | 1109 | let mut prereqs = main_api_prereqs.clone(); |
Lukasz Anforowicz | 6753d40 | 2023-02-10 16:41:23 -0800 | [diff] [blame] | 1110 | prereqs.move_defs_to_fwd_decls(); |
Lukasz Anforowicz | a691cf5 | 2023-03-08 12:24:33 -0800 | [diff] [blame] | 1111 | |
Lukasz Anforowicz | ae1ae17 | 2023-07-05 12:11:24 -0700 | [diff] [blame] | 1112 | let static_ = if method_kind == FunctionKind::StaticMethod { |
Lukasz Anforowicz | a691cf5 | 2023-03-08 12:24:33 -0800 | [diff] [blame] | 1113 | quote! { static } |
| 1114 | } else { |
| 1115 | quote! {} |
| 1116 | }; |
Lukasz Anforowicz | b9e515a | 2023-07-05 12:48:22 -0700 | [diff] [blame] | 1117 | let extern_c = if !needs_definition { |
Lukasz Anforowicz | 6753d40 | 2023-02-10 16:41:23 -0800 | [diff] [blame] | 1118 | quote! { extern "C" } |
Lukasz Anforowicz | 2f849cf | 2022-11-17 15:52:39 -0800 | [diff] [blame] | 1119 | } else { |
Lukasz Anforowicz | b9e515a | 2023-07-05 12:48:22 -0700 | [diff] [blame] | 1120 | quote! {} |
Lukasz Anforowicz | 6753d40 | 2023-02-10 16:41:23 -0800 | [diff] [blame] | 1121 | }; |
| 1122 | CcSnippet { |
| 1123 | prereqs, |
| 1124 | tokens: quote! { |
Lukasz Anforowicz | a0502fb | 2023-02-13 15:33:18 -0800 | [diff] [blame] | 1125 | __NEWLINE__ |
Lukasz Anforowicz | 6753d40 | 2023-02-10 16:41:23 -0800 | [diff] [blame] | 1126 | #doc_comment |
Lukasz Anforowicz | b9e515a | 2023-07-05 12:48:22 -0700 | [diff] [blame] | 1127 | #static_ #extern_c |
Lukasz Anforowicz | ae1ae17 | 2023-07-05 12:11:24 -0700 | [diff] [blame] | 1128 | #main_api_ret_type #main_api_fn_name ( |
| 1129 | #( #main_api_params ),* |
| 1130 | ) #method_qualifiers; |
Lukasz Anforowicz | a0502fb | 2023-02-13 15:33:18 -0800 | [diff] [blame] | 1131 | __NEWLINE__ |
Lukasz Anforowicz | 6753d40 | 2023-02-10 16:41:23 -0800 | [diff] [blame] | 1132 | }, |
| 1133 | } |
| 1134 | }; |
Lukasz Anforowicz | 73a2c0d | 2023-03-22 14:07:00 -0700 | [diff] [blame] | 1135 | let cc_details = if !needs_definition { |
| 1136 | CcSnippet::default() |
Lukasz Anforowicz | 6753d40 | 2023-02-10 16:41:23 -0800 | [diff] [blame] | 1137 | } else { |
Devin Jeanpierre | 81aec50 | 2023-04-04 15:33:39 -0700 | [diff] [blame] | 1138 | let thunk_name = format_cc_ident(&thunk_name).context("Error formatting thunk name")?; |
Lukasz Anforowicz | 73a2c0d | 2023-03-22 14:07:00 -0700 | [diff] [blame] | 1139 | let struct_name = match struct_name.as_ref() { |
| 1140 | None => quote! {}, |
Lukasz Anforowicz | 954696e | 2023-05-10 12:18:48 -0700 | [diff] [blame] | 1141 | 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 Anforowicz | 73a2c0d | 2023-03-22 14:07:00 -0700 | [diff] [blame] | 1144 | .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 Jeanpierre | 81aec50 | 2023-04-04 15:33:39 -0700 | [diff] [blame] | 1150 | let thunk_decl = |
Lukasz Anforowicz | 73a79ab | 2023-06-15 13:42:20 -0700 | [diff] [blame] | 1151 | format_thunk_decl(input, def_id, &sig, &thunk_name)?.into_tokens(&mut prereqs); |
Lukasz Anforowicz | 73edf15 | 2023-04-04 12:05:00 -0700 | [diff] [blame] | 1152 | |
Lukasz Anforowicz | 73a2c0d | 2023-03-22 14:07:00 -0700 | [diff] [blame] | 1153 | let mut thunk_args = params |
| 1154 | .iter() |
Lukasz Anforowicz | ae1ae17 | 2023-07-05 12:11:24 -0700 | [diff] [blame] | 1155 | .enumerate() |
| 1156 | .map(|(i, Param { cc_name, ty, .. })| { |
| 1157 | if i == 0 && method_kind.has_self_param() { |
Lukasz Anforowicz | ec4c8c7 | 2023-07-05 12:30:57 -0700 | [diff] [blame] | 1158 | if method_kind == FunctionKind::MethodTakingSelfByValue { |
| 1159 | quote! { this } |
| 1160 | } else { |
| 1161 | quote! { *this } |
| 1162 | } |
Lukasz Anforowicz | ae1ae17 | 2023-07-05 12:11:24 -0700 | [diff] [blame] | 1163 | } else if is_c_abi_compatible_by_value(*ty) { |
Lukasz Anforowicz | 73a2c0d | 2023-03-22 14:07:00 -0700 | [diff] [blame] | 1164 | quote! { #cc_name } |
| 1165 | } else { |
| 1166 | quote! { & #cc_name } |
| 1167 | } |
| 1168 | }) |
| 1169 | .collect_vec(); |
Lukasz Anforowicz | 73a2c0d | 2023-03-22 14:07:00 -0700 | [diff] [blame] | 1170 | let impl_body: TokenStream; |
| 1171 | if is_c_abi_compatible_by_value(sig.output()) { |
Lukasz Anforowicz | 73a2c0d | 2023-03-22 14:07:00 -0700 | [diff] [blame] | 1172 | impl_body = quote! { |
| 1173 | return __crubit_internal :: #thunk_name( #( #thunk_args ),* ); |
| 1174 | }; |
| 1175 | } else { |
Lukasz Anforowicz | 73a2c0d | 2023-03-22 14:07:00 -0700 | [diff] [blame] | 1176 | 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 Anforowicz | 73edf15 | 2023-04-04 12:05:00 -0700 | [diff] [blame] | 1189 | #thunk_decl |
Lukasz Anforowicz | 73a2c0d | 2023-03-22 14:07:00 -0700 | [diff] [blame] | 1190 | inline #main_api_ret_type #struct_name #main_api_fn_name ( |
Lukasz Anforowicz | ae1ae17 | 2023-07-05 12:11:24 -0700 | [diff] [blame] | 1191 | #( #main_api_params ),* ) #method_qualifiers { |
Lukasz Anforowicz | 73a2c0d | 2023-03-22 14:07:00 -0700 | [diff] [blame] | 1192 | #impl_body |
| 1193 | } |
| 1194 | __NEWLINE__ |
| 1195 | }, |
| 1196 | } |
| 1197 | }; |
| 1198 | |
| 1199 | let rs_details = if !needs_thunk { |
| 1200 | quote! {} |
| 1201 | } else { |
Lukasz Anforowicz | 954696e | 2023-05-10 12:18:48 -0700 | [diff] [blame] | 1202 | 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 Anforowicz | bad9963 | 2022-11-18 16:39:13 -0800 | [diff] [blame] | 1208 | } |
Lukasz Anforowicz | 6753d40 | 2023-02-10 16:41:23 -0800 | [diff] [blame] | 1209 | }; |
Lukasz Anforowicz | 73a79ab | 2023-06-15 13:42:20 -0700 | [diff] [blame] | 1210 | format_thunk_impl(tcx, def_id, &sig, &thunk_name, fully_qualified_fn_name)? |
Lukasz Anforowicz | bad9963 | 2022-11-18 16:39:13 -0800 | [diff] [blame] | 1211 | }; |
Lukasz Anforowicz | 73a2c0d | 2023-03-22 14:07:00 -0700 | [diff] [blame] | 1212 | Ok(ApiSnippets { main_api, cc_details, rs_details }) |
Lukasz Anforowicz | e433306 | 2022-10-17 14:47:53 -0700 | [diff] [blame] | 1213 | } |
| 1214 | |
Lukasz Anforowicz | 7589483 | 2022-11-22 18:25:28 -0800 | [diff] [blame] | 1215 | /// 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 Anforowicz | f4501b4 | 2023-01-07 09:41:29 -0800 | [diff] [blame] | 1221 | /// #keyword #alignment #name final { |
Lukasz Anforowicz | 7589483 | 2022-11-22 18:25:28 -0800 | [diff] [blame] | 1222 | /// #core |
Lukasz Anforowicz | 3b57954 | 2023-02-06 11:23:49 -0800 | [diff] [blame] | 1223 | /// #decls_of_other_parts // (e.g. struct fields, methods, etc.) |
Lukasz Anforowicz | 7589483 | 2022-11-22 18:25:28 -0800 | [diff] [blame] | 1224 | /// } |
| 1225 | /// } |
| 1226 | /// ``` |
Lukasz Anforowicz | f4501b4 | 2023-01-07 09:41:29 -0800 | [diff] [blame] | 1227 | /// |
| 1228 | /// `keyword`, `name` are stored separately, to support formatting them as a |
| 1229 | /// forward declaration - e.g. `struct SomeStruct`. |
Lukasz Anforowicz | 15a6002 | 2023-07-07 09:05:49 -0700 | [diff] [blame] | 1230 | struct AdtCoreBindings<'tcx> { |
Lukasz Anforowicz | 3b57954 | 2023-02-06 11:23:49 -0800 | [diff] [blame] | 1231 | /// DefId of the ADT. |
| 1232 | def_id: DefId, |
| 1233 | |
Lukasz Anforowicz | f4501b4 | 2023-01-07 09:41:29 -0800 | [diff] [blame] | 1234 | /// 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 Anforowicz | f4501b4 | 2023-01-07 09:41:29 -0800 | [diff] [blame] | 1239 | /// C++ translation of the ADT identifier - e.g. `SomeStruct`. |
Lukasz Anforowicz | ce56a80 | 2023-04-04 12:18:19 -0700 | [diff] [blame] | 1240 | /// |
| 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 Anforowicz | 7589483 | 2022-11-22 18:25:28 -0800 | [diff] [blame] | 1246 | |
Lukasz Anforowicz | 3b57954 | 2023-02-06 11:23:49 -0800 | [diff] [blame] | 1247 | /// Rust spelling of the ADT type - e.g. |
Lukasz Anforowicz | ce56a80 | 2023-04-04 12:18:19 -0700 | [diff] [blame] | 1248 | /// `::some_crate::some_module::SomeStruct`. |
| 1249 | rs_fully_qualified_name: TokenStream, |
Lukasz Anforowicz | 3b57954 | 2023-02-06 11:23:49 -0800 | [diff] [blame] | 1250 | |
Lukasz Anforowicz | 15a6002 | 2023-07-07 09:05:49 -0700 | [diff] [blame] | 1251 | self_ty: Ty<'tcx>, |
Lukasz Anforowicz | 3b57954 | 2023-02-06 11:23:49 -0800 | [diff] [blame] | 1252 | alignment_in_bytes: u64, |
| 1253 | size_in_bytes: u64, |
Lukasz Anforowicz | 7589483 | 2022-11-22 18:25:28 -0800 | [diff] [blame] | 1254 | } |
| 1255 | |
Lukasz Anforowicz | f36762a | 2023-03-02 18:43:07 -0800 | [diff] [blame] | 1256 | /// Like `TyCtxt::is_directly_public`, but works not only with `LocalDefId`, but |
| 1257 | /// also with `DefId`. |
| 1258 | fn 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 Anforowicz | 735bfb8 | 2023-03-15 16:18:44 -0700 | [diff] [blame] | 1271 | fn get_layout<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Result<Layout<'tcx>> { |
Lukasz Anforowicz | 2aab019 | 2023-06-15 12:05:39 -0700 | [diff] [blame] | 1272 | 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 Anforowicz | 735bfb8 | 2023-03-15 16:18:44 -0700 | [diff] [blame] | 1276 | |
| 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 Anforowicz | 7589483 | 2022-11-22 18:25:28 -0800 | [diff] [blame] | 1286 | /// 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 Anforowicz | 1ab5e87 | 2022-12-05 10:07:00 -0800 | [diff] [blame] | 1304 | // TODO(b/259724276): This function's results should be memoized. |
Lukasz Anforowicz | 15a6002 | 2023-07-07 09:05:49 -0700 | [diff] [blame] | 1305 | fn 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 Anforowicz | f36762a | 2023-03-02 18:43:07 -0800 | [diff] [blame] | 1308 | assert!(is_directly_public(tcx, def_id), "Caller should verify"); |
| 1309 | |
Lukasz Anforowicz | 15a6002 | 2023-07-07 09:05:49 -0700 | [diff] [blame] | 1310 | if self_ty.needs_drop(tcx, tcx.param_env(def_id)) { |
Lukasz Anforowicz | 7589483 | 2022-11-22 18:25:28 -0800 | [diff] [blame] | 1311 | // TODO(b/258251148): Support custom `Drop` impls. |
| 1312 | bail!("`Drop` trait and \"drop glue\" are not supported yet (b/258251148)"); |
| 1313 | } |
| 1314 | |
Lukasz Anforowicz | 15a6002 | 2023-07-07 09:05:49 -0700 | [diff] [blame] | 1315 | let adt_def = self_ty.ty_adt_def().expect("`def_id` needs to identify an ADT"); |
Lukasz Anforowicz | cc7a76b | 2023-02-28 14:19:42 -0800 | [diff] [blame] | 1316 | let keyword = match adt_def.adt_kind() { |
Lukasz Anforowicz | 32ad956 | 2023-03-10 14:48:25 -0800 | [diff] [blame] | 1317 | ty::AdtKind::Struct | ty::AdtKind::Enum => quote! { struct }, |
Lukasz Anforowicz | cc7a76b | 2023-02-28 14:19:42 -0800 | [diff] [blame] | 1318 | ty::AdtKind::Union => quote! { union }, |
Lukasz Anforowicz | cc7a76b | 2023-02-28 14:19:42 -0800 | [diff] [blame] | 1319 | }; |
| 1320 | |
Lukasz Anforowicz | 735bfb8 | 2023-03-15 16:18:44 -0700 | [diff] [blame] | 1321 | let item_name = tcx.item_name(def_id); |
Lukasz Anforowicz | 15a6002 | 2023-07-07 09:05:49 -0700 | [diff] [blame] | 1322 | let rs_fully_qualified_name = format_ty_for_rs(tcx, self_ty)?; |
Lukasz Anforowicz | ce56a80 | 2023-04-04 12:18:19 -0700 | [diff] [blame] | 1323 | let cc_short_name = |
| 1324 | format_cc_ident(item_name.as_str()).context("Error formatting item name")?; |
Lukasz Anforowicz | 3b57954 | 2023-02-06 11:23:49 -0800 | [diff] [blame] | 1325 | |
Lukasz Anforowicz | 15a6002 | 2023-07-07 09:05:49 -0700 | [diff] [blame] | 1326 | let layout = get_layout(tcx, self_ty) |
Lukasz Anforowicz | 735bfb8 | 2023-03-15 16:18:44 -0700 | [diff] [blame] | 1327 | .with_context(|| format!("Error computing the layout of #{item_name}"))?; |
Lukasz Anforowicz | 74e090d | 2023-04-25 15:14:14 -0700 | [diff] [blame] | 1328 | ensure!(layout.abi().is_sized(), "Bindings for dynamically sized types are not supported."); |
Lukasz Anforowicz | 3b57954 | 2023-02-06 11:23:49 -0800 | [diff] [blame] | 1329 | let alignment_in_bytes = { |
Lukasz Anforowicz | 7589483 | 2022-11-22 18:25:28 -0800 | [diff] [blame] | 1330 | // 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 Anforowicz | 3b57954 | 2023-02-06 11:23:49 -0800 | [diff] [blame] | 1334 | layout.align().abi.bytes() |
Lukasz Anforowicz | 7589483 | 2022-11-22 18:25:28 -0800 | [diff] [blame] | 1335 | }; |
Lukasz Anforowicz | 3b57954 | 2023-02-06 11:23:49 -0800 | [diff] [blame] | 1336 | let size_in_bytes = layout.size().bytes(); |
| 1337 | ensure!(size_in_bytes != 0, "Zero-sized types (ZSTs) are not supported (b/258259459)"); |
Lukasz Anforowicz | 7589483 | 2022-11-22 18:25:28 -0800 | [diff] [blame] | 1338 | |
Lukasz Anforowicz | f4501b4 | 2023-01-07 09:41:29 -0800 | [diff] [blame] | 1339 | Ok(AdtCoreBindings { |
Lukasz Anforowicz | 3b57954 | 2023-02-06 11:23:49 -0800 | [diff] [blame] | 1340 | def_id, |
Lukasz Anforowicz | cc7a76b | 2023-02-28 14:19:42 -0800 | [diff] [blame] | 1341 | keyword, |
Lukasz Anforowicz | ce56a80 | 2023-04-04 12:18:19 -0700 | [diff] [blame] | 1342 | cc_short_name, |
| 1343 | rs_fully_qualified_name, |
Lukasz Anforowicz | 15a6002 | 2023-07-07 09:05:49 -0700 | [diff] [blame] | 1344 | self_ty, |
Lukasz Anforowicz | 3b57954 | 2023-02-06 11:23:49 -0800 | [diff] [blame] | 1345 | alignment_in_bytes, |
| 1346 | size_in_bytes, |
Lukasz Anforowicz | f4501b4 | 2023-01-07 09:41:29 -0800 | [diff] [blame] | 1347 | }) |
Lukasz Anforowicz | 7589483 | 2022-11-22 18:25:28 -0800 | [diff] [blame] | 1348 | } |
| 1349 | |
Lukasz Anforowicz | 15a6002 | 2023-07-07 09:05:49 -0700 | [diff] [blame] | 1350 | fn format_fields<'tcx>(input: &Input<'tcx>, core: &AdtCoreBindings<'tcx>) -> ApiSnippets { |
Lukasz Anforowicz | 43f06c6 | 2023-02-10 17:14:19 -0800 | [diff] [blame] | 1351 | let tcx = input.tcx; |
| 1352 | |
Lukasz Anforowicz | 2eb2429 | 2023-03-14 09:38:21 -0700 | [diff] [blame] | 1353 | // TODO(b/259749095): Support non-empty set of generic parameters. |
Googler | a1e7228 | 2023-04-25 08:06:40 -0700 | [diff] [blame] | 1354 | let substs_ref = ty::List::empty(); |
Lukasz Anforowicz | 2eb2429 | 2023-03-14 09:38:21 -0700 | [diff] [blame] | 1355 | |
Lukasz Anforowicz | 672c515 | 2023-03-15 16:23:00 -0700 | [diff] [blame] | 1356 | struct FieldTypeInfo { |
| 1357 | size: u64, |
Lukasz Anforowicz | 2eb2429 | 2023-03-14 09:38:21 -0700 | [diff] [blame] | 1358 | cc_type: CcSnippet, |
Lukasz Anforowicz | 672c515 | 2023-03-15 16:23:00 -0700 | [diff] [blame] | 1359 | } |
| 1360 | struct Field { |
| 1361 | type_info: Result<FieldTypeInfo>, |
| 1362 | cc_name: TokenStream, |
Lukasz Anforowicz | cf60f52 | 2023-03-14 10:03:55 -0700 | [diff] [blame] | 1363 | rs_name: TokenStream, |
| 1364 | is_public: bool, |
Lukasz Anforowicz | 735bfb8 | 2023-03-15 16:18:44 -0700 | [diff] [blame] | 1365 | index: usize, |
Lukasz Anforowicz | 2eb2429 | 2023-03-14 09:38:21 -0700 | [diff] [blame] | 1366 | offset: u64, |
Lukasz Anforowicz | 735bfb8 | 2023-03-15 16:18:44 -0700 | [diff] [blame] | 1367 | offset_of_next_field: u64, |
Lukasz Anforowicz | f4b3760 | 2023-06-01 16:16:34 -0700 | [diff] [blame] | 1368 | doc_comment: TokenStream, |
Lukasz Anforowicz | 2eb2429 | 2023-03-14 09:38:21 -0700 | [diff] [blame] | 1369 | } |
Lukasz Anforowicz | 15a6002 | 2023-07-07 09:05:49 -0700 | [diff] [blame] | 1370 | 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 Anforowicz | 2eb2429 | 2023-03-14 09:38:21 -0700 | [diff] [blame] | 1373 | // Note that `#[repr(Rust)]` unions don't guarantee that all their fields |
| 1374 | // have offset 0. |
Lukasz Anforowicz | 672c515 | 2023-03-15 16:23:00 -0700 | [diff] [blame] | 1375 | 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 Anforowicz | f4b3760 | 2023-06-01 16:16:34 -0700 | [diff] [blame] | 1386 | doc_comment: quote! {}, |
Lukasz Anforowicz | 672c515 | 2023-03-15 16:23:00 -0700 | [diff] [blame] | 1387 | }] |
Lukasz Anforowicz | 2eb2429 | 2023-03-14 09:38:21 -0700 | [diff] [blame] | 1388 | } else { |
Lukasz Anforowicz | 15a6002 | 2023-07-07 09:05:49 -0700 | [diff] [blame] | 1389 | let mut fields = core |
| 1390 | .self_ty |
Lukasz Anforowicz | 2eb2429 | 2023-03-14 09:38:21 -0700 | [diff] [blame] | 1391 | .ty_adt_def() |
| 1392 | .expect("`core.def_id` needs to identify an ADT") |
| 1393 | .all_fields() |
Lukasz Anforowicz | 672c515 | 2023-03-15 16:23:00 -0700 | [diff] [blame] | 1394 | .sorted_by_key(|f| tcx.def_span(f.did)) |
Lukasz Anforowicz | 2eb2429 | 2023-03-14 09:38:21 -0700 | [diff] [blame] | 1395 | .enumerate() |
Lukasz Anforowicz | 672c515 | 2023-03-15 16:23:00 -0700 | [diff] [blame] | 1396 | .map(|(index, field_def)| { |
Lukasz Anforowicz | 735bfb8 | 2023-03-15 16:18:44 -0700 | [diff] [blame] | 1397 | let field_ty = field_def.ty(tcx, substs_ref); |
Lukasz Anforowicz | 672c515 | 2023-03-15 16:23:00 -0700 | [diff] [blame] | 1398 | let size = get_layout(tcx, field_ty).map(|layout| layout.size().bytes()); |
| 1399 | let type_info = size.and_then(|size| { |
Googler | 7a77a80 | 2023-04-21 08:32:50 -0700 | [diff] [blame] | 1400 | Ok(FieldTypeInfo { |
| 1401 | size, |
| 1402 | cc_type: format_ty_for_cc(input, field_ty, TypeLocation::Other)?, |
| 1403 | }) |
Lukasz Anforowicz | 672c515 | 2023-03-15 16:23:00 -0700 | [diff] [blame] | 1404 | }); |
| 1405 | let name = field_def.ident(tcx); |
Devin Jeanpierre | 81aec50 | 2023-04-04 15:33:39 -0700 | [diff] [blame] | 1406 | let cc_name = format_cc_ident(name.as_str()) |
| 1407 | .unwrap_or_else(|_err| format_ident!("__field{index}").into_token_stream()); |
Lukasz Anforowicz | cf60f52 | 2023-03-14 10:03:55 -0700 | [diff] [blame] | 1408 | 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 Anforowicz | 735bfb8 | 2023-03-15 16:18:44 -0700 | [diff] [blame] | 1423 | |
| 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 Anforowicz | 672c515 | 2023-03-15 16:23:00 -0700 | [diff] [blame] | 1429 | Field { |
| 1430 | type_info, |
Lukasz Anforowicz | 735bfb8 | 2023-03-15 16:18:44 -0700 | [diff] [blame] | 1431 | cc_name, |
Lukasz Anforowicz | 735bfb8 | 2023-03-15 16:18:44 -0700 | [diff] [blame] | 1432 | rs_name, |
Lukasz Anforowicz | f4b3760 | 2023-06-01 16:16:34 -0700 | [diff] [blame] | 1433 | is_public: field_def.vis == ty::Visibility::Public, |
Lukasz Anforowicz | 735bfb8 | 2023-03-15 16:18:44 -0700 | [diff] [blame] | 1434 | index, |
Lukasz Anforowicz | 735bfb8 | 2023-03-15 16:18:44 -0700 | [diff] [blame] | 1435 | offset, |
| 1436 | offset_of_next_field, |
Lukasz Anforowicz | f4b3760 | 2023-06-01 16:16:34 -0700 | [diff] [blame] | 1437 | doc_comment: format_doc_comment(tcx, field_def.did.expect_local()), |
Lukasz Anforowicz | 2eb2429 | 2023-03-14 09:38:21 -0700 | [diff] [blame] | 1438 | } |
| 1439 | }) |
Lukasz Anforowicz | 672c515 | 2023-03-15 16:23:00 -0700 | [diff] [blame] | 1440 | .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 Jeanpierre | 9e15d0b | 2023-04-06 13:18:22 -0700 | [diff] [blame] | 1450 | // 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 Anforowicz | 672c515 | 2023-03-15 16:23:00 -0700 | [diff] [blame] | 1452 | 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 Anforowicz | 2eb2429 | 2023-03-14 09:38:21 -0700 | [diff] [blame] | 1465 | }; |
| 1466 | |
Lukasz Anforowicz | b7145e6 | 2023-03-23 09:00:37 -0700 | [diff] [blame] | 1467 | let cc_details = if fields.is_empty() { |
| 1468 | CcSnippet::default() |
| 1469 | } else { |
Lukasz Anforowicz | ce56a80 | 2023-04-04 12:18:19 -0700 | [diff] [blame] | 1470 | let adt_cc_name = &core.cc_short_name; |
Lukasz Anforowicz | b7145e6 | 2023-03-23 09:00:37 -0700 | [diff] [blame] | 1471 | 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 Jeanpierre | b9b05fc | 2023-05-15 12:23:27 -0700 | [diff] [blame] | 1478 | 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 Anforowicz | b7145e6 | 2023-03-23 09:00:37 -0700 | [diff] [blame] | 1486 | }; |
| 1487 | let rs_details: TokenStream = { |
Lukasz Anforowicz | ce56a80 | 2023-04-04 12:18:19 -0700 | [diff] [blame] | 1488 | let adt_rs_name = &core.rs_fully_qualified_name; |
Lukasz Anforowicz | b7145e6 | 2023-03-23 09:00:37 -0700 | [diff] [blame] | 1489 | 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 Anforowicz | 0b8a02f | 2023-02-10 15:55:40 -0800 | [diff] [blame] | 1499 | let main_api = { |
Lukasz Anforowicz | b7145e6 | 2023-03-23 09:00:37 -0700 | [diff] [blame] | 1500 | 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 Anforowicz | b9e515a | 2023-07-05 12:48:22 -0700 | [diff] [blame] | 1505 | quote! { private: static void __crubit_field_offset_assertions(); } |
Lukasz Anforowicz | 2eb2429 | 2023-03-14 09:38:21 -0700 | [diff] [blame] | 1506 | }; |
| 1507 | |
Lukasz Anforowicz | 1422976 | 2023-02-10 15:28:33 -0800 | [diff] [blame] | 1508 | let mut prereqs = CcPrerequisites::default(); |
Lukasz Anforowicz | 672c515 | 2023-03-15 16:23:00 -0700 | [diff] [blame] | 1509 | 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 Jeanpierre | 9e15d0b | 2023-04-06 13:18:22 -0700 | [diff] [blame] | 1515 | let size = field.offset_of_next_field - field.offset; |
Lukasz Anforowicz | 672c515 | 2023-03-15 16:23:00 -0700 | [diff] [blame] | 1516 | let msg = |
| 1517 | format!("Field type has been replaced with a blob of bytes: {err:#}"); |
Devin Jeanpierre | 9e15d0b | 2023-04-06 13:18:22 -0700 | [diff] [blame] | 1518 | |
| 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 Anforowicz | 52bc49d | 2023-06-09 03:12:57 -0700 | [diff] [blame] | 1523 | private: __NEWLINE__ |
Lukasz Anforowicz | 68c8882 | 2023-06-01 16:06:15 -0700 | [diff] [blame] | 1524 | __COMMENT__ #msg |
| 1525 | unsigned char #cc_name[#size]; |
Devin Jeanpierre | 9e15d0b | 2023-04-06 13:18:22 -0700 | [diff] [blame] | 1526 | } |
| 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 Anforowicz | 52bc49d | 2023-06-09 03:12:57 -0700 | [diff] [blame] | 1533 | private: __NEWLINE__ |
Lukasz Anforowicz | 68c8882 | 2023-06-01 16:06:15 -0700 | [diff] [blame] | 1534 | __COMMENT__ #msg |
| 1535 | [[no_unique_address]] struct{} #cc_name; |
Devin Jeanpierre | 9e15d0b | 2023-04-06 13:18:22 -0700 | [diff] [blame] | 1536 | } |
Lukasz Anforowicz | 672c515 | 2023-03-15 16:23:00 -0700 | [diff] [blame] | 1537 | } |
| 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 Anforowicz | 68c8882 | 2023-06-01 16:06:15 -0700 | [diff] [blame] | 1546 | quote! { private: unsigned char #ident[#padding]; } |
| 1547 | }; |
| 1548 | let visibility = if field.is_public { |
| 1549 | quote! { public: } |
| 1550 | } else { |
| 1551 | quote! { private: } |
Lukasz Anforowicz | 672c515 | 2023-03-15 16:23:00 -0700 | [diff] [blame] | 1552 | }; |
| 1553 | let cc_type = cc_type.into_tokens(&mut prereqs); |
Lukasz Anforowicz | f4b3760 | 2023-06-01 16:16:34 -0700 | [diff] [blame] | 1554 | let doc_comment = field.doc_comment; |
Lukasz Anforowicz | 68c8882 | 2023-06-01 16:06:15 -0700 | [diff] [blame] | 1555 | quote! { |
Lukasz Anforowicz | 52bc49d | 2023-06-09 03:12:57 -0700 | [diff] [blame] | 1556 | #visibility __NEWLINE__ |
Lukasz Anforowicz | 8250222 | 2023-06-23 09:27:14 -0700 | [diff] [blame] | 1557 | // 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 Anforowicz | 68c8882 | 2023-06-01 16:06:15 -0700 | [diff] [blame] | 1564 | #padding |
| 1565 | } |
Lukasz Anforowicz | 672c515 | 2023-03-15 16:23:00 -0700 | [diff] [blame] | 1566 | } |
Lukasz Anforowicz | 2eb2429 | 2023-03-14 09:38:21 -0700 | [diff] [blame] | 1567 | } |
Lukasz Anforowicz | 672c515 | 2023-03-15 16:23:00 -0700 | [diff] [blame] | 1568 | }) |
| 1569 | .collect(); |
Lukasz Anforowicz | b7145e6 | 2023-03-23 09:00:37 -0700 | [diff] [blame] | 1570 | |
| 1571 | CcSnippet { |
| 1572 | prereqs, |
| 1573 | tokens: quote! { |
Lukasz Anforowicz | 68c8882 | 2023-06-01 16:06:15 -0700 | [diff] [blame] | 1574 | #fields |
| 1575 | #assertions_method_decl |
Lukasz Anforowicz | b7145e6 | 2023-03-23 09:00:37 -0700 | [diff] [blame] | 1576 | }, |
| 1577 | } |
| 1578 | }; |
| 1579 | |
| 1580 | ApiSnippets { main_api, cc_details, rs_details } |
| 1581 | } |
| 1582 | |
Lukasz Anforowicz | 15a6002 | 2023-07-07 09:05:49 -0700 | [diff] [blame] | 1583 | fn does_type_implement_trait<'tcx>(tcx: TyCtxt<'tcx>, self_ty: Ty<'tcx>, trait_id: DefId) -> bool { |
Lukasz Anforowicz | 92b6605 | 2023-07-06 09:26:54 -0700 | [diff] [blame] | 1584 | 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 Anforowicz | 92b6605 | 2023-07-06 09:26:54 -0700 | [diff] [blame] | 1593 | 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 Anforowicz | 36de572 | 2023-06-16 08:17:44 -0700 | [diff] [blame] | 1601 | struct TraitThunks { |
| 1602 | method_name_to_cc_thunk_name: HashMap<Symbol, TokenStream>, |
| 1603 | cc_thunk_decls: CcSnippet, |
| 1604 | rs_thunk_impls: TokenStream, |
| 1605 | } |
| 1606 | |
Lukasz Anforowicz | 15a6002 | 2023-07-07 09:05:49 -0700 | [diff] [blame] | 1607 | fn format_trait_thunks<'tcx>( |
| 1608 | input: &Input<'tcx>, |
Lukasz Anforowicz | 36de572 | 2023-06-16 08:17:44 -0700 | [diff] [blame] | 1609 | trait_id: DefId, |
Lukasz Anforowicz | 15a6002 | 2023-07-07 09:05:49 -0700 | [diff] [blame] | 1610 | adt: &AdtCoreBindings<'tcx>, |
Lukasz Anforowicz | 36de572 | 2023-06-16 08:17:44 -0700 | [diff] [blame] | 1611 | ) -> Result<TraitThunks> { |
| 1612 | let tcx = input.tcx; |
Lukasz Anforowicz | 92b6605 | 2023-07-06 09:26:54 -0700 | [diff] [blame] | 1613 | assert!(tcx.is_trait(trait_id)); |
| 1614 | |
Lukasz Anforowicz | 15a6002 | 2023-07-07 09:05:49 -0700 | [diff] [blame] | 1615 | let self_ty = adt.self_ty; |
| 1616 | if !does_type_implement_trait(tcx, self_ty, trait_id) { |
Lukasz Anforowicz | 36de572 | 2023-06-16 08:17:44 -0700 | [diff] [blame] | 1617 | let trait_name = tcx.item_name(trait_id); |
| 1618 | bail!("`{self_ty}` doesn't implement the `{trait_name}` trait"); |
Lukasz Anforowicz | 9924c43 | 2023-04-04 12:51:22 -0700 | [diff] [blame] | 1619 | } |
Lukasz Anforowicz | 36de572 | 2023-06-16 08:17:44 -0700 | [diff] [blame] | 1620 | |
| 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 Anforowicz | 9924c43 | 2023-04-04 12:51:22 -0700 | [diff] [blame] | 1670 | } |
Lukasz Anforowicz | 36de572 | 2023-06-16 08:17:44 -0700 | [diff] [blame] | 1671 | |
| 1672 | Ok(TraitThunks { method_name_to_cc_thunk_name, cc_thunk_decls, rs_thunk_impls }) |
Lukasz Anforowicz | 9924c43 | 2023-04-04 12:51:22 -0700 | [diff] [blame] | 1673 | } |
| 1674 | |
Lukasz Anforowicz | 10c52f2 | 2023-07-06 09:34:13 -0700 | [diff] [blame] | 1675 | /// Gets the `DefId` for the `Default` trait. |
| 1676 | fn 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 Anforowicz | 9924c43 | 2023-04-04 12:51:22 -0700 | [diff] [blame] | 1680 | /// 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 Anforowicz | 15a6002 | 2023-07-07 09:05:49 -0700 | [diff] [blame] | 1683 | fn format_default_ctor<'tcx>( |
| 1684 | input: &Input<'tcx>, |
| 1685 | core: &AdtCoreBindings<'tcx>, |
| 1686 | ) -> Result<ApiSnippets> { |
Lukasz Anforowicz | 9924c43 | 2023-04-04 12:51:22 -0700 | [diff] [blame] | 1687 | let tcx = input.tcx; |
Lukasz Anforowicz | 10c52f2 | 2023-07-06 09:34:13 -0700 | [diff] [blame] | 1688 | let trait_id = get_def_id_of_default_trait(tcx); |
Lukasz Anforowicz | 36de572 | 2023-06-16 08:17:44 -0700 | [diff] [blame] | 1689 | 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 Anforowicz | 9924c43 | 2023-04-04 12:51:22 -0700 | [diff] [blame] | 1691 | |
Lukasz Anforowicz | 9924c43 | 2023-04-04 12:51:22 -0700 | [diff] [blame] | 1692 | let cc_struct_name = &core.cc_short_name; |
| 1693 | let main_api = CcSnippet::new(quote! { |
| 1694 | __NEWLINE__ __COMMENT__ "Default::default" |
Lukasz Anforowicz | b9e515a | 2023-07-05 12:48:22 -0700 | [diff] [blame] | 1695 | #cc_struct_name(); __NEWLINE__ __NEWLINE__ |
Lukasz Anforowicz | 9924c43 | 2023-04-04 12:51:22 -0700 | [diff] [blame] | 1696 | }); |
Lukasz Anforowicz | 9924c43 | 2023-04-04 12:51:22 -0700 | [diff] [blame] | 1697 | let cc_details = { |
Lukasz Anforowicz | 36de572 | 2023-06-16 08:17:44 -0700 | [diff] [blame] | 1698 | 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 Anforowicz | 9924c43 | 2023-04-04 12:51:22 -0700 | [diff] [blame] | 1706 | let tokens = quote! { |
Lukasz Anforowicz | 36de572 | 2023-06-16 08:17:44 -0700 | [diff] [blame] | 1707 | #cc_thunk_decls |
Lukasz Anforowicz | b9e515a | 2023-07-05 12:48:22 -0700 | [diff] [blame] | 1708 | inline #cc_struct_name::#cc_struct_name() { |
Lukasz Anforowicz | 9924c43 | 2023-04-04 12:51:22 -0700 | [diff] [blame] | 1709 | __crubit_internal::#thunk_name(this); |
| 1710 | } |
| 1711 | }; |
| 1712 | CcSnippet { tokens, prereqs } |
| 1713 | }; |
Lukasz Anforowicz | 9924c43 | 2023-04-04 12:51:22 -0700 | [diff] [blame] | 1714 | Ok(ApiSnippets { main_api, cc_details, rs_details }) |
| 1715 | } |
| 1716 | |
Lukasz Anforowicz | 901161a | 2023-06-01 15:39:58 -0700 | [diff] [blame] | 1717 | /// 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 Anforowicz | 15a6002 | 2023-07-07 09:05:49 -0700 | [diff] [blame] | 1720 | fn format_copy_ctor_and_assignment_operator<'tcx>( |
| 1721 | input: &Input<'tcx>, |
| 1722 | core: &AdtCoreBindings<'tcx>, |
Lukasz Anforowicz | 901161a | 2023-06-01 15:39:58 -0700 | [diff] [blame] | 1723 | ) -> Result<ApiSnippets> { |
| 1724 | let tcx = input.tcx; |
Lukasz Anforowicz | 1b665a4 | 2023-06-20 13:04:54 -0700 | [diff] [blame] | 1725 | let cc_struct_name = &core.cc_short_name; |
Lukasz Anforowicz | 901161a | 2023-06-01 15:39:58 -0700 | [diff] [blame] | 1726 | |
| 1727 | let is_copy = { |
Lukasz Anforowicz | 901161a | 2023-06-01 15:39:58 -0700 | [diff] [blame] | 1728 | // 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 Anforowicz | 15a6002 | 2023-07-07 09:05:49 -0700 | [diff] [blame] | 1731 | core.self_ty.is_copy_modulo_regions(tcx, tcx.param_env(core.def_id)) |
Lukasz Anforowicz | 901161a | 2023-06-01 15:39:58 -0700 | [diff] [blame] | 1732 | }; |
Lukasz Anforowicz | 901161a | 2023-06-01 15:39:58 -0700 | [diff] [blame] | 1733 | if is_copy { |
Lukasz Anforowicz | 901161a | 2023-06-01 15:39:58 -0700 | [diff] [blame] | 1734 | 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 Anforowicz | f18a857 | 2023-06-06 08:42:14 -0700 | [diff] [blame] | 1741 | 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 Anforowicz | 901161a | 2023-06-01 15:39:58 -0700 | [diff] [blame] | 1750 | } |
| 1751 | |
Lukasz Anforowicz | 36de572 | 2023-06-16 08:17:44 -0700 | [diff] [blame] | 1752 | let trait_id = |
| 1753 | tcx.lang_items().clone_trait().ok_or_else(|| anyhow!("Can't find the `Clone` trait"))?; |
Lukasz Anforowicz | 1b665a4 | 2023-06-20 13:04:54 -0700 | [diff] [blame] | 1754 | 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 Anforowicz | b9e515a | 2023-07-05 12:48:22 -0700 | [diff] [blame] | 1758 | #cc_struct_name(const #cc_struct_name&); __NEWLINE__ |
Lukasz Anforowicz | 1b665a4 | 2023-06-20 13:04:54 -0700 | [diff] [blame] | 1759 | __NEWLINE__ __COMMENT__ "Clone::clone_from" |
Lukasz Anforowicz | b9e515a | 2023-07-05 12:48:22 -0700 | [diff] [blame] | 1760 | #cc_struct_name& operator=(const #cc_struct_name&); __NEWLINE__ __NEWLINE__ |
Lukasz Anforowicz | 1b665a4 | 2023-06-20 13:04:54 -0700 | [diff] [blame] | 1761 | }); |
| 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 Anforowicz | b9e515a | 2023-07-05 12:48:22 -0700 | [diff] [blame] | 1772 | inline #cc_struct_name::#cc_struct_name(const #cc_struct_name& other) { |
Lukasz Anforowicz | 1b665a4 | 2023-06-20 13:04:54 -0700 | [diff] [blame] | 1773 | __crubit_internal::#clone_thunk_name(other, this); |
| 1774 | } |
Lukasz Anforowicz | b9e515a | 2023-07-05 12:48:22 -0700 | [diff] [blame] | 1775 | inline #cc_struct_name& #cc_struct_name::operator=(const #cc_struct_name& other) { |
Lukasz Anforowicz | 1b665a4 | 2023-06-20 13:04:54 -0700 | [diff] [blame] | 1776 | 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 Anforowicz | 901161a | 2023-06-01 15:39:58 -0700 | [diff] [blame] | 1787 | } |
| 1788 | |
Lukasz Anforowicz | b7145e6 | 2023-03-23 09:00:37 -0700 | [diff] [blame] | 1789 | /// 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 Anforowicz | 15a6002 | 2023-07-07 09:05:49 -0700 | [diff] [blame] | 1793 | fn format_adt<'tcx>(input: &Input<'tcx>, core: &AdtCoreBindings<'tcx>) -> ApiSnippets { |
Lukasz Anforowicz | b7145e6 | 2023-03-23 09:00:37 -0700 | [diff] [blame] | 1794 | let tcx = input.tcx; |
Lukasz Anforowicz | 9924c43 | 2023-04-04 12:51:22 -0700 | [diff] [blame] | 1795 | let adt_cc_name = &core.cc_short_name; |
Lukasz Anforowicz | b7145e6 | 2023-03-23 09:00:37 -0700 | [diff] [blame] | 1796 | |
| 1797 | // `format_adt` should only be called for local ADTs. |
| 1798 | let local_def_id = core.def_id.expect_local(); |
| 1799 | |
Lukasz Anforowicz | bcb4000 | 2023-07-07 08:39:19 -0700 | [diff] [blame] | 1800 | let default_ctor_snippets = format_default_ctor(input, core).unwrap_or_else(|err| { |
Lukasz Anforowicz | 9924c43 | 2023-04-04 12:51:22 -0700 | [diff] [blame] | 1801 | 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 Anforowicz | bcb4000 | 2023-07-07 08:39:19 -0700 | [diff] [blame] | 1811 | let destructor_and_move_snippets = { |
Lukasz Anforowicz | 15a6002 | 2023-07-07 09:05:49 -0700 | [diff] [blame] | 1812 | assert!(!core.self_ty.needs_drop(tcx, tcx.param_env(core.def_id))); |
Lukasz Anforowicz | 57c7014 | 2023-07-07 08:35:04 -0700 | [diff] [blame] | 1813 | 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 Anforowicz | bcb4000 | 2023-07-07 08:39:19 -0700 | [diff] [blame] | 1853 | 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 Anforowicz | 901161a | 2023-06-01 15:39:58 -0700 | [diff] [blame] | 1865 | |
Lukasz Anforowicz | bcb4000 | 2023-07-07 08:39:19 -0700 | [diff] [blame] | 1866 | let impl_items_snippets = tcx |
Lukasz Anforowicz | b7145e6 | 2023-03-23 09:00:37 -0700 | [diff] [blame] | 1867 | .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 Anforowicz | bcb4000 | 2023-07-07 08:39:19 -0700 | [diff] [blame] | 1891 | 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 Anforowicz | b7145e6 | 2023-03-23 09:00:37 -0700 | [diff] [blame] | 1910 | let alignment = Literal::u64_unsuffixed(core.alignment_in_bytes); |
| 1911 | let size = Literal::u64_unsuffixed(core.size_in_bytes); |
Lukasz Anforowicz | b7145e6 | 2023-03-23 09:00:37 -0700 | [diff] [blame] | 1912 | let main_api = { |
Devin Jeanpierre | 64ac8ad | 2023-05-30 17:22:55 -0700 | [diff] [blame] | 1913 | 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 Anforowicz | b7145e6 | 2023-03-23 09:00:37 -0700 | [diff] [blame] | 1923 | |
| 1924 | let doc_comment = format_doc_comment(tcx, core.def_id.expect_local()); |
| 1925 | let keyword = &core.keyword; |
Lukasz Anforowicz | b7145e6 | 2023-03-23 09:00:37 -0700 | [diff] [blame] | 1926 | |
| 1927 | let mut prereqs = CcPrerequisites::default(); |
Devin Jeanpierre | 64ac8ad | 2023-05-30 17:22:55 -0700 | [diff] [blame] | 1928 | prereqs.includes.insert(input.support_header("internal/attribute_macros.h")); |
Lukasz Anforowicz | bcb4000 | 2023-07-07 08:39:19 -0700 | [diff] [blame] | 1929 | let public_functions_main_api = public_functions_main_api.into_tokens(&mut prereqs); |
Lukasz Anforowicz | b7145e6 | 2023-03-23 09:00:37 -0700 | [diff] [blame] | 1930 | let fields_main_api = fields_main_api.into_tokens(&mut prereqs); |
Lukasz Anforowicz | 64b04ba | 2023-02-10 17:19:05 -0800 | [diff] [blame] | 1931 | prereqs.fwd_decls.remove(&local_def_id); |
Lukasz Anforowicz | 1422976 | 2023-02-10 15:28:33 -0800 | [diff] [blame] | 1932 | |
| 1933 | CcSnippet { |
| 1934 | prereqs, |
| 1935 | tokens: quote! { |
| 1936 | __NEWLINE__ #doc_comment |
Devin Jeanpierre | 64ac8ad | 2023-05-30 17:22:55 -0700 | [diff] [blame] | 1937 | #keyword #(#attributes)* #adt_cc_name final { |
Lukasz Anforowicz | 52bc49d | 2023-06-09 03:12:57 -0700 | [diff] [blame] | 1938 | public: __NEWLINE__ |
Lukasz Anforowicz | bcb4000 | 2023-07-07 08:39:19 -0700 | [diff] [blame] | 1939 | #public_functions_main_api |
Lukasz Anforowicz | b7145e6 | 2023-03-23 09:00:37 -0700 | [diff] [blame] | 1940 | #fields_main_api |
Lukasz Anforowicz | 1422976 | 2023-02-10 15:28:33 -0800 | [diff] [blame] | 1941 | }; |
Lukasz Anforowicz | a0502fb | 2023-02-13 15:33:18 -0800 | [diff] [blame] | 1942 | __NEWLINE__ |
Lukasz Anforowicz | 1422976 | 2023-02-10 15:28:33 -0800 | [diff] [blame] | 1943 | }, |
| 1944 | } |
Lukasz Anforowicz | 3b57954 | 2023-02-06 11:23:49 -0800 | [diff] [blame] | 1945 | }; |
Lukasz Anforowicz | 73a2c0d | 2023-03-22 14:07:00 -0700 | [diff] [blame] | 1946 | let cc_details = { |
Lukasz Anforowicz | 73a2c0d | 2023-03-22 14:07:00 -0700 | [diff] [blame] | 1947 | let mut prereqs = CcPrerequisites::default(); |
Lukasz Anforowicz | bcb4000 | 2023-07-07 08:39:19 -0700 | [diff] [blame] | 1948 | let public_functions_cc_details = public_functions_cc_details.into_tokens(&mut prereqs); |
Lukasz Anforowicz | b7145e6 | 2023-03-23 09:00:37 -0700 | [diff] [blame] | 1949 | let fields_cc_details = fields_cc_details.into_tokens(&mut prereqs); |
Lukasz Anforowicz | 73a2c0d | 2023-03-22 14:07:00 -0700 | [diff] [blame] | 1950 | prereqs.defs.insert(local_def_id); |
Lukasz Anforowicz | e0a778c | 2023-06-06 09:00:32 -0700 | [diff] [blame] | 1951 | prereqs.includes.insert(CcInclude::type_traits()); |
Lukasz Anforowicz | 73a2c0d | 2023-03-22 14:07:00 -0700 | [diff] [blame] | 1952 | CcSnippet { |
| 1953 | prereqs, |
| 1954 | tokens: quote! { |
Lukasz Anforowicz | e3eb1cf | 2023-03-14 09:56:00 -0700 | [diff] [blame] | 1955 | __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 Anforowicz | e0a778c | 2023-06-06 09:00:32 -0700 | [diff] [blame] | 1962 | static_assert(std::is_trivially_move_constructible_v<#adt_cc_name>); |
| 1963 | static_assert(std::is_trivially_move_assignable_v<#adt_cc_name>); |
Lukasz Anforowicz | e3eb1cf | 2023-03-14 09:56:00 -0700 | [diff] [blame] | 1964 | __NEWLINE__ |
Lukasz Anforowicz | bcb4000 | 2023-07-07 08:39:19 -0700 | [diff] [blame] | 1965 | #public_functions_cc_details |
Lukasz Anforowicz | b7145e6 | 2023-03-23 09:00:37 -0700 | [diff] [blame] | 1966 | #fields_cc_details |
Lukasz Anforowicz | 73a2c0d | 2023-03-22 14:07:00 -0700 | [diff] [blame] | 1967 | }, |
| 1968 | } |
Lukasz Anforowicz | 3b57954 | 2023-02-06 11:23:49 -0800 | [diff] [blame] | 1969 | }; |
Lukasz Anforowicz | b7145e6 | 2023-03-23 09:00:37 -0700 | [diff] [blame] | 1970 | let rs_details = { |
Lukasz Anforowicz | ce56a80 | 2023-04-04 12:18:19 -0700 | [diff] [blame] | 1971 | let adt_rs_name = &core.rs_fully_qualified_name; |
Lukasz Anforowicz | b7145e6 | 2023-03-23 09:00:37 -0700 | [diff] [blame] | 1972 | quote! { |
| 1973 | const _: () = assert!(::std::mem::size_of::<#adt_rs_name>() == #size); |
| 1974 | const _: () = assert!(::std::mem::align_of::<#adt_rs_name>() == #alignment); |
Lukasz Anforowicz | bcb4000 | 2023-07-07 08:39:19 -0700 | [diff] [blame] | 1975 | #public_functions_rs_details |
Lukasz Anforowicz | b7145e6 | 2023-03-23 09:00:37 -0700 | [diff] [blame] | 1976 | #fields_rs_details |
| 1977 | } |
Lukasz Anforowicz | 73a2c0d | 2023-03-22 14:07:00 -0700 | [diff] [blame] | 1978 | }; |
| 1979 | ApiSnippets { main_api, cc_details, rs_details } |
Lukasz Anforowicz | 7589483 | 2022-11-22 18:25:28 -0800 | [diff] [blame] | 1980 | } |
| 1981 | |
Lukasz Anforowicz | f4501b4 | 2023-01-07 09:41:29 -0800 | [diff] [blame] | 1982 | /// 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`. |
| 1988 | fn 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 Anforowicz | ce56a80 | 2023-04-04 12:18:19 -0700 | [diff] [blame] | 1994 | let AdtCoreBindings { keyword, cc_short_name, .. } = format_adt_core(tcx, def_id) |
Lukasz Anforowicz | f4501b4 | 2023-01-07 09:41:29 -0800 | [diff] [blame] | 1995 | .expect("`format_fwd_decl` should only be called if `format_adt_core` succeeded"); |
| 1996 | |
Lukasz Anforowicz | ce56a80 | 2023-04-04 12:18:19 -0700 | [diff] [blame] | 1997 | quote! { #keyword #cc_short_name; } |
Lukasz Anforowicz | f4501b4 | 2023-01-07 09:41:29 -0800 | [diff] [blame] | 1998 | } |
| 1999 | |
Googler | 47fd957 | 2023-01-20 09:57:32 -0800 | [diff] [blame] | 2000 | fn 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 Anforowicz | 1f23391 | 2023-02-14 08:50:26 -0800 | [diff] [blame] | 2012 | // brackets, thus don't prepend "google3/" prefix. |
| 2013 | if file.name.is_real() { "google3/" } else { "" } |
Googler | 47fd957 | 2023-01-20 09:57:32 -0800 | [diff] [blame] | 2014 | }; |
Googler | 785e6b4 | 2023-01-23 12:11:36 -0800 | [diff] [blame] | 2015 | format!("{google3_prefix}{file_name};l={line_number}") |
Googler | 47fd957 | 2023-01-20 09:57:32 -0800 | [diff] [blame] | 2016 | } |
| 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. |
Googler | fb20427 | 2022-12-02 00:52:05 -0800 | [diff] [blame] | 2021 | fn format_doc_comment(tcx: TyCtxt, local_def_id: LocalDefId) -> TokenStream { |
| 2022 | let hir_id = tcx.local_def_id_to_hir_id(local_def_id); |
Googler | 47fd957 | 2023-01-20 09:57:32 -0800 | [diff] [blame] | 2023 | let doc_comment = tcx |
Googler | fb20427 | 2022-12-02 00:52:05 -0800 | [diff] [blame] | 2024 | .hir() |
| 2025 | .attrs(hir_id) |
| 2026 | .iter() |
Lukasz Anforowicz | df363ee | 2022-12-16 14:56:38 -0800 | [diff] [blame] | 2027 | .filter_map(|attr| attr.doc_str()) |
Googler | 47fd957 | 2023-01-20 09:57:32 -0800 | [diff] [blame] | 2028 | .map(|symbol| symbol.to_string()) |
Googler | 785e6b4 | 2023-01-23 12:11:36 -0800 | [diff] [blame] | 2029 | .chain(once(format!("Generated from: {}", format_source_location(tcx, local_def_id)))) |
Googler | 34f3d57 | 2022-12-02 00:53:37 -0800 | [diff] [blame] | 2030 | .join("\n\n"); |
Googler | 47fd957 | 2023-01-20 09:57:32 -0800 | [diff] [blame] | 2031 | quote! { __COMMENT__ #doc_comment} |
Googler | fb20427 | 2022-12-02 00:52:05 -0800 | [diff] [blame] | 2032 | } |
| 2033 | |
Lukasz Anforowicz | e465b9e | 2023-02-06 15:00:25 -0800 | [diff] [blame] | 2034 | /// Formats a HIR item idenfied by `def_id`. Returns `None` if the item |
Lukasz Anforowicz | 1422976 | 2023-02-10 15:28:33 -0800 | [diff] [blame] | 2035 | /// can be ignored. Returns an `Err` if the definition couldn't be formatted. |
Lukasz Anforowicz | e433306 | 2022-10-17 14:47:53 -0700 | [diff] [blame] | 2036 | /// |
Lukasz Anforowicz | e465b9e | 2023-02-06 15:00:25 -0800 | [diff] [blame] | 2037 | /// Will panic if `def_id` is invalid (i.e. doesn't identify a HIR item). |
Lukasz Anforowicz | 73a2c0d | 2023-03-22 14:07:00 -0700 | [diff] [blame] | 2038 | fn format_item(input: &Input, def_id: LocalDefId) -> Result<Option<ApiSnippets>> { |
Lukasz Anforowicz | 1382f39 | 2022-12-12 17:13:23 -0800 | [diff] [blame] | 2039 | // TODO(b/262052635): When adding support for re-exports we may need to change |
Devin Jeanpierre | 81aec50 | 2023-04-04 15:33:39 -0700 | [diff] [blame] | 2040 | // `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 Anforowicz | 2eb2429 | 2023-03-14 09:38:21 -0700 | [diff] [blame] | 2045 | if !input.tcx.effective_visibilities(()).is_directly_public(def_id) { |
Lukasz Anforowicz | 73a2c0d | 2023-03-22 14:07:00 -0700 | [diff] [blame] | 2046 | return Ok(None); |
Lukasz Anforowicz | 8b68a55 | 2022-12-12 15:07:58 -0800 | [diff] [blame] | 2047 | } |
| 2048 | |
Lukasz Anforowicz | e465b9e | 2023-02-06 15:00:25 -0800 | [diff] [blame] | 2049 | match input.tcx.hir().expect_item(def_id) { |
Lukasz Anforowicz | 56d94fc | 2023-02-17 12:23:20 -0800 | [diff] [blame] | 2050 | Item { kind: ItemKind::Struct(_, generics) | |
Lukasz Anforowicz | 93513ec | 2023-02-10 14:21:20 -0800 | [diff] [blame] | 2051 | ItemKind::Enum(_, generics) | |
| 2052 | ItemKind::Union(_, generics), |
| 2053 | .. } if !generics.params.is_empty() => { |
Lukasz Anforowicz | 56d94fc | 2023-02-17 12:23:20 -0800 | [diff] [blame] | 2054 | bail!("Generic types are not supported yet (b/259749095)"); |
Lukasz Anforowicz | 93513ec | 2023-02-10 14:21:20 -0800 | [diff] [blame] | 2055 | }, |
Lukasz Anforowicz | 73a2c0d | 2023-03-22 14:07:00 -0700 | [diff] [blame] | 2056 | Item { kind: ItemKind::Fn(..), .. } => format_fn(input, def_id).map(Some), |
Lukasz Anforowicz | 93513ec | 2023-02-10 14:21:20 -0800 | [diff] [blame] | 2057 | Item { kind: ItemKind::Struct(..) | ItemKind::Enum(..) | ItemKind::Union(..), .. } => |
Lukasz Anforowicz | 2eb2429 | 2023-03-14 09:38:21 -0700 | [diff] [blame] | 2058 | format_adt_core(input.tcx, def_id.to_def_id()) |
Lukasz Anforowicz | 73a2c0d | 2023-03-22 14:07:00 -0700 | [diff] [blame] | 2059 | .map(|core| Some(format_adt(input, &core))), |
Lukasz Anforowicz | 93513ec | 2023-02-10 14:21:20 -0800 | [diff] [blame] | 2060 | Item { kind: ItemKind::Impl(_), .. } | // Handled by `format_adt` |
| 2061 | Item { kind: ItemKind::Mod(_), .. } => // Handled by `format_crate` |
Lukasz Anforowicz | 73a2c0d | 2023-03-22 14:07:00 -0700 | [diff] [blame] | 2062 | Ok(None), |
Lukasz Anforowicz | e465b9e | 2023-02-06 15:00:25 -0800 | [diff] [blame] | 2063 | Item { kind, .. } => bail!("Unsupported rustc_hir::hir::ItemKind: {}", kind.descr()), |
Lukasz Anforowicz | e433306 | 2022-10-17 14:47:53 -0700 | [diff] [blame] | 2064 | } |
| 2065 | } |
| 2066 | |
| 2067 | /// Formats a C++ comment explaining why no bindings have been generated for |
| 2068 | /// `local_def_id`. |
Lukasz Anforowicz | ed1c4f0 | 2022-09-29 11:11:20 -0700 | [diff] [blame] | 2069 | fn format_unsupported_def( |
| 2070 | tcx: TyCtxt, |
| 2071 | local_def_id: LocalDefId, |
Lukasz Anforowicz | e433306 | 2022-10-17 14:47:53 -0700 | [diff] [blame] | 2072 | err: anyhow::Error, |
Lukasz Anforowicz | 73a2c0d | 2023-03-22 14:07:00 -0700 | [diff] [blame] | 2073 | ) -> ApiSnippets { |
Googler | 785e6b4 | 2023-01-23 12:11:36 -0800 | [diff] [blame] | 2074 | let source_loc = format_source_location(tcx, local_def_id); |
Lukasz Anforowicz | ed1c4f0 | 2022-09-29 11:11:20 -0700 | [diff] [blame] | 2075 | let name = tcx.def_path_str(local_def_id.to_def_id()); |
Lukasz Anforowicz | e433306 | 2022-10-17 14:47:53 -0700 | [diff] [blame] | 2076 | |
| 2077 | // https://docs.rs/anyhow/latest/anyhow/struct.Error.html#display-representations |
| 2078 | // says: To print causes as well [...], use the alternate selector “{:#}”. |
Googler | 785e6b4 | 2023-01-23 12:11:36 -0800 | [diff] [blame] | 2079 | let msg = format!("Error generating bindings for `{name}` defined at {source_loc}: {err:#}"); |
Lukasz Anforowicz | 73a2c0d | 2023-03-22 14:07:00 -0700 | [diff] [blame] | 2080 | let main_api = CcSnippet::new(quote! { __NEWLINE__ __NEWLINE__ __COMMENT__ #msg __NEWLINE__ }); |
Lukasz Anforowicz | f04bc52 | 2022-11-04 09:19:51 -0700 | [diff] [blame] | 2081 | |
Lukasz Anforowicz | 73a2c0d | 2023-03-22 14:07:00 -0700 | [diff] [blame] | 2082 | ApiSnippets { main_api, cc_details: CcSnippet::default(), rs_details: quote! {} } |
Lukasz Anforowicz | ed1c4f0 | 2022-09-29 11:11:20 -0700 | [diff] [blame] | 2083 | } |
| 2084 | |
Lukasz Anforowicz | 2ec1312 | 2022-11-10 12:39:04 -0800 | [diff] [blame] | 2085 | /// Formats all public items from the Rust crate being compiled. |
Lukasz Anforowicz | be25c76 | 2023-01-17 12:43:52 -0800 | [diff] [blame] | 2086 | fn format_crate(input: &Input) -> Result<Output> { |
| 2087 | let tcx = input.tcx; |
Lukasz Anforowicz | 73a2c0d | 2023-03-22 14:07:00 -0700 | [diff] [blame] | 2088 | let mut cc_details_prereqs = CcPrerequisites::default(); |
| 2089 | let mut cc_details: Vec<(LocalDefId, TokenStream)> = vec![]; |
| 2090 | let mut rs_body = TokenStream::default(); |
Lukasz Anforowicz | 991b2a5 | 2023-03-23 07:28:41 -0700 | [diff] [blame] | 2091 | let mut main_apis = HashMap::<LocalDefId, CcSnippet>::new(); |
| 2092 | let formatted_items = tcx |
Lukasz Anforowicz | 2ec1312 | 2022-11-10 12:39:04 -0800 | [diff] [blame] | 2093 | .hir() |
| 2094 | .items() |
Lukasz Anforowicz | 73a2c0d | 2023-03-22 14:07:00 -0700 | [diff] [blame] | 2095 | .filter_map(|item_id| { |
Lukasz Anforowicz | 2ec1312 | 2022-11-10 12:39:04 -0800 | [diff] [blame] | 2096 | let def_id: LocalDefId = item_id.owner_id.def_id; |
Lukasz Anforowicz | e465b9e | 2023-02-06 15:00:25 -0800 | [diff] [blame] | 2097 | format_item(input, def_id) |
Lukasz Anforowicz | 73a2c0d | 2023-03-22 14:07:00 -0700 | [diff] [blame] | 2098 | .unwrap_or_else(|err| Some(format_unsupported_def(tcx, def_id, err))) |
| 2099 | .map(|api_snippets| (def_id, api_snippets)) |
Lukasz Anforowicz | ed17d05 | 2022-11-02 12:07:28 -0700 | [diff] [blame] | 2100 | }) |
Lukasz Anforowicz | 991b2a5 | 2023-03-23 07:28:41 -0700 | [diff] [blame] | 2101 | .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 Anforowicz | 73a2c0d | 2023-03-22 14:07:00 -0700 | [diff] [blame] | 2105 | |
Lukasz Anforowicz | 991b2a5 | 2023-03-23 07:28:41 -0700 | [diff] [blame] | 2106 | // `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 Anforowicz | 816cbaa | 2022-12-07 09:31:30 -0800 | [diff] [blame] | 2113 | |
Lukasz Anforowicz | 73a2c0d | 2023-03-22 14:07:00 -0700 | [diff] [blame] | 2114 | // Find the order of `main_apis` that 1) meets the requirements of |
Lukasz Anforowicz | 816cbaa | 2022-12-07 09:31:30 -0800 | [diff] [blame] | 2115 | // `CcPrerequisites::defs` and 2) makes a best effort attempt to keep the |
Lukasz Anforowicz | 73a2c0d | 2023-03-22 14:07:00 -0700 | [diff] [blame] | 2116 | // `main_apis` in the same order as the source order of the Rust APIs. |
Lukasz Anforowicz | 2ecb27f | 2023-01-12 15:29:51 -0800 | [diff] [blame] | 2117 | let ordered_ids = { |
| 2118 | let toposort::TopoSortResult { ordered: ordered_ids, failed: failed_ids } = { |
Lukasz Anforowicz | 73a2c0d | 2023-03-22 14:07:00 -0700 | [diff] [blame] | 2119 | let nodes = main_apis.keys().copied(); |
| 2120 | let deps = main_apis.iter().flat_map(|(&successor, main_api)| { |
Lukasz Anforowicz | e721208 | 2023-07-05 13:50:10 -0700 | [diff] [blame] | 2121 | let predecessors = main_api.prereqs.defs.iter().copied(); |
Lukasz Anforowicz | 2ecb27f | 2023-01-12 15:29:51 -0800 | [diff] [blame] | 2122 | predecessors.map(move |predecessor| toposort::Dependency { predecessor, successor }) |
| 2123 | }); |
Lukasz Anforowicz | 73a2c0d | 2023-03-22 14:07:00 -0700 | [diff] [blame] | 2124 | toposort::toposort(nodes, deps, move |lhs_id, rhs_id| { |
| 2125 | tcx.def_span(*lhs_id).cmp(&tcx.def_span(*rhs_id)) |
| 2126 | }) |
Lukasz Anforowicz | 2ecb27f | 2023-01-12 15:29:51 -0800 | [diff] [blame] | 2127 | }; |
| 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 Anforowicz | ab563af | 2022-12-15 08:09:50 -0800 | [diff] [blame] | 2136 | }; |
Lukasz Anforowicz | 816cbaa | 2022-12-07 09:31:30 -0800 | [diff] [blame] | 2137 | |
Lukasz Anforowicz | 73a2c0d | 2023-03-22 14:07:00 -0700 | [diff] [blame] | 2138 | // 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 Anforowicz | f4501b4 | 2023-01-07 09:41:29 -0800 | [diff] [blame] | 2141 | let mut already_declared = HashSet::new(); |
| 2142 | let mut fwd_decls = HashSet::new(); |
Lukasz Anforowicz | 73a2c0d | 2023-03-22 14:07:00 -0700 | [diff] [blame] | 2143 | 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 Anforowicz | 7f31f80 | 2022-12-16 08:24:13 -0800 | [diff] [blame] | 2152 | } |
Lukasz Anforowicz | 73a2c0d | 2023-03-22 14:07:00 -0700 | [diff] [blame] | 2153 | } = main_apis.remove(&def_id).unwrap(); |
Lukasz Anforowicz | f4501b4 | 2023-01-07 09:41:29 -0800 | [diff] [blame] | 2154 | |
| 2155 | fwd_decls.extend(inner_fwd_decls.difference(&already_declared).copied()); |
Lukasz Anforowicz | 73a2c0d | 2023-03-22 14:07:00 -0700 | [diff] [blame] | 2156 | already_declared.insert(def_id); |
Lukasz Anforowicz | f4501b4 | 2023-01-07 09:41:29 -0800 | [diff] [blame] | 2157 | already_declared.extend(inner_fwd_decls.into_iter()); |
| 2158 | |
| 2159 | includes.append(&mut inner_includes); |
Lukasz Anforowicz | 73a2c0d | 2023-03-22 14:07:00 -0700 | [diff] [blame] | 2160 | ordered_main_apis.push((def_id, cc_tokens)); |
Lukasz Anforowicz | f4501b4 | 2023-01-07 09:41:29 -0800 | [diff] [blame] | 2161 | } |
| 2162 | |
Lukasz Anforowicz | f4501b4 | 2023-01-07 09:41:29 -0800 | [diff] [blame] | 2163 | let fwd_decls = fwd_decls |
| 2164 | .into_iter() |
| 2165 | .sorted_by_key(|def_id| tcx.def_span(*def_id)) |
Lukasz Anforowicz | 5c1b3ad | 2023-04-13 17:05:00 -0700 | [diff] [blame] | 2166 | .map(|local_def_id| (local_def_id, format_fwd_decl(tcx, local_def_id))); |
Lukasz Anforowicz | 73a2c0d | 2023-03-22 14:07:00 -0700 | [diff] [blame] | 2167 | |
| 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 Anforowicz | f4501b4 | 2023-01-07 09:41:29 -0800 | [diff] [blame] | 2173 | let mod_path = FullyQualifiedName::new(tcx, local_def_id.to_def_id()).mod_path; |
Lukasz Anforowicz | 73a2c0d | 2023-03-22 14:07:00 -0700 | [diff] [blame] | 2174 | (mod_path, tokens) |
Lukasz Anforowicz | f4501b4 | 2023-01-07 09:41:29 -0800 | [diff] [blame] | 2175 | }) |
| 2176 | .collect_vec(); |
Lukasz Anforowicz | f4501b4 | 2023-01-07 09:41:29 -0800 | [diff] [blame] | 2177 | |
Lukasz Anforowicz | 73a2c0d | 2023-03-22 14:07:00 -0700 | [diff] [blame] | 2178 | (includes, ordered_cc) |
Lukasz Anforowicz | f4501b4 | 2023-01-07 09:41:29 -0800 | [diff] [blame] | 2179 | }; |
Lukasz Anforowicz | a577d82 | 2022-12-12 15:00:46 -0800 | [diff] [blame] | 2180 | |
| 2181 | // Generate top-level elements of the C++ header file. |
Lukasz Anforowicz | bad9963 | 2022-11-18 16:39:13 -0800 | [diff] [blame] | 2182 | let h_body = { |
Lukasz Anforowicz | f04bc52 | 2022-11-04 09:19:51 -0700 | [diff] [blame] | 2183 | // 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 Anforowicz | bad9963 | 2022-11-18 16:39:13 -0800 | [diff] [blame] | 2187 | |
Lukasz Anforowicz | 54efc16 | 2022-12-16 15:58:44 -0800 | [diff] [blame] | 2188 | let includes = format_cc_includes(&includes); |
Lukasz Anforowicz | e1da537 | 2023-01-03 12:31:14 -0800 | [diff] [blame] | 2189 | let ordered_cc = format_namespace_bound_cc_tokens(ordered_cc); |
Lukasz Anforowicz | f04bc52 | 2022-11-04 09:19:51 -0700 | [diff] [blame] | 2190 | quote! { |
Lukasz Anforowicz | a0502fb | 2023-02-13 15:33:18 -0800 | [diff] [blame] | 2191 | #includes |
| 2192 | __NEWLINE__ __NEWLINE__ |
Lukasz Anforowicz | f04bc52 | 2022-11-04 09:19:51 -0700 | [diff] [blame] | 2193 | namespace #crate_name { |
Lukasz Anforowicz | a0502fb | 2023-02-13 15:33:18 -0800 | [diff] [blame] | 2194 | __NEWLINE__ |
Lukasz Anforowicz | 54efc16 | 2022-12-16 15:58:44 -0800 | [diff] [blame] | 2195 | #ordered_cc |
Lukasz Anforowicz | a0502fb | 2023-02-13 15:33:18 -0800 | [diff] [blame] | 2196 | __NEWLINE__ |
Lukasz Anforowicz | f04bc52 | 2022-11-04 09:19:51 -0700 | [diff] [blame] | 2197 | } |
Lukasz Anforowicz | a0502fb | 2023-02-13 15:33:18 -0800 | [diff] [blame] | 2198 | __NEWLINE__ |
Lukasz Anforowicz | f04bc52 | 2022-11-04 09:19:51 -0700 | [diff] [blame] | 2199 | } |
| 2200 | }; |
Lukasz Anforowicz | 7f31f80 | 2022-12-16 08:24:13 -0800 | [diff] [blame] | 2201 | |
Lukasz Anforowicz | be25c76 | 2023-01-17 12:43:52 -0800 | [diff] [blame] | 2202 | Ok(Output { h_body, rs_body }) |
Lukasz Anforowicz | ed1c4f0 | 2022-09-29 11:11:20 -0700 | [diff] [blame] | 2203 | } |
| 2204 | |
Lukasz Anforowicz | bda1cfe | 2022-09-20 06:25:43 -0700 | [diff] [blame] | 2205 | #[cfg(test)] |
Lukasz Anforowicz | f018e4d | 2022-09-28 07:35:59 -0700 | [diff] [blame] | 2206 | pub mod tests { |
Lukasz Anforowicz | 0b8a02f | 2023-02-10 15:55:40 -0800 | [diff] [blame] | 2207 | use super::*; |
Lukasz Anforowicz | 581fd75 | 2022-09-21 11:30:15 -0700 | [diff] [blame] | 2208 | |
Lukasz Anforowicz | 8a83341 | 2022-10-14 14:44:13 -0700 | [diff] [blame] | 2209 | use anyhow::Result; |
| 2210 | use itertools::Itertools; |
| 2211 | use proc_macro2::TokenStream; |
Lukasz Anforowicz | 2f2510a | 2022-09-29 13:06:44 -0700 | [diff] [blame] | 2212 | use quote::quote; |
Lukasz Anforowicz | 4f9f552 | 2022-10-17 15:03:37 -0700 | [diff] [blame] | 2213 | use rustc_middle::ty::{Ty, TyCtxt}; |
Lukasz Anforowicz | 8a83341 | 2022-10-14 14:44:13 -0700 | [diff] [blame] | 2214 | use rustc_span::def_id::LocalDefId; |
Lukasz Anforowicz | bda1cfe | 2022-09-20 06:25:43 -0700 | [diff] [blame] | 2215 | |
Lukasz Anforowicz | 0bef264 | 2023-01-05 09:20:31 -0800 | [diff] [blame] | 2216 | use crate::run_compiler::tests::run_compiler_for_testing; |
Lukasz Anforowicz | 5227499 | 2023-03-08 12:29:28 -0800 | [diff] [blame] | 2217 | use code_gen_utils::format_cc_includes; |
Lukasz Anforowicz | 2f849cf | 2022-11-17 15:52:39 -0800 | [diff] [blame] | 2218 | use token_stream_matchers::{ |
| 2219 | assert_cc_matches, assert_cc_not_matches, assert_rs_matches, assert_rs_not_matches, |
| 2220 | }; |
Lukasz Anforowicz | 2b38d27 | 2022-09-23 08:08:18 -0700 | [diff] [blame] | 2221 | |
Lukasz Anforowicz | 5bddf18 | 2022-09-30 16:06:59 -0700 | [diff] [blame] | 2222 | #[test] |
Lukasz Anforowicz | 8a83341 | 2022-10-14 14:44:13 -0700 | [diff] [blame] | 2223 | #[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 Anforowicz | 0bef264 | 2023-01-05 09:20:31 -0800 | [diff] [blame] | 2236 | run_compiler_for_testing(test_src, |tcx| find_def_id_by_name(tcx, "missing_name")); |
Lukasz Anforowicz | 8a83341 | 2022-10-14 14:44:13 -0700 | [diff] [blame] | 2237 | } |
| 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 Anforowicz | 0bef264 | 2023-01-05 09:20:31 -0800 | [diff] [blame] | 2250 | run_compiler_for_testing(test_src, |tcx| find_def_id_by_name(tcx, "some_name")); |
Lukasz Anforowicz | 8a83341 | 2022-10-14 14:44:13 -0700 | [diff] [blame] | 2251 | } |
| 2252 | |
Lukasz Anforowicz | 4c19ad9 | 2022-12-16 15:23:14 -0800 | [diff] [blame] | 2253 | /// 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 Anforowicz | e465b9e | 2023-02-06 15:00:25 -0800 | [diff] [blame] | 2257 | /// by `test_format_item_..._fn_...` tests (which work at the `format_fn` |
Lukasz Anforowicz | 4c19ad9 | 2022-12-16 15:23:14 -0800 | [diff] [blame] | 2258 | /// level). |
Lukasz Anforowicz | 8a83341 | 2022-10-14 14:44:13 -0700 | [diff] [blame] | 2259 | #[test] |
Lukasz Anforowicz | 7589483 | 2022-11-22 18:25:28 -0800 | [diff] [blame] | 2260 | fn test_generated_bindings_fn_no_mangle_extern_c() { |
Lukasz Anforowicz | 581fd75 | 2022-09-21 11:30:15 -0700 | [diff] [blame] | 2261 | let test_src = r#" |
Lukasz Anforowicz | 7123f21 | 2022-10-20 08:51:04 -0700 | [diff] [blame] | 2262 | #[no_mangle] |
Lukasz Anforowicz | 2f2510a | 2022-09-29 13:06:44 -0700 | [diff] [blame] | 2263 | pub extern "C" fn public_function() { |
| 2264 | println!("foo"); |
Lukasz Anforowicz | 581fd75 | 2022-09-21 11:30:15 -0700 | [diff] [blame] | 2265 | } |
Lukasz Anforowicz | 581fd75 | 2022-09-21 11:30:15 -0700 | [diff] [blame] | 2266 | "#; |
| 2267 | test_generated_bindings(test_src, |bindings| { |
Lukasz Anforowicz | 4c19ad9 | 2022-12-16 15:23:14 -0800 | [diff] [blame] | 2268 | let bindings = bindings.unwrap(); |
Lukasz Anforowicz | 2f2510a | 2022-09-29 13:06:44 -0700 | [diff] [blame] | 2269 | assert_cc_matches!( |
| 2270 | bindings.h_body, |
| 2271 | quote! { |
Lukasz Anforowicz | e433306 | 2022-10-17 14:47:53 -0700 | [diff] [blame] | 2272 | extern "C" void public_function(); |
Lukasz Anforowicz | 2f2510a | 2022-09-29 13:06:44 -0700 | [diff] [blame] | 2273 | } |
Lukasz Anforowicz | d9ff4ab | 2022-09-23 08:11:18 -0700 | [diff] [blame] | 2274 | ); |
Lukasz Anforowicz | 4c19ad9 | 2022-12-16 15:23:14 -0800 | [diff] [blame] | 2275 | |
| 2276 | // No Rust thunks should be generated in this test scenario. |
Devin Jeanpierre | 81aec50 | 2023-04-04 15:33:39 -0700 | [diff] [blame] | 2277 | assert_rs_not_matches!(bindings.rs_body, quote! { public_function }); |
Lukasz Anforowicz | 2f2510a | 2022-09-29 13:06:44 -0700 | [diff] [blame] | 2278 | }); |
| 2279 | } |
| 2280 | |
Lukasz Anforowicz | 4c19ad9 | 2022-12-16 15:23:14 -0800 | [diff] [blame] | 2281 | /// `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 Anforowicz | 2f2510a | 2022-09-29 13:06:44 -0700 | [diff] [blame] | 2284 | #[test] |
Lukasz Anforowicz | f04bc52 | 2022-11-04 09:19:51 -0700 | [diff] [blame] | 2285 | fn test_generated_bindings_fn_export_name() { |
Lukasz Anforowicz | f04bc52 | 2022-11-04 09:19:51 -0700 | [diff] [blame] | 2286 | 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 Anforowicz | 4c19ad9 | 2022-12-16 15:23:14 -0800 | [diff] [blame] | 2291 | let bindings = bindings.unwrap(); |
Lukasz Anforowicz | f04bc52 | 2022-11-04 09:19:51 -0700 | [diff] [blame] | 2292 | assert_cc_matches!( |
| 2293 | bindings.h_body, |
| 2294 | quote! { |
Lukasz Anforowicz | f04bc52 | 2022-11-04 09:19:51 -0700 | [diff] [blame] | 2295 | namespace rust_out { |
Lukasz Anforowicz | 6753d40 | 2023-02-10 16:41:23 -0800 | [diff] [blame] | 2296 | ... |
Lukasz Anforowicz | b9e515a | 2023-07-05 12:48:22 -0700 | [diff] [blame] | 2297 | double public_function(double x, double y); |
Lukasz Anforowicz | bad9963 | 2022-11-18 16:39:13 -0800 | [diff] [blame] | 2298 | namespace __crubit_internal { |
Lukasz Anforowicz | 73edf15 | 2023-04-04 12:05:00 -0700 | [diff] [blame] | 2299 | extern "C" double export_name(double, double); |
Lukasz Anforowicz | bad9963 | 2022-11-18 16:39:13 -0800 | [diff] [blame] | 2300 | } |
Lukasz Anforowicz | f04bc52 | 2022-11-04 09:19:51 -0700 | [diff] [blame] | 2301 | inline double public_function(double x, double y) { |
Lukasz Anforowicz | bad9963 | 2022-11-18 16:39:13 -0800 | [diff] [blame] | 2302 | return __crubit_internal::export_name(x, y); |
Lukasz Anforowicz | f04bc52 | 2022-11-04 09:19:51 -0700 | [diff] [blame] | 2303 | } |
| 2304 | } |
| 2305 | } |
| 2306 | ); |
| 2307 | }); |
| 2308 | } |
| 2309 | |
Lukasz Anforowicz | 4c19ad9 | 2022-12-16 15:23:14 -0800 | [diff] [blame] | 2310 | /// 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 Anforowicz | e465b9e | 2023-02-06 15:00:25 -0800 | [diff] [blame] | 2313 | /// `test_format_item_..._struct_...`, `test_format_item_..._enum_...`, |
| 2314 | /// and `test_format_item_..._union_...` tests. |
Lukasz Anforowicz | 4c19ad9 | 2022-12-16 15:23:14 -0800 | [diff] [blame] | 2315 | /// |
| 2316 | /// We don't want to duplicate coverage already provided by |
Lukasz Anforowicz | e465b9e | 2023-02-06 15:00:25 -0800 | [diff] [blame] | 2317 | /// `test_format_item_struct_with_fields`, but we do want to verify that |
Lukasz Anforowicz | 4c19ad9 | 2022-12-16 15:23:14 -0800 | [diff] [blame] | 2318 | /// * `format_crate` will actually find and process the struct |
Lukasz Anforowicz | e465b9e | 2023-02-06 15:00:25 -0800 | [diff] [blame] | 2319 | /// (`test_format_item_...` doesn't cover this aspect - it uses a |
| 2320 | /// test-only `find_def_id_by_name` instead) |
Lukasz Anforowicz | 4c19ad9 | 2022-12-16 15:23:14 -0800 | [diff] [blame] | 2321 | /// * The actual shape of the bindings still looks okay at this level. |
Lukasz Anforowicz | f04bc52 | 2022-11-04 09:19:51 -0700 | [diff] [blame] | 2322 | #[test] |
Lukasz Anforowicz | 7589483 | 2022-11-22 18:25:28 -0800 | [diff] [blame] | 2323 | fn test_generated_bindings_struct() { |
Lukasz Anforowicz | 7589483 | 2022-11-22 18:25:28 -0800 | [diff] [blame] | 2324 | 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 Anforowicz | 4c19ad9 | 2022-12-16 15:23:14 -0800 | [diff] [blame] | 2331 | let bindings = bindings.unwrap(); |
Lukasz Anforowicz | 7589483 | 2022-11-22 18:25:28 -0800 | [diff] [blame] | 2332 | assert_cc_matches!( |
| 2333 | bindings.h_body, |
| 2334 | quote! { |
| 2335 | namespace rust_out { |
Googler | 47fd957 | 2023-01-20 09:57:32 -0800 | [diff] [blame] | 2336 | ... |
Devin Jeanpierre | 64ac8ad | 2023-05-30 17:22:55 -0700 | [diff] [blame] | 2337 | struct CRUBIT_INTERNAL_RUST_TYPE(":: rust_out :: Point") alignas(4) Point final { |
Lukasz Anforowicz | 7589483 | 2022-11-22 18:25:28 -0800 | [diff] [blame] | 2338 | // No point replicating test coverage of |
Lukasz Anforowicz | e465b9e | 2023-02-06 15:00:25 -0800 | [diff] [blame] | 2339 | // `test_format_item_struct_with_fields`. |
Lukasz Anforowicz | 7589483 | 2022-11-22 18:25:28 -0800 | [diff] [blame] | 2340 | ... |
| 2341 | }; |
| 2342 | static_assert(sizeof(Point) == 8, ...); |
| 2343 | static_assert(alignof(Point) == 4, ...); |
Lukasz Anforowicz | e3eb1cf | 2023-03-14 09:56:00 -0700 | [diff] [blame] | 2344 | ... // Other static_asserts are covered by |
| 2345 | // `test_format_item_struct_with_fields` |
Lukasz Anforowicz | 7589483 | 2022-11-22 18:25:28 -0800 | [diff] [blame] | 2346 | } // namespace rust_out |
| 2347 | } |
| 2348 | ); |
| 2349 | assert_rs_matches!( |
| 2350 | bindings.rs_body, |
| 2351 | quote! { |
Lukasz Anforowicz | cf60f52 | 2023-03-14 10:03:55 -0700 | [diff] [blame] | 2352 | // No point replicating test coverage of |
| 2353 | // `test_format_item_struct_with_fields`. |
Lukasz Anforowicz | 8b475a4 | 2022-11-29 09:33:02 -0800 | [diff] [blame] | 2354 | const _: () = assert!(::std::mem::size_of::<::rust_out::Point>() == 8); |
| 2355 | const _: () = assert!(::std::mem::align_of::<::rust_out::Point>() == 4); |
Lukasz Anforowicz | cf60f52 | 2023-03-14 10:03:55 -0700 | [diff] [blame] | 2356 | const _: () = assert!( memoffset::offset_of!(::rust_out::Point, x) == 0); |
| 2357 | const _: () = assert!( memoffset::offset_of!(::rust_out::Point, y) == 4); |
Lukasz Anforowicz | 7589483 | 2022-11-22 18:25:28 -0800 | [diff] [blame] | 2358 | } |
| 2359 | ); |
| 2360 | }); |
| 2361 | } |
| 2362 | |
Lukasz Anforowicz | 93513ec | 2023-02-10 14:21:20 -0800 | [diff] [blame] | 2363 | /// 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 Anforowicz | 43f06c6 | 2023-02-10 17:14:19 -0800 | [diff] [blame] | 2366 | /// |
| 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 Anforowicz | 93513ec | 2023-02-10 14:21:20 -0800 | [diff] [blame] | 2371 | #[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 Anforowicz | 43f06c6 | 2023-02-10 17:14:19 -0800 | [diff] [blame] | 2392 | // `test_format_item_static_method`. |
Lukasz Anforowicz | 93513ec | 2023-02-10 14:21:20 -0800 | [diff] [blame] | 2393 | ... |
Lukasz Anforowicz | 43f06c6 | 2023-02-10 17:14:19 -0800 | [diff] [blame] | 2394 | std::int32_t public_static_method(); |
| 2395 | ... |
Lukasz Anforowicz | 93513ec | 2023-02-10 14:21:20 -0800 | [diff] [blame] | 2396 | }; |
| 2397 | ... |
Lukasz Anforowicz | 43f06c6 | 2023-02-10 17:14:19 -0800 | [diff] [blame] | 2398 | std::int32_t SomeStruct::public_static_method() { |
| 2399 | ... |
| 2400 | } |
Lukasz Anforowicz | b7145e6 | 2023-03-23 09:00:37 -0700 | [diff] [blame] | 2401 | ... |
Lukasz Anforowicz | 93513ec | 2023-02-10 14:21:20 -0800 | [diff] [blame] | 2402 | } // namespace rust_out |
| 2403 | } |
| 2404 | ); |
Lukasz Anforowicz | 43f06c6 | 2023-02-10 17:14:19 -0800 | [diff] [blame] | 2405 | 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 Anforowicz | 93513ec | 2023-02-10 14:21:20 -0800 | [diff] [blame] | 2413 | }); |
| 2414 | } |
| 2415 | |
Lukasz Anforowicz | 7589483 | 2022-11-22 18:25:28 -0800 | [diff] [blame] | 2416 | #[test] |
Lukasz Anforowicz | f04bc52 | 2022-11-04 09:19:51 -0700 | [diff] [blame] | 2417 | fn test_generated_bindings_includes() { |
Lukasz Anforowicz | ed17d05 | 2022-11-02 12:07:28 -0700 | [diff] [blame] | 2418 | 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 Anforowicz | 4c19ad9 | 2022-12-16 15:23:14 -0800 | [diff] [blame] | 2427 | let bindings = bindings.unwrap(); |
Lukasz Anforowicz | ed17d05 | 2022-11-02 12:07:28 -0700 | [diff] [blame] | 2428 | assert_cc_matches!( |
| 2429 | bindings.h_body, |
| 2430 | quote! { |
| 2431 | __HASH_TOKEN__ include <cstdint> ... |
| 2432 | namespace ... { |
Googler | 47fd957 | 2023-01-20 09:57:32 -0800 | [diff] [blame] | 2433 | ... |
Lukasz Anforowicz | ed17d05 | 2022-11-02 12:07:28 -0700 | [diff] [blame] | 2434 | 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 Anforowicz | 2eb2429 | 2023-03-14 09:38:21 -0700 | [diff] [blame] | 2444 | /// Tests that `toposort` is used to reorder item bindings. |
Lukasz Anforowicz | ed17d05 | 2022-11-02 12:07:28 -0700 | [diff] [blame] | 2445 | #[test] |
Lukasz Anforowicz | 2eb2429 | 2023-03-14 09:38:21 -0700 | [diff] [blame] | 2446 | fn test_generated_bindings_prereq_defs_field_deps_require_reordering() { |
Lukasz Anforowicz | 816cbaa | 2022-12-07 09:31:30 -0800 | [diff] [blame] | 2447 | let test_src = r#" |
Lukasz Anforowicz | 2eb2429 | 2023-03-14 09:38:21 -0700 | [diff] [blame] | 2448 | // In the generated bindings `Outer` needs to come *after* `Inner`. |
| 2449 | pub struct Outer(Inner); |
| 2450 | pub struct Inner(bool); |
Lukasz Anforowicz | 816cbaa | 2022-12-07 09:31:30 -0800 | [diff] [blame] | 2451 | "#; |
| 2452 | test_generated_bindings(test_src, |bindings| { |
Lukasz Anforowicz | 4c19ad9 | 2022-12-16 15:23:14 -0800 | [diff] [blame] | 2453 | let bindings = bindings.unwrap(); |
Lukasz Anforowicz | 816cbaa | 2022-12-07 09:31:30 -0800 | [diff] [blame] | 2454 | assert_cc_matches!( |
| 2455 | bindings.h_body, |
| 2456 | quote! { |
| 2457 | namespace rust_out { |
Lukasz Anforowicz | 0b8a02f | 2023-02-10 15:55:40 -0800 | [diff] [blame] | 2458 | ... |
Devin Jeanpierre | 64ac8ad | 2023-05-30 17:22:55 -0700 | [diff] [blame] | 2459 | struct CRUBIT_INTERNAL_RUST_TYPE(...) alignas(1) Inner final { |
Lukasz Anforowicz | 8250222 | 2023-06-23 09:27:14 -0700 | [diff] [blame] | 2460 | ... union { ... bool __field0; }; ... |
Lukasz Anforowicz | 2eb2429 | 2023-03-14 09:38:21 -0700 | [diff] [blame] | 2461 | }; |
| 2462 | ... |
Devin Jeanpierre | 64ac8ad | 2023-05-30 17:22:55 -0700 | [diff] [blame] | 2463 | struct CRUBIT_INTERNAL_RUST_TYPE(...) alignas(1) Outer final { |
Lukasz Anforowicz | 8250222 | 2023-06-23 09:27:14 -0700 | [diff] [blame] | 2464 | ... union { ... ::rust_out::Inner __field0; }; ... |
Lukasz Anforowicz | 2eb2429 | 2023-03-14 09:38:21 -0700 | [diff] [blame] | 2465 | }; |
| 2466 | ... |
| 2467 | } // namespace rust_out |
Lukasz Anforowicz | 816cbaa | 2022-12-07 09:31:30 -0800 | [diff] [blame] | 2468 | } |
| 2469 | ); |
| 2470 | }); |
| 2471 | } |
| 2472 | |
Lukasz Anforowicz | f4501b4 | 2023-01-07 09:41:29 -0800 | [diff] [blame] | 2473 | /// Tests that a forward declaration is present when it is required to |
Lukasz Anforowicz | 7a7b81f | 2023-01-12 15:50:46 -0800 | [diff] [blame] | 2474 | /// preserve the original source order. In this test the |
| 2475 | /// `CcPrerequisites::fwd_decls` dependency comes from a pointer parameter. |
Lukasz Anforowicz | f4501b4 | 2023-01-07 09:41:29 -0800 | [diff] [blame] | 2476 | #[test] |
Lukasz Anforowicz | 7a7b81f | 2023-01-12 15:50:46 -0800 | [diff] [blame] | 2477 | fn test_generated_bindings_prereq_fwd_decls_for_ptr_param() { |
Lukasz Anforowicz | f4501b4 | 2023-01-07 09:41:29 -0800 | [diff] [blame] | 2478 | 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 Anforowicz | 6753d40 | 2023-02-10 16:41:23 -0800 | [diff] [blame] | 2491 | // it the essence of this test. The order of the items |
| 2492 | // below also matters. |
Lukasz Anforowicz | f4501b4 | 2023-01-07 09:41:29 -0800 | [diff] [blame] | 2493 | struct S; |
| 2494 | ... |
Lukasz Anforowicz | b9e515a | 2023-07-05 12:48:22 -0700 | [diff] [blame] | 2495 | void f(::rust_out::S const* __param_0); |
Lukasz Anforowicz | f4501b4 | 2023-01-07 09:41:29 -0800 | [diff] [blame] | 2496 | ... |
Devin Jeanpierre | 64ac8ad | 2023-05-30 17:22:55 -0700 | [diff] [blame] | 2497 | struct CRUBIT_INTERNAL_RUST_TYPE(...) alignas(...) S final { ... } |
Lukasz Anforowicz | f4501b4 | 2023-01-07 09:41:29 -0800 | [diff] [blame] | 2498 | ... |
Lukasz Anforowicz | cf78302 | 2023-06-15 10:36:58 -0700 | [diff] [blame] | 2499 | inline void f(::rust_out::S const* __param_0) { ... } |
Lukasz Anforowicz | 6753d40 | 2023-02-10 16:41:23 -0800 | [diff] [blame] | 2500 | ... |
Lukasz Anforowicz | f4501b4 | 2023-01-07 09:41:29 -0800 | [diff] [blame] | 2501 | } // namespace rust_out |
| 2502 | } |
| 2503 | ); |
| 2504 | }); |
| 2505 | } |
| 2506 | |
Lukasz Anforowicz | 7a7b81f | 2023-01-12 15:50:46 -0800 | [diff] [blame] | 2507 | /// Tests that a forward declaration is present when it is required to |
| 2508 | /// preserve the original source order. In this test the |
Lukasz Anforowicz | 10b1a29 | 2023-04-03 16:19:08 -0700 | [diff] [blame] | 2509 | /// `CcPrerequisites::fwd_decls` dependency comes from a |
| 2510 | /// function declaration that has a parameter that takes a struct by value. |
Lukasz Anforowicz | 7a7b81f | 2023-01-12 15:50:46 -0800 | [diff] [blame] | 2511 | #[test] |
| 2512 | fn test_generated_bindings_prereq_fwd_decls_for_cpp_fn_decl() { |
| 2513 | let test_src = r#" |
Lukasz Anforowicz | 7a7b81f | 2023-01-12 15:50:46 -0800 | [diff] [blame] | 2514 | #[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 Anforowicz | 10b1a29 | 2023-04-03 16:19:08 -0700 | [diff] [blame] | 2529 | // is the essence of this test. The order also matters: |
Lukasz Anforowicz | 7a7b81f | 2023-01-12 15:50:46 -0800 | [diff] [blame] | 2530 | // 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 Anforowicz | 10b1a29 | 2023-04-03 16:19:08 -0700 | [diff] [blame] | 2535 | // `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 Anforowicz | b9e515a | 2023-07-05 12:48:22 -0700 | [diff] [blame] | 2537 | bool f(::rust_out::S s); |
Lukasz Anforowicz | 7a7b81f | 2023-01-12 15:50:46 -0800 | [diff] [blame] | 2538 | ... |
Devin Jeanpierre | 64ac8ad | 2023-05-30 17:22:55 -0700 | [diff] [blame] | 2539 | struct CRUBIT_INTERNAL_RUST_TYPE(...) alignas(...) S final { ... } |
Lukasz Anforowicz | 7a7b81f | 2023-01-12 15:50:46 -0800 | [diff] [blame] | 2540 | ... |
| 2541 | } // namespace rust_out |
| 2542 | } |
| 2543 | ); |
| 2544 | }); |
| 2545 | } |
| 2546 | |
Lukasz Anforowicz | f4501b4 | 2023-01-07 09:41:29 -0800 | [diff] [blame] | 2547 | /// 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 Anforowicz | b9e515a | 2023-07-05 12:48:22 -0700 | [diff] [blame] | 2616 | void f1 ... |
| 2617 | void f2 ... |
| 2618 | void f3 ... |
Lukasz Anforowicz | f4501b4 | 2023-01-07 09:41:29 -0800 | [diff] [blame] | 2619 | |
| 2620 | namespace a { ... |
Devin Jeanpierre | 64ac8ad | 2023-05-30 17:22:55 -0700 | [diff] [blame] | 2621 | struct CRUBIT_INTERNAL_RUST_TYPE(...) alignas(...) S1 final { ... } ... |
| 2622 | struct CRUBIT_INTERNAL_RUST_TYPE(...) alignas(...) S2 final { ... } ... |
Lukasz Anforowicz | f4501b4 | 2023-01-07 09:41:29 -0800 | [diff] [blame] | 2623 | } ... |
| 2624 | namespace b { ... |
Devin Jeanpierre | 64ac8ad | 2023-05-30 17:22:55 -0700 | [diff] [blame] | 2625 | struct CRUBIT_INTERNAL_RUST_TYPE(...) alignas(...) S3 final { ... } ... |
Lukasz Anforowicz | f4501b4 | 2023-01-07 09:41:29 -0800 | [diff] [blame] | 2626 | } ... |
| 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 Anforowicz | 64b04ba | 2023-02-10 17:19:05 -0800 | [diff] [blame] | 2639 | fn test_generated_bindings_prereq_fwd_decls_not_needed_because_of_initial_order() { |
Lukasz Anforowicz | f4501b4 | 2023-01-07 09:41:29 -0800 | [diff] [blame] | 2640 | 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 Anforowicz | cf78302 | 2023-06-15 10:36:58 -0700 | [diff] [blame] | 2649 | assert_cc_matches!(bindings.h_body, quote! { void f(::rust_out::S const* _s); }); |
Lukasz Anforowicz | 64b04ba | 2023-02-10 17:19:05 -0800 | [diff] [blame] | 2650 | }); |
| 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 Anforowicz | 2eb2429 | 2023-03-14 09:38:21 -0700 | [diff] [blame] | 2658 | #![allow(dead_code)] |
| 2659 | |
| 2660 | pub struct S { |
| 2661 | // This shouldn't require a fwd decl of S. |
| 2662 | field: *const S, |
| 2663 | } |
Lukasz Anforowicz | 64b04ba | 2023-02-10 17:19:05 -0800 | [diff] [blame] | 2664 | |
| 2665 | impl S { |
| 2666 | // This shouldn't require a fwd decl of S. |
Lukasz Anforowicz | 2eb2429 | 2023-03-14 09:38:21 -0700 | [diff] [blame] | 2667 | pub fn create() -> S { Self{ field: std::ptr::null() } } |
Lukasz Anforowicz | 64b04ba | 2023-02-10 17:19:05 -0800 | [diff] [blame] | 2668 | } |
| 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 Anforowicz | 2eb2429 | 2023-03-14 09:38:21 -0700 | [diff] [blame] | 2673 | assert_cc_matches!( |
| 2674 | bindings.h_body, |
| 2675 | quote! { |
Lukasz Anforowicz | b9e515a | 2023-07-05 12:48:22 -0700 | [diff] [blame] | 2676 | static ::rust_out::S create(); ... |
Lukasz Anforowicz | 8250222 | 2023-06-23 09:27:14 -0700 | [diff] [blame] | 2677 | union { ... ::rust_out::S const* field; }; ... |
Lukasz Anforowicz | 2eb2429 | 2023-03-14 09:38:21 -0700 | [diff] [blame] | 2678 | } |
| 2679 | ); |
Lukasz Anforowicz | f4501b4 | 2023-01-07 09:41:29 -0800 | [diff] [blame] | 2680 | }); |
| 2681 | } |
| 2682 | |
Lukasz Anforowicz | 816cbaa | 2022-12-07 09:31:30 -0800 | [diff] [blame] | 2683 | #[test] |
Lukasz Anforowicz | e1da537 | 2023-01-03 12:31:14 -0800 | [diff] [blame] | 2684 | fn test_generated_bindings_module_basics() { |
Lukasz Anforowicz | a577d82 | 2022-12-12 15:00:46 -0800 | [diff] [blame] | 2685 | let test_src = r#" |
| 2686 | pub mod some_module { |
| 2687 | pub fn some_func() {} |
| 2688 | } |
| 2689 | "#; |
| 2690 | test_generated_bindings(test_src, |bindings| { |
Lukasz Anforowicz | 4c19ad9 | 2022-12-16 15:23:14 -0800 | [diff] [blame] | 2691 | let bindings = bindings.unwrap(); |
Lukasz Anforowicz | a577d82 | 2022-12-12 15:00:46 -0800 | [diff] [blame] | 2692 | assert_cc_matches!( |
| 2693 | bindings.h_body, |
| 2694 | quote! { |
| 2695 | namespace rust_out { |
Lukasz Anforowicz | a577d82 | 2022-12-12 15:00:46 -0800 | [diff] [blame] | 2696 | 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 Anforowicz | e1da537 | 2023-01-03 12:31:14 -0800 | [diff] [blame] | 2717 | #[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 Anforowicz | b9e515a | 2023-07-05 12:48:22 -0700 | [diff] [blame] | 2748 | void working_module_f1(); |
Lukasz Anforowicz | e1da537 | 2023-01-03 12:31:14 -0800 | [diff] [blame] | 2749 | ... |
Lukasz Anforowicz | b9e515a | 2023-07-05 12:48:22 -0700 | [diff] [blame] | 2750 | void working_module_f2(); |
Lukasz Anforowicz | e1da537 | 2023-01-03 12:31:14 -0800 | [diff] [blame] | 2751 | ... |
| 2752 | } // namespace some_module |
| 2753 | |
| 2754 | __COMMENT__ #broken_module_msg |
Lukasz Anforowicz | 6753d40 | 2023-02-10 16:41:23 -0800 | [diff] [blame] | 2755 | ... |
Lukasz Anforowicz | e1da537 | 2023-01-03 12:31:14 -0800 | [diff] [blame] | 2756 | } // namespace rust_out |
| 2757 | } |
| 2758 | ); |
| 2759 | }); |
| 2760 | } |
| 2761 | |
Lukasz Anforowicz | 4c19ad9 | 2022-12-16 15:23:14 -0800 | [diff] [blame] | 2762 | /// `test_generated_bindings_non_pub_items` verifies that non-public items |
| 2763 | /// are not present/propagated into the generated bindings. |
Lukasz Anforowicz | a577d82 | 2022-12-12 15:00:46 -0800 | [diff] [blame] | 2764 | #[test] |
Lukasz Anforowicz | 7589483 | 2022-11-22 18:25:28 -0800 | [diff] [blame] | 2765 | fn test_generated_bindings_non_pub_items() { |
Lukasz Anforowicz | 2f2510a | 2022-09-29 13:06:44 -0700 | [diff] [blame] | 2766 | let test_src = r#" |
Lukasz Anforowicz | 88bde9b | 2022-10-14 14:48:07 -0700 | [diff] [blame] | 2767 | #![allow(dead_code)] |
Lukasz Anforowicz | 7589483 | 2022-11-22 18:25:28 -0800 | [diff] [blame] | 2768 | |
Lukasz Anforowicz | 2f2510a | 2022-09-29 13:06:44 -0700 | [diff] [blame] | 2769 | extern "C" fn private_function() { |
| 2770 | println!("foo"); |
| 2771 | } |
Lukasz Anforowicz | 7589483 | 2022-11-22 18:25:28 -0800 | [diff] [blame] | 2772 | |
| 2773 | struct PrivateStruct { |
| 2774 | x: i32, |
| 2775 | y: i32, |
| 2776 | } |
Lukasz Anforowicz | a577d82 | 2022-12-12 15:00:46 -0800 | [diff] [blame] | 2777 | |
Lukasz Anforowicz | 1422976 | 2023-02-10 15:28:33 -0800 | [diff] [blame] | 2778 | pub struct PublicStruct(i32); |
| 2779 | |
| 2780 | impl PublicStruct { |
| 2781 | fn private_method() {} |
| 2782 | } |
| 2783 | |
Lukasz Anforowicz | a577d82 | 2022-12-12 15:00:46 -0800 | [diff] [blame] | 2784 | 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 Anforowicz | 2f2510a | 2022-09-29 13:06:44 -0700 | [diff] [blame] | 2792 | "#; |
| 2793 | test_generated_bindings(test_src, |bindings| { |
Lukasz Anforowicz | 4c19ad9 | 2022-12-16 15:23:14 -0800 | [diff] [blame] | 2794 | let bindings = bindings.unwrap(); |
Lukasz Anforowicz | 2f2510a | 2022-09-29 13:06:44 -0700 | [diff] [blame] | 2795 | assert_cc_not_matches!(bindings.h_body, quote! { private_function }); |
Lukasz Anforowicz | e7a2500 | 2022-11-10 06:21:42 -0800 | [diff] [blame] | 2796 | assert_rs_not_matches!(bindings.rs_body, quote! { private_function }); |
Lukasz Anforowicz | 7589483 | 2022-11-22 18:25:28 -0800 | [diff] [blame] | 2797 | assert_cc_not_matches!(bindings.h_body, quote! { PrivateStruct }); |
| 2798 | assert_rs_not_matches!(bindings.rs_body, quote! { PrivateStruct }); |
Lukasz Anforowicz | 1422976 | 2023-02-10 15:28:33 -0800 | [diff] [blame] | 2799 | assert_cc_not_matches!(bindings.h_body, quote! { private_method }); |
| 2800 | assert_rs_not_matches!(bindings.rs_body, quote! { private_method }); |
Lukasz Anforowicz | a577d82 | 2022-12-12 15:00:46 -0800 | [diff] [blame] | 2801 | 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 Anforowicz | 1382f39 | 2022-12-12 17:13:23 -0800 | [diff] [blame] | 2805 | 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 Anforowicz | 2f2510a | 2022-09-29 13:06:44 -0700 | [diff] [blame] | 2809 | }); |
| 2810 | } |
| 2811 | |
| 2812 | #[test] |
Lukasz Anforowicz | 4aa1ff9 | 2022-10-10 11:22:22 -0700 | [diff] [blame] | 2813 | fn test_generated_bindings_top_level_items() { |
Lukasz Anforowicz | 2f2510a | 2022-09-29 13:06:44 -0700 | [diff] [blame] | 2814 | let test_src = "pub fn public_function() {}"; |
| 2815 | test_generated_bindings(test_src, |bindings| { |
Lukasz Anforowicz | 4c19ad9 | 2022-12-16 15:23:14 -0800 | [diff] [blame] | 2816 | let bindings = bindings.unwrap(); |
Devin Jeanpierre | 81aec50 | 2023-04-04 15:33:39 -0700 | [diff] [blame] | 2817 | let expected_comment_txt = "Automatically @generated C++ bindings for the following Rust crate:\n\ |
Lukasz Anforowicz | 2f2510a | 2022-09-29 13:06:44 -0700 | [diff] [blame] | 2818 | rust_out"; |
| 2819 | assert_cc_matches!( |
| 2820 | bindings.h_body, |
| 2821 | quote! { |
| 2822 | __COMMENT__ #expected_comment_txt |
Lukasz Anforowicz | 4aa1ff9 | 2022-10-10 11:22:22 -0700 | [diff] [blame] | 2823 | ... |
Lukasz Anforowicz | 31b29cd | 2022-10-10 11:33:41 -0700 | [diff] [blame] | 2824 | __HASH_TOKEN__ pragma once |
| 2825 | ... |
Lukasz Anforowicz | 4aa1ff9 | 2022-10-10 11:22:22 -0700 | [diff] [blame] | 2826 | namespace rust_out { |
| 2827 | ... |
| 2828 | } |
Lukasz Anforowicz | 2f2510a | 2022-09-29 13:06:44 -0700 | [diff] [blame] | 2829 | } |
| 2830 | ); |
Lukasz Anforowicz | e7a2500 | 2022-11-10 06:21:42 -0800 | [diff] [blame] | 2831 | assert_cc_matches!( |
| 2832 | bindings.rs_body, |
| 2833 | quote! { |
| 2834 | __COMMENT__ #expected_comment_txt |
| 2835 | } |
| 2836 | ); |
Lukasz Anforowicz | 2f2510a | 2022-09-29 13:06:44 -0700 | [diff] [blame] | 2837 | }) |
| 2838 | } |
| 2839 | |
Lukasz Anforowicz | 4c19ad9 | 2022-12-16 15:23:14 -0800 | [diff] [blame] | 2840 | /// The `test_generated_bindings_unsupported_item` test verifies how `Err` |
Lukasz Anforowicz | e465b9e | 2023-02-06 15:00:25 -0800 | [diff] [blame] | 2841 | /// from `format_item` is formatted as a C++ comment (in `format_crate` |
Lukasz Anforowicz | 4c19ad9 | 2022-12-16 15:23:14 -0800 | [diff] [blame] | 2842 | /// and `format_unsupported_def`): |
| 2843 | /// - This test covers only a single example of an unsupported item. |
Lukasz Anforowicz | e465b9e | 2023-02-06 15:00:25 -0800 | [diff] [blame] | 2844 | /// Additional coverage is provided by `test_format_item_unsupported_...` |
Lukasz Anforowicz | 4c19ad9 | 2022-12-16 15:23:14 -0800 | [diff] [blame] | 2845 | /// 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 Anforowicz | 2f2510a | 2022-09-29 13:06:44 -0700 | [diff] [blame] | 2850 | #[test] |
| 2851 | fn test_generated_bindings_unsupported_item() { |
Lukasz Anforowicz | 2f2510a | 2022-09-29 13:06:44 -0700 | [diff] [blame] | 2852 | let test_src = r#" |
Lukasz Anforowicz | 7123f21 | 2022-10-20 08:51:04 -0700 | [diff] [blame] | 2853 | #[no_mangle] |
Lukasz Anforowicz | e433306 | 2022-10-17 14:47:53 -0700 | [diff] [blame] | 2854 | pub extern "C" fn reinterpret_cast() {} |
Lukasz Anforowicz | 2f2510a | 2022-09-29 13:06:44 -0700 | [diff] [blame] | 2855 | "#; |
Lukasz Anforowicz | 2f2510a | 2022-09-29 13:06:44 -0700 | [diff] [blame] | 2856 | test_generated_bindings(test_src, |bindings| { |
Lukasz Anforowicz | 4c19ad9 | 2022-12-16 15:23:14 -0800 | [diff] [blame] | 2857 | let bindings = bindings.unwrap(); |
Lukasz Anforowicz | e433306 | 2022-10-17 14:47:53 -0700 | [diff] [blame] | 2858 | let expected_comment_txt = "Error generating bindings for `reinterpret_cast` \ |
Googler | 785e6b4 | 2023-01-23 12:11:36 -0800 | [diff] [blame] | 2859 | defined at <crubit_unittests.rs>;l=3: \ |
Lukasz Anforowicz | e433306 | 2022-10-17 14:47:53 -0700 | [diff] [blame] | 2860 | Error formatting function name: \ |
| 2861 | `reinterpret_cast` is a C++ reserved keyword \ |
| 2862 | and can't be used as a C++ identifier"; |
Lukasz Anforowicz | 2f2510a | 2022-09-29 13:06:44 -0700 | [diff] [blame] | 2863 | assert_cc_matches!( |
| 2864 | bindings.h_body, |
| 2865 | quote! { |
| 2866 | __COMMENT__ #expected_comment_txt |
| 2867 | } |
| 2868 | ); |
Lukasz Anforowicz | 581fd75 | 2022-09-21 11:30:15 -0700 | [diff] [blame] | 2869 | }) |
| 2870 | } |
| 2871 | |
Lukasz Anforowicz | 8a83341 | 2022-10-14 14:44:13 -0700 | [diff] [blame] | 2872 | #[test] |
Lukasz Anforowicz | ce17f3f | 2023-02-27 11:32:14 -0800 | [diff] [blame] | 2873 | 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 Anforowicz | e465b9e | 2023-02-06 15:00:25 -0800 | [diff] [blame] | 2920 | fn test_format_item_fn_extern_c_no_mangle_no_params_no_return_type() { |
Lukasz Anforowicz | 8a83341 | 2022-10-14 14:44:13 -0700 | [diff] [blame] | 2921 | let test_src = r#" |
Lukasz Anforowicz | 7123f21 | 2022-10-20 08:51:04 -0700 | [diff] [blame] | 2922 | #[no_mangle] |
Lukasz Anforowicz | 8a83341 | 2022-10-14 14:44:13 -0700 | [diff] [blame] | 2923 | pub extern "C" fn public_function() {} |
| 2924 | "#; |
Lukasz Anforowicz | e465b9e | 2023-02-06 15:00:25 -0800 | [diff] [blame] | 2925 | test_format_item(test_src, "public_function", |result| { |
Lukasz Anforowicz | 73a2c0d | 2023-03-22 14:07:00 -0700 | [diff] [blame] | 2926 | let result = result.unwrap().unwrap(); |
| 2927 | let main_api = &result.main_api; |
Lukasz Anforowicz | 0b8a02f | 2023-02-10 15:55:40 -0800 | [diff] [blame] | 2928 | assert!(main_api.prereqs.is_empty()); |
Lukasz Anforowicz | e433306 | 2022-10-17 14:47:53 -0700 | [diff] [blame] | 2929 | assert_cc_matches!( |
Lukasz Anforowicz | 0b8a02f | 2023-02-10 15:55:40 -0800 | [diff] [blame] | 2930 | main_api.tokens, |
Lukasz Anforowicz | e433306 | 2022-10-17 14:47:53 -0700 | [diff] [blame] | 2931 | quote! { |
| 2932 | extern "C" void public_function(); |
| 2933 | } |
| 2934 | ); |
Lukasz Anforowicz | 10b1a29 | 2023-04-03 16:19:08 -0700 | [diff] [blame] | 2935 | |
| 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 Anforowicz | e433306 | 2022-10-17 14:47:53 -0700 | [diff] [blame] | 2942 | }); |
| 2943 | } |
| 2944 | |
Lukasz Anforowicz | e465b9e | 2023-02-06 15:00:25 -0800 | [diff] [blame] | 2945 | /// The `test_format_item_fn_explicit_unit_return_type` test below is very |
Lukasz Anforowicz | 4c19ad9 | 2022-12-16 15:23:14 -0800 | [diff] [blame] | 2946 | /// similar to the |
Lukasz Anforowicz | e465b9e | 2023-02-06 15:00:25 -0800 | [diff] [blame] | 2947 | /// `test_format_item_fn_extern_c_no_mangle_no_params_no_return_type` above, |
Lukasz Anforowicz | 4c19ad9 | 2022-12-16 15:23:14 -0800 | [diff] [blame] | 2948 | /// 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 Anforowicz | e433306 | 2022-10-17 14:47:53 -0700 | [diff] [blame] | 2954 | #[test] |
Lukasz Anforowicz | e465b9e | 2023-02-06 15:00:25 -0800 | [diff] [blame] | 2955 | fn test_format_item_fn_explicit_unit_return_type() { |
Lukasz Anforowicz | e433306 | 2022-10-17 14:47:53 -0700 | [diff] [blame] | 2956 | let test_src = r#" |
Lukasz Anforowicz | 7123f21 | 2022-10-20 08:51:04 -0700 | [diff] [blame] | 2957 | #[no_mangle] |
Lukasz Anforowicz | e433306 | 2022-10-17 14:47:53 -0700 | [diff] [blame] | 2958 | pub extern "C" fn explicit_unit_return_type() -> () {} |
| 2959 | "#; |
Lukasz Anforowicz | e465b9e | 2023-02-06 15:00:25 -0800 | [diff] [blame] | 2960 | test_format_item(test_src, "explicit_unit_return_type", |result| { |
Lukasz Anforowicz | 73a2c0d | 2023-03-22 14:07:00 -0700 | [diff] [blame] | 2961 | let result = result.unwrap().unwrap(); |
| 2962 | let main_api = &result.main_api; |
Lukasz Anforowicz | 0b8a02f | 2023-02-10 15:55:40 -0800 | [diff] [blame] | 2963 | assert!(main_api.prereqs.is_empty()); |
Lukasz Anforowicz | e433306 | 2022-10-17 14:47:53 -0700 | [diff] [blame] | 2964 | assert_cc_matches!( |
Lukasz Anforowicz | 0b8a02f | 2023-02-10 15:55:40 -0800 | [diff] [blame] | 2965 | main_api.tokens, |
Lukasz Anforowicz | e433306 | 2022-10-17 14:47:53 -0700 | [diff] [blame] | 2966 | quote! { |
| 2967 | extern "C" void explicit_unit_return_type(); |
| 2968 | } |
| 2969 | ); |
| 2970 | }); |
| 2971 | } |
| 2972 | |
| 2973 | #[test] |
Lukasz Anforowicz | e465b9e | 2023-02-06 15:00:25 -0800 | [diff] [blame] | 2974 | fn test_format_item_fn_never_return_type() { |
Lukasz Anforowicz | 903fc63 | 2022-10-25 08:55:33 -0700 | [diff] [blame] | 2975 | 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 Anforowicz | e465b9e | 2023-02-06 15:00:25 -0800 | [diff] [blame] | 2981 | test_format_item(test_src, "never_returning_function", |result| { |
Lukasz Anforowicz | 903fc63 | 2022-10-25 08:55:33 -0700 | [diff] [blame] | 2982 | // 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 Anforowicz | 73a2c0d | 2023-03-22 14:07:00 -0700 | [diff] [blame] | 2986 | let result = result.unwrap().unwrap(); |
| 2987 | let main_api = &result.main_api; |
Lukasz Anforowicz | 0b8a02f | 2023-02-10 15:55:40 -0800 | [diff] [blame] | 2988 | assert!(main_api.prereqs.is_empty()); |
Lukasz Anforowicz | 903fc63 | 2022-10-25 08:55:33 -0700 | [diff] [blame] | 2989 | assert_cc_matches!( |
Lukasz Anforowicz | 0b8a02f | 2023-02-10 15:55:40 -0800 | [diff] [blame] | 2990 | main_api.tokens, |
Lukasz Anforowicz | 903fc63 | 2022-10-25 08:55:33 -0700 | [diff] [blame] | 2991 | quote! { |
| 2992 | extern "C" void never_returning_function(); |
| 2993 | } |
| 2994 | ); |
| 2995 | }) |
| 2996 | } |
| 2997 | |
Lukasz Anforowicz | e465b9e | 2023-02-06 15:00:25 -0800 | [diff] [blame] | 2998 | /// `test_format_item_fn_mangling` checks that bindings can be generated for |
Lukasz Anforowicz | 4c19ad9 | 2022-12-16 15:23:14 -0800 | [diff] [blame] | 2999 | /// `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 Anforowicz | 903fc63 | 2022-10-25 08:55:33 -0700 | [diff] [blame] | 3003 | #[test] |
Lukasz Anforowicz | e465b9e | 2023-02-06 15:00:25 -0800 | [diff] [blame] | 3004 | fn test_format_item_fn_mangling() { |
Lukasz Anforowicz | 7123f21 | 2022-10-20 08:51:04 -0700 | [diff] [blame] | 3005 | let test_src = r#" |
Lukasz Anforowicz | f04bc52 | 2022-11-04 09:19:51 -0700 | [diff] [blame] | 3006 | pub extern "C" fn public_function(x: f64, y: f64) -> f64 { x + y } |
Lukasz Anforowicz | 7123f21 | 2022-10-20 08:51:04 -0700 | [diff] [blame] | 3007 | "#; |
Lukasz Anforowicz | e465b9e | 2023-02-06 15:00:25 -0800 | [diff] [blame] | 3008 | test_format_item(test_src, "public_function", |result| { |
Lukasz Anforowicz | 73a2c0d | 2023-03-22 14:07:00 -0700 | [diff] [blame] | 3009 | let result = result.unwrap().unwrap(); |
| 3010 | let main_api = &result.main_api; |
Lukasz Anforowicz | 6753d40 | 2023-02-10 16:41:23 -0800 | [diff] [blame] | 3011 | assert!(main_api.prereqs.is_empty()); |
Lukasz Anforowicz | f04bc52 | 2022-11-04 09:19:51 -0700 | [diff] [blame] | 3012 | assert_cc_matches!( |
Lukasz Anforowicz | 6753d40 | 2023-02-10 16:41:23 -0800 | [diff] [blame] | 3013 | main_api.tokens, |
| 3014 | quote! { |
Lukasz Anforowicz | b9e515a | 2023-07-05 12:48:22 -0700 | [diff] [blame] | 3015 | double public_function(double x, double y); |
Lukasz Anforowicz | 6753d40 | 2023-02-10 16:41:23 -0800 | [diff] [blame] | 3016 | } |
| 3017 | ); |
Lukasz Anforowicz | 73a2c0d | 2023-03-22 14:07:00 -0700 | [diff] [blame] | 3018 | assert!(result.rs_details.is_empty()); |
| 3019 | assert!(result.cc_details.prereqs.is_empty()); |
Lukasz Anforowicz | 6753d40 | 2023-02-10 16:41:23 -0800 | [diff] [blame] | 3020 | assert_cc_matches!( |
Lukasz Anforowicz | 73a2c0d | 2023-03-22 14:07:00 -0700 | [diff] [blame] | 3021 | result.cc_details.tokens, |
Lukasz Anforowicz | f04bc52 | 2022-11-04 09:19:51 -0700 | [diff] [blame] | 3022 | quote! { |
Lukasz Anforowicz | bad9963 | 2022-11-18 16:39:13 -0800 | [diff] [blame] | 3023 | namespace __crubit_internal { |
Lukasz Anforowicz | 73edf15 | 2023-04-04 12:05:00 -0700 | [diff] [blame] | 3024 | extern "C" double ...(double, double); |
Lukasz Anforowicz | bad9963 | 2022-11-18 16:39:13 -0800 | [diff] [blame] | 3025 | } |
Googler | 47fd957 | 2023-01-20 09:57:32 -0800 | [diff] [blame] | 3026 | ... |
Lukasz Anforowicz | f04bc52 | 2022-11-04 09:19:51 -0700 | [diff] [blame] | 3027 | inline double public_function(double x, double y) { |
Lukasz Anforowicz | 7589483 | 2022-11-22 18:25:28 -0800 | [diff] [blame] | 3028 | return __crubit_internal::...(x, y); |
Lukasz Anforowicz | f04bc52 | 2022-11-04 09:19:51 -0700 | [diff] [blame] | 3029 | } |
| 3030 | } |
| 3031 | ); |
Lukasz Anforowicz | 7123f21 | 2022-10-20 08:51:04 -0700 | [diff] [blame] | 3032 | }); |
| 3033 | } |
| 3034 | |
| 3035 | #[test] |
Lukasz Anforowicz | e465b9e | 2023-02-06 15:00:25 -0800 | [diff] [blame] | 3036 | fn test_format_item_fn_export_name() { |
Lukasz Anforowicz | 7123f21 | 2022-10-20 08:51:04 -0700 | [diff] [blame] | 3037 | let test_src = r#" |
Lukasz Anforowicz | f04bc52 | 2022-11-04 09:19:51 -0700 | [diff] [blame] | 3038 | #[export_name = "export_name"] |
| 3039 | pub extern "C" fn public_function(x: f64, y: f64) -> f64 { x + y } |
Lukasz Anforowicz | 7123f21 | 2022-10-20 08:51:04 -0700 | [diff] [blame] | 3040 | "#; |
Lukasz Anforowicz | e465b9e | 2023-02-06 15:00:25 -0800 | [diff] [blame] | 3041 | test_format_item(test_src, "public_function", |result| { |
Lukasz Anforowicz | 73a2c0d | 2023-03-22 14:07:00 -0700 | [diff] [blame] | 3042 | let result = result.unwrap().unwrap(); |
| 3043 | let main_api = &result.main_api; |
Lukasz Anforowicz | 6753d40 | 2023-02-10 16:41:23 -0800 | [diff] [blame] | 3044 | assert!(main_api.prereqs.is_empty()); |
Lukasz Anforowicz | f04bc52 | 2022-11-04 09:19:51 -0700 | [diff] [blame] | 3045 | assert_cc_matches!( |
Lukasz Anforowicz | 6753d40 | 2023-02-10 16:41:23 -0800 | [diff] [blame] | 3046 | main_api.tokens, |
| 3047 | quote! { |
Lukasz Anforowicz | b9e515a | 2023-07-05 12:48:22 -0700 | [diff] [blame] | 3048 | double public_function(double x, double y); |
Lukasz Anforowicz | 6753d40 | 2023-02-10 16:41:23 -0800 | [diff] [blame] | 3049 | } |
| 3050 | ); |
Lukasz Anforowicz | 10b1a29 | 2023-04-03 16:19:08 -0700 | [diff] [blame] | 3051 | |
| 3052 | // There is no need to have a separate thunk for an `extern "C"` function. |
Lukasz Anforowicz | 73a2c0d | 2023-03-22 14:07:00 -0700 | [diff] [blame] | 3053 | assert!(result.rs_details.is_empty()); |
Lukasz Anforowicz | 10b1a29 | 2023-04-03 16:19:08 -0700 | [diff] [blame] | 3054 | |
| 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 Anforowicz | 73a2c0d | 2023-03-22 14:07:00 -0700 | [diff] [blame] | 3057 | assert!(result.cc_details.prereqs.is_empty()); |
Lukasz Anforowicz | 6753d40 | 2023-02-10 16:41:23 -0800 | [diff] [blame] | 3058 | assert_cc_matches!( |
Lukasz Anforowicz | 73a2c0d | 2023-03-22 14:07:00 -0700 | [diff] [blame] | 3059 | result.cc_details.tokens, |
Lukasz Anforowicz | f04bc52 | 2022-11-04 09:19:51 -0700 | [diff] [blame] | 3060 | quote! { |
Lukasz Anforowicz | bad9963 | 2022-11-18 16:39:13 -0800 | [diff] [blame] | 3061 | namespace __crubit_internal { |
Lukasz Anforowicz | 73edf15 | 2023-04-04 12:05:00 -0700 | [diff] [blame] | 3062 | extern "C" double export_name(double, double); |
Lukasz Anforowicz | f04bc52 | 2022-11-04 09:19:51 -0700 | [diff] [blame] | 3063 | } |
Googler | 47fd957 | 2023-01-20 09:57:32 -0800 | [diff] [blame] | 3064 | ... |
Lukasz Anforowicz | bad9963 | 2022-11-18 16:39:13 -0800 | [diff] [blame] | 3065 | inline double public_function(double x, double y) { |
| 3066 | return __crubit_internal::export_name(x, y); |
| 3067 | } |
Lukasz Anforowicz | f04bc52 | 2022-11-04 09:19:51 -0700 | [diff] [blame] | 3068 | } |
| 3069 | ); |
Lukasz Anforowicz | 7123f21 | 2022-10-20 08:51:04 -0700 | [diff] [blame] | 3070 | }); |
| 3071 | } |
| 3072 | |
| 3073 | #[test] |
Lukasz Anforowicz | e465b9e | 2023-02-06 15:00:25 -0800 | [diff] [blame] | 3074 | fn test_format_item_unsupported_fn_unsafe() { |
Lukasz Anforowicz | e433306 | 2022-10-17 14:47:53 -0700 | [diff] [blame] | 3075 | let test_src = r#" |
Lukasz Anforowicz | 7123f21 | 2022-10-20 08:51:04 -0700 | [diff] [blame] | 3076 | #[no_mangle] |
Lukasz Anforowicz | e433306 | 2022-10-17 14:47:53 -0700 | [diff] [blame] | 3077 | pub unsafe extern "C" fn foo() {} |
| 3078 | "#; |
Lukasz Anforowicz | e465b9e | 2023-02-06 15:00:25 -0800 | [diff] [blame] | 3079 | test_format_item(test_src, "foo", |result| { |
Lukasz Anforowicz | 4c19ad9 | 2022-12-16 15:23:14 -0800 | [diff] [blame] | 3080 | let err = result.unwrap_err(); |
Lukasz Anforowicz | e433306 | 2022-10-17 14:47:53 -0700 | [diff] [blame] | 3081 | assert_eq!( |
| 3082 | err, |
| 3083 | "Bindings for `unsafe` functions \ |
| 3084 | are not fully designed yet (b/254095482)" |
| 3085 | ); |
| 3086 | }); |
| 3087 | } |
| 3088 | |
Lukasz Anforowicz | e465b9e | 2023-02-06 15:00:25 -0800 | [diff] [blame] | 3089 | /// `test_format_item_fn_const` tests how bindings for an `const fn` are |
Lukasz Anforowicz | 4c19ad9 | 2022-12-16 15:23:14 -0800 | [diff] [blame] | 3090 | /// generated. |
| 3091 | /// |
Googler | 47fd957 | 2023-01-20 09:57:32 -0800 | [diff] [blame] | 3092 | /// 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 Anforowicz | e433306 | 2022-10-17 14:47:53 -0700 | [diff] [blame] | 3095 | #[test] |
Lukasz Anforowicz | e465b9e | 2023-02-06 15:00:25 -0800 | [diff] [blame] | 3096 | fn test_format_item_fn_const() { |
Lukasz Anforowicz | e433306 | 2022-10-17 14:47:53 -0700 | [diff] [blame] | 3097 | let test_src = r#" |
| 3098 | pub const fn foo(i: i32) -> i32 { i * 42 } |
| 3099 | "#; |
Lukasz Anforowicz | e465b9e | 2023-02-06 15:00:25 -0800 | [diff] [blame] | 3100 | test_format_item(test_src, "foo", |result| { |
Lukasz Anforowicz | e433306 | 2022-10-17 14:47:53 -0700 | [diff] [blame] | 3101 | // TODO(b/254095787): Update test expectations below once `const fn` from Rust |
| 3102 | // is translated into a `consteval` C++ function. |
Lukasz Anforowicz | 73a2c0d | 2023-03-22 14:07:00 -0700 | [diff] [blame] | 3103 | let result = result.unwrap().unwrap(); |
| 3104 | let main_api = &result.main_api; |
Lukasz Anforowicz | 6753d40 | 2023-02-10 16:41:23 -0800 | [diff] [blame] | 3105 | assert!(!main_api.prereqs.is_empty()); |
Lukasz Anforowicz | 2f849cf | 2022-11-17 15:52:39 -0800 | [diff] [blame] | 3106 | assert_cc_matches!( |
Lukasz Anforowicz | 6753d40 | 2023-02-10 16:41:23 -0800 | [diff] [blame] | 3107 | main_api.tokens, |
| 3108 | quote! { |
Lukasz Anforowicz | b9e515a | 2023-07-05 12:48:22 -0700 | [diff] [blame] | 3109 | std::int32_t foo(std::int32_t i); |
Lukasz Anforowicz | 6753d40 | 2023-02-10 16:41:23 -0800 | [diff] [blame] | 3110 | } |
| 3111 | ); |
Lukasz Anforowicz | 73a2c0d | 2023-03-22 14:07:00 -0700 | [diff] [blame] | 3112 | assert!(!result.cc_details.prereqs.is_empty()); |
Lukasz Anforowicz | 6753d40 | 2023-02-10 16:41:23 -0800 | [diff] [blame] | 3113 | assert_cc_matches!( |
Lukasz Anforowicz | 73a2c0d | 2023-03-22 14:07:00 -0700 | [diff] [blame] | 3114 | result.cc_details.tokens, |
Lukasz Anforowicz | 2f849cf | 2022-11-17 15:52:39 -0800 | [diff] [blame] | 3115 | quote! { |
Lukasz Anforowicz | bad9963 | 2022-11-18 16:39:13 -0800 | [diff] [blame] | 3116 | namespace __crubit_internal { |
Lukasz Anforowicz | 73edf15 | 2023-04-04 12:05:00 -0700 | [diff] [blame] | 3117 | extern "C" std::int32_t ...( std::int32_t); |
Lukasz Anforowicz | bad9963 | 2022-11-18 16:39:13 -0800 | [diff] [blame] | 3118 | } |
Googler | 47fd957 | 2023-01-20 09:57:32 -0800 | [diff] [blame] | 3119 | ... |
Lukasz Anforowicz | 2f849cf | 2022-11-17 15:52:39 -0800 | [diff] [blame] | 3120 | inline std::int32_t foo(std::int32_t i) { |
Lukasz Anforowicz | b4beb39 | 2022-12-01 16:49:11 -0800 | [diff] [blame] | 3121 | return __crubit_internal::...(i); |
Lukasz Anforowicz | 2f849cf | 2022-11-17 15:52:39 -0800 | [diff] [blame] | 3122 | } |
| 3123 | } |
| 3124 | ); |
Lukasz Anforowicz | 2f849cf | 2022-11-17 15:52:39 -0800 | [diff] [blame] | 3125 | assert_rs_matches!( |
Lukasz Anforowicz | 73a2c0d | 2023-03-22 14:07:00 -0700 | [diff] [blame] | 3126 | result.rs_details, |
Lukasz Anforowicz | 2f849cf | 2022-11-17 15:52:39 -0800 | [diff] [blame] | 3127 | quote! { |
| 3128 | #[no_mangle] |
| 3129 | extern "C" |
Lukasz Anforowicz | b4beb39 | 2022-12-01 16:49:11 -0800 | [diff] [blame] | 3130 | fn ...(i: i32) -> i32 { |
Lukasz Anforowicz | 8b475a4 | 2022-11-29 09:33:02 -0800 | [diff] [blame] | 3131 | ::rust_out::foo(i) |
Lukasz Anforowicz | 2f849cf | 2022-11-17 15:52:39 -0800 | [diff] [blame] | 3132 | } |
| 3133 | } |
| 3134 | ); |
Lukasz Anforowicz | e433306 | 2022-10-17 14:47:53 -0700 | [diff] [blame] | 3135 | }); |
| 3136 | } |
| 3137 | |
| 3138 | #[test] |
Lukasz Anforowicz | e465b9e | 2023-02-06 15:00:25 -0800 | [diff] [blame] | 3139 | fn test_format_item_fn_with_c_unwind_abi() { |
Lukasz Anforowicz | e433306 | 2022-10-17 14:47:53 -0700 | [diff] [blame] | 3140 | // See also https://rust-lang.github.io/rfcs/2945-c-unwind-abi.html |
| 3141 | let test_src = r#" |
| 3142 | #![feature(c_unwind)] |
Lukasz Anforowicz | 7123f21 | 2022-10-20 08:51:04 -0700 | [diff] [blame] | 3143 | |
| 3144 | #[no_mangle] |
Lukasz Anforowicz | e433306 | 2022-10-17 14:47:53 -0700 | [diff] [blame] | 3145 | pub extern "C-unwind" fn may_throw() {} |
| 3146 | "#; |
Lukasz Anforowicz | e465b9e | 2023-02-06 15:00:25 -0800 | [diff] [blame] | 3147 | test_format_item(test_src, "may_throw", |result| { |
Lukasz Anforowicz | 73a2c0d | 2023-03-22 14:07:00 -0700 | [diff] [blame] | 3148 | let result = result.unwrap().unwrap(); |
| 3149 | let main_api = &result.main_api; |
Lukasz Anforowicz | 0b8a02f | 2023-02-10 15:55:40 -0800 | [diff] [blame] | 3150 | assert!(main_api.prereqs.is_empty()); |
Lukasz Anforowicz | e433306 | 2022-10-17 14:47:53 -0700 | [diff] [blame] | 3151 | assert_cc_matches!( |
Lukasz Anforowicz | 0b8a02f | 2023-02-10 15:55:40 -0800 | [diff] [blame] | 3152 | main_api.tokens, |
Lukasz Anforowicz | e433306 | 2022-10-17 14:47:53 -0700 | [diff] [blame] | 3153 | quote! { |
| 3154 | extern "C" void may_throw(); |
| 3155 | } |
| 3156 | ); |
| 3157 | }); |
| 3158 | } |
| 3159 | |
Lukasz Anforowicz | e465b9e | 2023-02-06 15:00:25 -0800 | [diff] [blame] | 3160 | /// This test mainly verifies that `format_item` correctly propagates |
Lukasz Anforowicz | 5c9c00d | 2022-12-02 08:41:39 -0800 | [diff] [blame] | 3161 | /// `CcPrerequisites` of parameter types and return type. |
| 3162 | #[test] |
Lukasz Anforowicz | e465b9e | 2023-02-06 15:00:25 -0800 | [diff] [blame] | 3163 | fn test_format_item_fn_cc_prerequisites_if_cpp_definition_needed() { |
Lukasz Anforowicz | 5c9c00d | 2022-12-02 08:41:39 -0800 | [diff] [blame] | 3164 | let test_src = r#" |
Lukasz Anforowicz | 5c9c00d | 2022-12-02 08:41:39 -0800 | [diff] [blame] | 3165 | pub fn foo(_i: i32) -> S { panic!("foo") } |
Lukasz Anforowicz | 7a7b81f | 2023-01-12 15:50:46 -0800 | [diff] [blame] | 3166 | pub struct S(i32); |
Lukasz Anforowicz | 5c9c00d | 2022-12-02 08:41:39 -0800 | [diff] [blame] | 3167 | "#; |
Lukasz Anforowicz | e465b9e | 2023-02-06 15:00:25 -0800 | [diff] [blame] | 3168 | test_format_item(test_src, "foo", |result| { |
Lukasz Anforowicz | 73a2c0d | 2023-03-22 14:07:00 -0700 | [diff] [blame] | 3169 | let result = result.unwrap().unwrap(); |
| 3170 | let main_api = &result.main_api; |
Lukasz Anforowicz | 5c9c00d | 2022-12-02 08:41:39 -0800 | [diff] [blame] | 3171 | |
| 3172 | // Minimal coverage, just to double-check that the test setup works. |
Lukasz Anforowicz | 7a7b81f | 2023-01-12 15:50:46 -0800 | [diff] [blame] | 3173 | // |
| 3174 | // Note that this is a definition, and therefore `S` should be defined |
| 3175 | // earlier (not just forward declared). |
Lukasz Anforowicz | 6753d40 | 2023-02-10 16:41:23 -0800 | [diff] [blame] | 3176 | assert_cc_matches!(main_api.tokens, quote! { S foo(std::int32_t _i);}); |
Lukasz Anforowicz | 73a2c0d | 2023-03-22 14:07:00 -0700 | [diff] [blame] | 3177 | assert_cc_matches!(result.cc_details.tokens, quote! { S foo(std::int32_t _i) { ... }}); |
Lukasz Anforowicz | 5c9c00d | 2022-12-02 08:41:39 -0800 | [diff] [blame] | 3178 | |
| 3179 | // Main checks: `CcPrerequisites::includes`. |
| 3180 | assert_cc_matches!( |
Lukasz Anforowicz | 6753d40 | 2023-02-10 16:41:23 -0800 | [diff] [blame] | 3181 | format_cc_includes(&main_api.prereqs.includes), |
| 3182 | quote! { include <cstdint> } |
| 3183 | ); |
| 3184 | assert_cc_matches!( |
Lukasz Anforowicz | 73a2c0d | 2023-03-22 14:07:00 -0700 | [diff] [blame] | 3185 | format_cc_includes(&result.cc_details.prereqs.includes), |
Lukasz Anforowicz | 5c9c00d | 2022-12-02 08:41:39 -0800 | [diff] [blame] | 3186 | quote! { include <cstdint> } |
| 3187 | ); |
Lukasz Anforowicz | 7a7b81f | 2023-01-12 15:50:46 -0800 | [diff] [blame] | 3188 | |
| 3189 | // Main checks: `CcPrerequisites::defs` and `CcPrerequisites::fwd_decls`. |
Lukasz Anforowicz | 5c9c00d | 2022-12-02 08:41:39 -0800 | [diff] [blame] | 3190 | // |
Lukasz Anforowicz | e465b9e | 2023-02-06 15:00:25 -0800 | [diff] [blame] | 3191 | // Verifying the actual def_id is tricky, because `test_format_item` doesn't |
Lukasz Anforowicz | 5c9c00d | 2022-12-02 08:41:39 -0800 | [diff] [blame] | 3192 | // expose `tcx` to the verification function (and therefore calling |
| 3193 | // `find_def_id_by_name` is not easily possible). |
Lukasz Anforowicz | 6753d40 | 2023-02-10 16:41:23 -0800 | [diff] [blame] | 3194 | // |
| 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 Anforowicz | 73a2c0d | 2023-03-22 14:07:00 -0700 | [diff] [blame] | 3198 | assert_eq!(1, result.cc_details.prereqs.defs.len()); |
| 3199 | assert_eq!(0, result.cc_details.prereqs.fwd_decls.len()); |
Lukasz Anforowicz | 7a7b81f | 2023-01-12 15:50:46 -0800 | [diff] [blame] | 3200 | }); |
| 3201 | } |
| 3202 | |
Lukasz Anforowicz | e465b9e | 2023-02-06 15:00:25 -0800 | [diff] [blame] | 3203 | /// This test verifies that `format_item` uses `CcPrerequisites::fwd_decls` |
Lukasz Anforowicz | 10b1a29 | 2023-04-03 16:19:08 -0700 | [diff] [blame] | 3204 | /// rather than `CcPrerequisites::defs` for function declarations in the |
| 3205 | /// `main_api`. |
Lukasz Anforowicz | 7a7b81f | 2023-01-12 15:50:46 -0800 | [diff] [blame] | 3206 | #[test] |
Lukasz Anforowicz | e465b9e | 2023-02-06 15:00:25 -0800 | [diff] [blame] | 3207 | fn test_format_item_fn_cc_prerequisites_if_only_cpp_declaration_needed() { |
Lukasz Anforowicz | 7a7b81f | 2023-01-12 15:50:46 -0800 | [diff] [blame] | 3208 | let test_src = r#" |
Lukasz Anforowicz | 7a7b81f | 2023-01-12 15:50:46 -0800 | [diff] [blame] | 3209 | #[no_mangle] |
| 3210 | pub extern "C" fn foo(s: S) -> bool { s.0 } |
| 3211 | |
| 3212 | #[repr(C)] |
| 3213 | pub struct S(bool); |
| 3214 | "#; |
Lukasz Anforowicz | e465b9e | 2023-02-06 15:00:25 -0800 | [diff] [blame] | 3215 | test_format_item(test_src, "foo", |result| { |
Lukasz Anforowicz | 73a2c0d | 2023-03-22 14:07:00 -0700 | [diff] [blame] | 3216 | let result = result.unwrap().unwrap(); |
| 3217 | let main_api = &result.main_api; |
Lukasz Anforowicz | 7a7b81f | 2023-01-12 15:50:46 -0800 | [diff] [blame] | 3218 | |
| 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 Anforowicz | b9e515a | 2023-07-05 12:48:22 -0700 | [diff] [blame] | 3224 | assert_cc_matches!(main_api.tokens, quote! { bool foo(::rust_out::S s); }); |
Lukasz Anforowicz | 7a7b81f | 2023-01-12 15:50:46 -0800 | [diff] [blame] | 3225 | |
| 3226 | // Main checks: `CcPrerequisites::defs` and `CcPrerequisites::fwd_decls`. |
| 3227 | // |
Lukasz Anforowicz | e465b9e | 2023-02-06 15:00:25 -0800 | [diff] [blame] | 3228 | // Verifying the actual def_id is tricky, because `test_format_item` doesn't |
Lukasz Anforowicz | 7a7b81f | 2023-01-12 15:50:46 -0800 | [diff] [blame] | 3229 | // expose `tcx` to the verification function (and therefore calling |
| 3230 | // `find_def_id_by_name` is not easily possible). |
Lukasz Anforowicz | 0b8a02f | 2023-02-10 15:55:40 -0800 | [diff] [blame] | 3231 | assert_eq!(0, main_api.prereqs.defs.len()); |
| 3232 | assert_eq!(1, main_api.prereqs.fwd_decls.len()); |
Lukasz Anforowicz | 5c9c00d | 2022-12-02 08:41:39 -0800 | [diff] [blame] | 3233 | }); |
| 3234 | } |
| 3235 | |
Lukasz Anforowicz | e433306 | 2022-10-17 14:47:53 -0700 | [diff] [blame] | 3236 | #[test] |
Lukasz Anforowicz | e465b9e | 2023-02-06 15:00:25 -0800 | [diff] [blame] | 3237 | fn test_format_item_fn_with_type_aliased_return_type() { |
Lukasz Anforowicz | e433306 | 2022-10-17 14:47:53 -0700 | [diff] [blame] | 3238 | // 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 Anforowicz | 4f9f552 | 2022-10-17 15:03:37 -0700 | [diff] [blame] | 3244 | type MyTypeAlias = f64; |
Lukasz Anforowicz | e433306 | 2022-10-17 14:47:53 -0700 | [diff] [blame] | 3245 | |
Lukasz Anforowicz | 7123f21 | 2022-10-20 08:51:04 -0700 | [diff] [blame] | 3246 | #[no_mangle] |
Lukasz Anforowicz | 4f9f552 | 2022-10-17 15:03:37 -0700 | [diff] [blame] | 3247 | pub extern "C" fn type_aliased_return() -> MyTypeAlias { 42.0 } |
Lukasz Anforowicz | e433306 | 2022-10-17 14:47:53 -0700 | [diff] [blame] | 3248 | "#; |
Lukasz Anforowicz | e465b9e | 2023-02-06 15:00:25 -0800 | [diff] [blame] | 3249 | test_format_item(test_src, "type_aliased_return", |result| { |
Lukasz Anforowicz | 73a2c0d | 2023-03-22 14:07:00 -0700 | [diff] [blame] | 3250 | let result = result.unwrap().unwrap(); |
| 3251 | let main_api = &result.main_api; |
Lukasz Anforowicz | 0b8a02f | 2023-02-10 15:55:40 -0800 | [diff] [blame] | 3252 | assert!(main_api.prereqs.is_empty()); |
Lukasz Anforowicz | e433306 | 2022-10-17 14:47:53 -0700 | [diff] [blame] | 3253 | assert_cc_matches!( |
Lukasz Anforowicz | 0b8a02f | 2023-02-10 15:55:40 -0800 | [diff] [blame] | 3254 | main_api.tokens, |
Lukasz Anforowicz | e433306 | 2022-10-17 14:47:53 -0700 | [diff] [blame] | 3255 | quote! { |
Lukasz Anforowicz | 4f9f552 | 2022-10-17 15:03:37 -0700 | [diff] [blame] | 3256 | extern "C" double type_aliased_return(); |
Lukasz Anforowicz | e433306 | 2022-10-17 14:47:53 -0700 | [diff] [blame] | 3257 | } |
| 3258 | ); |
| 3259 | }); |
| 3260 | } |
| 3261 | |
| 3262 | #[test] |
Lukasz Anforowicz | e465b9e | 2023-02-06 15:00:25 -0800 | [diff] [blame] | 3263 | fn test_format_item_fn_with_doc_comment_with_unmangled_name() { |
Googler | 624580b | 2022-12-01 01:23:42 -0800 | [diff] [blame] | 3264 | 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 Anforowicz | e465b9e | 2023-02-06 15:00:25 -0800 | [diff] [blame] | 3272 | test_format_item(test_src, "fn_with_doc_comment_with_unmangled_name", |result| { |
Lukasz Anforowicz | 73a2c0d | 2023-03-22 14:07:00 -0700 | [diff] [blame] | 3273 | let result = result.unwrap().unwrap(); |
| 3274 | let main_api = &result.main_api; |
Lukasz Anforowicz | 0b8a02f | 2023-02-10 15:55:40 -0800 | [diff] [blame] | 3275 | assert!(main_api.prereqs.is_empty()); |
Googler | 624580b | 2022-12-01 01:23:42 -0800 | [diff] [blame] | 3276 | let doc_comments = [ |
| 3277 | " Outer line doc.", |
Googler | 34f3d57 | 2022-12-02 00:53:37 -0800 | [diff] [blame] | 3278 | "", |
Googler | 624580b | 2022-12-01 01:23:42 -0800 | [diff] [blame] | 3279 | " Outer block doc that spans lines.", |
| 3280 | " ", |
Googler | 34f3d57 | 2022-12-02 00:53:37 -0800 | [diff] [blame] | 3281 | "", |
Googler | 624580b | 2022-12-01 01:23:42 -0800 | [diff] [blame] | 3282 | "Doc comment via doc attribute.", |
Googler | 47fd957 | 2023-01-20 09:57:32 -0800 | [diff] [blame] | 3283 | "", |
| 3284 | "Generated from: <crubit_unittests.rs>;l=7", |
Googler | 624580b | 2022-12-01 01:23:42 -0800 | [diff] [blame] | 3285 | ] |
| 3286 | .join("\n"); |
| 3287 | assert_cc_matches!( |
Lukasz Anforowicz | 0b8a02f | 2023-02-10 15:55:40 -0800 | [diff] [blame] | 3288 | main_api.tokens, |
Googler | 624580b | 2022-12-01 01:23:42 -0800 | [diff] [blame] | 3289 | quote! { |
| 3290 | __COMMENT__ #doc_comments |
| 3291 | extern "C" void fn_with_doc_comment_with_unmangled_name(); |
| 3292 | } |
| 3293 | ); |
| 3294 | }); |
| 3295 | } |
| 3296 | |
| 3297 | #[test] |
Lukasz Anforowicz | e465b9e | 2023-02-06 15:00:25 -0800 | [diff] [blame] | 3298 | fn test_format_item_fn_with_inner_doc_comment_with_unmangled_name() { |
Googler | 624580b | 2022-12-01 01:23:42 -0800 | [diff] [blame] | 3299 | 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 Anforowicz | e465b9e | 2023-02-06 15:00:25 -0800 | [diff] [blame] | 3306 | test_format_item(test_src, "fn_with_inner_doc_comment_with_unmangled_name", |result| { |
Lukasz Anforowicz | 73a2c0d | 2023-03-22 14:07:00 -0700 | [diff] [blame] | 3307 | let result = result.unwrap().unwrap(); |
| 3308 | let main_api = &result.main_api; |
Lukasz Anforowicz | 0b8a02f | 2023-02-10 15:55:40 -0800 | [diff] [blame] | 3309 | assert!(main_api.prereqs.is_empty()); |
Googler | 47fd957 | 2023-01-20 09:57:32 -0800 | [diff] [blame] | 3310 | let doc_comments = [ |
| 3311 | " Outer doc comment.", |
| 3312 | " Inner doc comment.", |
| 3313 | "Generated from: <crubit_unittests.rs>;l=4", |
| 3314 | ] |
| 3315 | .join("\n\n"); |
Googler | 624580b | 2022-12-01 01:23:42 -0800 | [diff] [blame] | 3316 | assert_cc_matches!( |
Lukasz Anforowicz | 0b8a02f | 2023-02-10 15:55:40 -0800 | [diff] [blame] | 3317 | main_api.tokens, |
Googler | 624580b | 2022-12-01 01:23:42 -0800 | [diff] [blame] | 3318 | 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 Anforowicz | e465b9e | 2023-02-06 15:00:25 -0800 | [diff] [blame] | 3327 | fn test_format_item_fn_with_doc_comment_with_mangled_name() { |
Googler | 624580b | 2022-12-01 01:23:42 -0800 | [diff] [blame] | 3328 | 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 Anforowicz | e465b9e | 2023-02-06 15:00:25 -0800 | [diff] [blame] | 3332 | test_format_item(test_src, "fn_with_doc_comment_with_mangled_name", |result| { |
Lukasz Anforowicz | 73a2c0d | 2023-03-22 14:07:00 -0700 | [diff] [blame] | 3333 | let result = result.unwrap().unwrap(); |
| 3334 | let main_api = &result.main_api; |
Lukasz Anforowicz | 6753d40 | 2023-02-10 16:41:23 -0800 | [diff] [blame] | 3335 | assert!(main_api.prereqs.is_empty()); |
Googler | 47fd957 | 2023-01-20 09:57:32 -0800 | [diff] [blame] | 3336 | let comment = " Doc comment of a function with mangled name.\n\n\ |
| 3337 | Generated from: <crubit_unittests.rs>;l=3"; |
Googler | 624580b | 2022-12-01 01:23:42 -0800 | [diff] [blame] | 3338 | assert_cc_matches!( |
Lukasz Anforowicz | 6753d40 | 2023-02-10 16:41:23 -0800 | [diff] [blame] | 3339 | main_api.tokens, |
Googler | 624580b | 2022-12-01 01:23:42 -0800 | [diff] [blame] | 3340 | quote! { |
Googler | 624580b | 2022-12-01 01:23:42 -0800 | [diff] [blame] | 3341 | __COMMENT__ #comment |
Lukasz Anforowicz | b9e515a | 2023-07-05 12:48:22 -0700 | [diff] [blame] | 3342 | void fn_with_doc_comment_with_mangled_name(); |
Googler | 624580b | 2022-12-01 01:23:42 -0800 | [diff] [blame] | 3343 | } |
| 3344 | ); |
| 3345 | }); |
| 3346 | } |
| 3347 | |
| 3348 | #[test] |
Lukasz Anforowicz | e465b9e | 2023-02-06 15:00:25 -0800 | [diff] [blame] | 3349 | fn test_format_item_unsupported_fn_name_is_reserved_cpp_keyword() { |
Lukasz Anforowicz | e433306 | 2022-10-17 14:47:53 -0700 | [diff] [blame] | 3350 | let test_src = r#" |
Lukasz Anforowicz | 7123f21 | 2022-10-20 08:51:04 -0700 | [diff] [blame] | 3351 | #[no_mangle] |
Lukasz Anforowicz | e433306 | 2022-10-17 14:47:53 -0700 | [diff] [blame] | 3352 | pub extern "C" fn reinterpret_cast() -> () {} |
| 3353 | "#; |
Lukasz Anforowicz | e465b9e | 2023-02-06 15:00:25 -0800 | [diff] [blame] | 3354 | test_format_item(test_src, "reinterpret_cast", |result| { |
Lukasz Anforowicz | 4c19ad9 | 2022-12-16 15:23:14 -0800 | [diff] [blame] | 3355 | let err = result.unwrap_err(); |
Lukasz Anforowicz | e433306 | 2022-10-17 14:47:53 -0700 | [diff] [blame] | 3356 | 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 Anforowicz | e465b9e | 2023-02-06 15:00:25 -0800 | [diff] [blame] | 3366 | fn test_format_item_unsupported_fn_ret_type() { |
Lukasz Anforowicz | e433306 | 2022-10-17 14:47:53 -0700 | [diff] [blame] | 3367 | let test_src = r#" |
Lukasz Anforowicz | eb58a49 | 2023-01-07 08:25:48 -0800 | [diff] [blame] | 3368 | pub fn foo() -> (i32, i32) { (123, 456) } |
Lukasz Anforowicz | e433306 | 2022-10-17 14:47:53 -0700 | [diff] [blame] | 3369 | "#; |
Lukasz Anforowicz | e465b9e | 2023-02-06 15:00:25 -0800 | [diff] [blame] | 3370 | test_format_item(test_src, "foo", |result| { |
Lukasz Anforowicz | 4c19ad9 | 2022-12-16 15:23:14 -0800 | [diff] [blame] | 3371 | let err = result.unwrap_err(); |
Lukasz Anforowicz | e433306 | 2022-10-17 14:47:53 -0700 | [diff] [blame] | 3372 | assert_eq!( |
| 3373 | err, |
| 3374 | "Error formatting function return type: \ |
Lukasz Anforowicz | eb58a49 | 2023-01-07 08:25:48 -0800 | [diff] [blame] | 3375 | Tuples are not supported yet: (i32, i32) (b/254099023)" |
Lukasz Anforowicz | e433306 | 2022-10-17 14:47:53 -0700 | [diff] [blame] | 3376 | ); |
| 3377 | }); |
| 3378 | } |
| 3379 | |
Lukasz Anforowicz | aa2de7e | 2023-06-15 11:32:05 -0700 | [diff] [blame] | 3380 | /// 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 Anforowicz | e433306 | 2022-10-17 14:47:53 -0700 | [diff] [blame] | 3385 | #[test] |
Lukasz Anforowicz | aa2de7e | 2023-06-15 11:32:05 -0700 | [diff] [blame] | 3386 | fn test_format_item_lifetime_generic_fn_with_inferred_lifetimes() { |
Lukasz Anforowicz | e433306 | 2022-10-17 14:47:53 -0700 | [diff] [blame] | 3387 | let test_src = r#" |
Lukasz Anforowicz | aa2de7e | 2023-06-15 11:32:05 -0700 | [diff] [blame] | 3388 | 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 Anforowicz | aa2de7e | 2023-06-15 11:32:05 -0700 | [diff] [blame] | 3398 | 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 Anforowicz | e433306 | 2022-10-17 14:47:53 -0700 | [diff] [blame] | 3428 | |
Lukasz Anforowicz | aa2de7e | 2023-06-15 11:32:05 -0700 | [diff] [blame] | 3429 | /// 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 Anforowicz | aa2de7e | 2023-06-15 11:32:05 -0700 | [diff] [blame] | 3462 | 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 Anforowicz | e433306 | 2022-10-17 14:47:53 -0700 | [diff] [blame] | 3531 | "#; |
Lukasz Anforowicz | e465b9e | 2023-02-06 15:00:25 -0800 | [diff] [blame] | 3532 | test_format_item(test_src, "foo", |result| { |
Lukasz Anforowicz | 4c19ad9 | 2022-12-16 15:23:14 -0800 | [diff] [blame] | 3533 | let err = result.unwrap_err(); |
Lukasz Anforowicz | 56d94fc | 2023-02-17 12:23:20 -0800 | [diff] [blame] | 3534 | assert_eq!(err, "Generic functions are not supported yet (b/259749023)"); |
Lukasz Anforowicz | e433306 | 2022-10-17 14:47:53 -0700 | [diff] [blame] | 3535 | }); |
| 3536 | } |
| 3537 | |
| 3538 | #[test] |
Lukasz Anforowicz | ed96fcb | 2023-06-12 11:39:45 -0700 | [diff] [blame] | 3539 | fn test_format_item_unsupported_type_generic_fn() { |
Lukasz Anforowicz | e433306 | 2022-10-17 14:47:53 -0700 | [diff] [blame] | 3540 | 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 Anforowicz | e465b9e | 2023-02-06 15:00:25 -0800 | [diff] [blame] | 3547 | test_format_item(test_src, "generic_function", |result| { |
Lukasz Anforowicz | 4c19ad9 | 2022-12-16 15:23:14 -0800 | [diff] [blame] | 3548 | let err = result.unwrap_err(); |
Lukasz Anforowicz | 56d94fc | 2023-02-17 12:23:20 -0800 | [diff] [blame] | 3549 | assert_eq!(err, "Generic functions are not supported yet (b/259749023)"); |
Lukasz Anforowicz | 8a83341 | 2022-10-14 14:44:13 -0700 | [diff] [blame] | 3550 | }); |
| 3551 | } |
| 3552 | |
| 3553 | #[test] |
Lukasz Anforowicz | ed96fcb | 2023-06-12 11:39:45 -0700 | [diff] [blame] | 3554 | fn test_format_item_unsupported_type_generic_struct() { |
Lukasz Anforowicz | 27914f5 | 2022-11-08 10:55:03 -0800 | [diff] [blame] | 3555 | let test_src = r#" |
| 3556 | pub struct Point<T> { |
| 3557 | pub x: T, |
| 3558 | pub y: T, |
| 3559 | } |
| 3560 | "#; |
Lukasz Anforowicz | e465b9e | 2023-02-06 15:00:25 -0800 | [diff] [blame] | 3561 | test_format_item(test_src, "Point", |result| { |
Lukasz Anforowicz | 4c19ad9 | 2022-12-16 15:23:14 -0800 | [diff] [blame] | 3562 | let err = result.unwrap_err(); |
Lukasz Anforowicz | 56d94fc | 2023-02-17 12:23:20 -0800 | [diff] [blame] | 3563 | assert_eq!(err, "Generic types are not supported yet (b/259749095)"); |
Lukasz Anforowicz | 27914f5 | 2022-11-08 10:55:03 -0800 | [diff] [blame] | 3564 | }); |
| 3565 | } |
| 3566 | |
| 3567 | #[test] |
Lukasz Anforowicz | ed96fcb | 2023-06-12 11:39:45 -0700 | [diff] [blame] | 3568 | 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 Anforowicz | e465b9e | 2023-02-06 15:00:25 -0800 | [diff] [blame] | 3588 | fn test_format_item_unsupported_generic_enum() { |
Lukasz Anforowicz | 27914f5 | 2022-11-08 10:55:03 -0800 | [diff] [blame] | 3589 | let test_src = r#" |
| 3590 | pub enum Point<T> { |
| 3591 | Cartesian{x: T, y: T}, |
| 3592 | Polar{angle: T, dist: T}, |
| 3593 | } |
| 3594 | "#; |
Lukasz Anforowicz | e465b9e | 2023-02-06 15:00:25 -0800 | [diff] [blame] | 3595 | test_format_item(test_src, "Point", |result| { |
Lukasz Anforowicz | 4c19ad9 | 2022-12-16 15:23:14 -0800 | [diff] [blame] | 3596 | let err = result.unwrap_err(); |
Lukasz Anforowicz | 56d94fc | 2023-02-17 12:23:20 -0800 | [diff] [blame] | 3597 | assert_eq!(err, "Generic types are not supported yet (b/259749095)"); |
Lukasz Anforowicz | 27914f5 | 2022-11-08 10:55:03 -0800 | [diff] [blame] | 3598 | }); |
| 3599 | } |
| 3600 | |
| 3601 | #[test] |
Lukasz Anforowicz | e465b9e | 2023-02-06 15:00:25 -0800 | [diff] [blame] | 3602 | fn test_format_item_unsupported_generic_union() { |
Lukasz Anforowicz | 27914f5 | 2022-11-08 10:55:03 -0800 | [diff] [blame] | 3603 | let test_src = r#" |
| 3604 | pub union SomeUnion<T> { |
| 3605 | pub x: std::mem::ManuallyDrop<T>, |
| 3606 | pub y: i32, |
| 3607 | } |
| 3608 | "#; |
Lukasz Anforowicz | e465b9e | 2023-02-06 15:00:25 -0800 | [diff] [blame] | 3609 | test_format_item(test_src, "SomeUnion", |result| { |
Lukasz Anforowicz | 4c19ad9 | 2022-12-16 15:23:14 -0800 | [diff] [blame] | 3610 | let err = result.unwrap_err(); |
Lukasz Anforowicz | 56d94fc | 2023-02-17 12:23:20 -0800 | [diff] [blame] | 3611 | assert_eq!(err, "Generic types are not supported yet (b/259749095)"); |
Lukasz Anforowicz | 27914f5 | 2022-11-08 10:55:03 -0800 | [diff] [blame] | 3612 | }); |
| 3613 | } |
| 3614 | |
| 3615 | #[test] |
Lukasz Anforowicz | e465b9e | 2023-02-06 15:00:25 -0800 | [diff] [blame] | 3616 | fn test_format_item_unsupported_fn_async() { |
Lukasz Anforowicz | 8a83341 | 2022-10-14 14:44:13 -0700 | [diff] [blame] | 3617 | let test_src = r#" |
Lukasz Anforowicz | e433306 | 2022-10-17 14:47:53 -0700 | [diff] [blame] | 3618 | pub async fn async_function() {} |
Lukasz Anforowicz | 8a83341 | 2022-10-14 14:44:13 -0700 | [diff] [blame] | 3619 | "#; |
Lukasz Anforowicz | e465b9e | 2023-02-06 15:00:25 -0800 | [diff] [blame] | 3620 | test_format_item(test_src, "async_function", |result| { |
Lukasz Anforowicz | 4c19ad9 | 2022-12-16 15:23:14 -0800 | [diff] [blame] | 3621 | let err = result.unwrap_err(); |
Devin Jeanpierre | 81aec50 | 2023-04-04 15:33:39 -0700 | [diff] [blame] | 3622 | assert_eq!( |
| 3623 | err, |
| 3624 | "Error formatting function return type: \ |
Lukasz Anforowicz | 2f849cf | 2022-11-17 15:52:39 -0800 | [diff] [blame] | 3625 | The following Rust type is not supported yet: \ |
Devin Jeanpierre | 81aec50 | 2023-04-04 15:33:39 -0700 | [diff] [blame] | 3626 | impl std::future::Future<Output = ()>" |
| 3627 | ); |
Lukasz Anforowicz | e433306 | 2022-10-17 14:47:53 -0700 | [diff] [blame] | 3628 | }); |
| 3629 | } |
| 3630 | |
| 3631 | #[test] |
Lukasz Anforowicz | e465b9e | 2023-02-06 15:00:25 -0800 | [diff] [blame] | 3632 | fn test_format_item_fn_rust_abi() { |
Lukasz Anforowicz | e433306 | 2022-10-17 14:47:53 -0700 | [diff] [blame] | 3633 | let test_src = r#" |
Lukasz Anforowicz | 2f849cf | 2022-11-17 15:52:39 -0800 | [diff] [blame] | 3634 | pub fn add(x: f64, y: f64) -> f64 { x * y } |
Lukasz Anforowicz | e433306 | 2022-10-17 14:47:53 -0700 | [diff] [blame] | 3635 | "#; |
Lukasz Anforowicz | e465b9e | 2023-02-06 15:00:25 -0800 | [diff] [blame] | 3636 | test_format_item(test_src, "add", |result| { |
Devin Jeanpierre | 81aec50 | 2023-04-04 15:33:39 -0700 | [diff] [blame] | 3637 | // 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 Anforowicz | 73a2c0d | 2023-03-22 14:07:00 -0700 | [diff] [blame] | 3640 | let result = result.unwrap().unwrap(); |
| 3641 | let main_api = &result.main_api; |
Lukasz Anforowicz | 6753d40 | 2023-02-10 16:41:23 -0800 | [diff] [blame] | 3642 | assert!(main_api.prereqs.is_empty()); |
Lukasz Anforowicz | 2f849cf | 2022-11-17 15:52:39 -0800 | [diff] [blame] | 3643 | assert_cc_matches!( |
Lukasz Anforowicz | 6753d40 | 2023-02-10 16:41:23 -0800 | [diff] [blame] | 3644 | main_api.tokens, |
| 3645 | quote! { |
Lukasz Anforowicz | b9e515a | 2023-07-05 12:48:22 -0700 | [diff] [blame] | 3646 | double add(double x, double y); |
Lukasz Anforowicz | 6753d40 | 2023-02-10 16:41:23 -0800 | [diff] [blame] | 3647 | } |
| 3648 | ); |
Lukasz Anforowicz | 73a2c0d | 2023-03-22 14:07:00 -0700 | [diff] [blame] | 3649 | assert!(result.cc_details.prereqs.is_empty()); |
Lukasz Anforowicz | 6753d40 | 2023-02-10 16:41:23 -0800 | [diff] [blame] | 3650 | assert_cc_matches!( |
Lukasz Anforowicz | 73a2c0d | 2023-03-22 14:07:00 -0700 | [diff] [blame] | 3651 | result.cc_details.tokens, |
Lukasz Anforowicz | 2f849cf | 2022-11-17 15:52:39 -0800 | [diff] [blame] | 3652 | quote! { |
Lukasz Anforowicz | bad9963 | 2022-11-18 16:39:13 -0800 | [diff] [blame] | 3653 | namespace __crubit_internal { |
Lukasz Anforowicz | 73edf15 | 2023-04-04 12:05:00 -0700 | [diff] [blame] | 3654 | extern "C" double ...(double, double); |
Lukasz Anforowicz | bad9963 | 2022-11-18 16:39:13 -0800 | [diff] [blame] | 3655 | } |
Googler | 47fd957 | 2023-01-20 09:57:32 -0800 | [diff] [blame] | 3656 | ... |
Lukasz Anforowicz | 2f849cf | 2022-11-17 15:52:39 -0800 | [diff] [blame] | 3657 | inline double add(double x, double y) { |
Lukasz Anforowicz | b4beb39 | 2022-12-01 16:49:11 -0800 | [diff] [blame] | 3658 | return __crubit_internal::...(x, y); |
Lukasz Anforowicz | 2f849cf | 2022-11-17 15:52:39 -0800 | [diff] [blame] | 3659 | } |
| 3660 | } |
| 3661 | ); |
Lukasz Anforowicz | 2f849cf | 2022-11-17 15:52:39 -0800 | [diff] [blame] | 3662 | assert_rs_matches!( |
Lukasz Anforowicz | 73a2c0d | 2023-03-22 14:07:00 -0700 | [diff] [blame] | 3663 | result.rs_details, |
Lukasz Anforowicz | 2f849cf | 2022-11-17 15:52:39 -0800 | [diff] [blame] | 3664 | quote! { |
| 3665 | #[no_mangle] |
| 3666 | extern "C" |
Lukasz Anforowicz | b4beb39 | 2022-12-01 16:49:11 -0800 | [diff] [blame] | 3667 | fn ...(x: f64, y: f64) -> f64 { |
Lukasz Anforowicz | 8b475a4 | 2022-11-29 09:33:02 -0800 | [diff] [blame] | 3668 | ::rust_out::add(x, y) |
Lukasz Anforowicz | 2f849cf | 2022-11-17 15:52:39 -0800 | [diff] [blame] | 3669 | } |
| 3670 | } |
| 3671 | ); |
| 3672 | }); |
| 3673 | } |
| 3674 | |
Lukasz Anforowicz | 5227499 | 2023-03-08 12:29:28 -0800 | [diff] [blame] | 3675 | #[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 Anforowicz | 73a2c0d | 2023-03-22 14:07:00 -0700 | [diff] [blame] | 3682 | let result = result.unwrap().unwrap(); |
| 3683 | let main_api = &result.main_api; |
Lukasz Anforowicz | 5227499 | 2023-03-08 12:29:28 -0800 | [diff] [blame] | 3684 | assert_cc_matches!( |
| 3685 | main_api.tokens, |
| 3686 | quote! { |
Lukasz Anforowicz | b9e515a | 2023-07-05 12:48:22 -0700 | [diff] [blame] | 3687 | std::int32_t into_i32(::rust_out::S s); |
Lukasz Anforowicz | 5227499 | 2023-03-08 12:29:28 -0800 | [diff] [blame] | 3688 | } |
| 3689 | ); |
| 3690 | assert_cc_matches!( |
Lukasz Anforowicz | 73a2c0d | 2023-03-22 14:07:00 -0700 | [diff] [blame] | 3691 | result.cc_details.tokens, |
Lukasz Anforowicz | 5227499 | 2023-03-08 12:29:28 -0800 | [diff] [blame] | 3692 | quote! { |
| 3693 | namespace __crubit_internal { |
Lukasz Anforowicz | 73edf15 | 2023-04-04 12:05:00 -0700 | [diff] [blame] | 3694 | extern "C" std::int32_t ...(::rust_out::S*); |
Lukasz Anforowicz | 5227499 | 2023-03-08 12:29:28 -0800 | [diff] [blame] | 3695 | } |
| 3696 | ... |
| 3697 | inline std::int32_t into_i32(::rust_out::S s) { |
Lukasz Anforowicz | d082f35 | 2023-03-09 17:46:11 -0800 | [diff] [blame] | 3698 | return __crubit_internal::...(&s); |
Lukasz Anforowicz | 5227499 | 2023-03-08 12:29:28 -0800 | [diff] [blame] | 3699 | } |
| 3700 | } |
| 3701 | ); |
| 3702 | assert_rs_matches!( |
Lukasz Anforowicz | 73a2c0d | 2023-03-22 14:07:00 -0700 | [diff] [blame] | 3703 | result.rs_details, |
Lukasz Anforowicz | 5227499 | 2023-03-08 12:29:28 -0800 | [diff] [blame] | 3704 | quote! { |
| 3705 | #[no_mangle] |
| 3706 | extern "C" |
Lukasz Anforowicz | d082f35 | 2023-03-09 17:46:11 -0800 | [diff] [blame] | 3707 | fn ...(s: &mut ::core::mem::MaybeUninit<::rust_out::S>) -> i32 { |
| 3708 | ::rust_out::into_i32(unsafe { s.assume_init_read() }) |
Lukasz Anforowicz | 5227499 | 2023-03-08 12:29:28 -0800 | [diff] [blame] | 3709 | } |
| 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 Anforowicz | 73a2c0d | 2023-03-22 14:07:00 -0700 | [diff] [blame] | 3722 | let result = result.unwrap().unwrap(); |
| 3723 | let main_api = &result.main_api; |
Lukasz Anforowicz | 5227499 | 2023-03-08 12:29:28 -0800 | [diff] [blame] | 3724 | assert_cc_matches!( |
| 3725 | main_api.tokens, |
| 3726 | quote! { |
Lukasz Anforowicz | b9e515a | 2023-07-05 12:48:22 -0700 | [diff] [blame] | 3727 | ::rust_out::S create(std::int32_t i); |
Lukasz Anforowicz | 5227499 | 2023-03-08 12:29:28 -0800 | [diff] [blame] | 3728 | } |
| 3729 | ); |
| 3730 | assert_cc_matches!( |
Lukasz Anforowicz | 73a2c0d | 2023-03-22 14:07:00 -0700 | [diff] [blame] | 3731 | result.cc_details.tokens, |
Lukasz Anforowicz | 5227499 | 2023-03-08 12:29:28 -0800 | [diff] [blame] | 3732 | quote! { |
| 3733 | namespace __crubit_internal { |
Lukasz Anforowicz | 73edf15 | 2023-04-04 12:05:00 -0700 | [diff] [blame] | 3734 | extern "C" void ...(std::int32_t, ::rust_out::S* __ret_ptr); |
Lukasz Anforowicz | 5227499 | 2023-03-08 12:29:28 -0800 | [diff] [blame] | 3735 | } |
| 3736 | ... |
| 3737 | inline ::rust_out::S create(std::int32_t i) { |
Lukasz Anforowicz | a3b7db0 | 2023-03-09 17:34:05 -0800 | [diff] [blame] | 3738 | crubit::ReturnValueSlot<::rust_out::S> __ret_slot; |
| 3739 | __crubit_internal::...(i, __ret_slot.Get()); |
| 3740 | return std::move(__ret_slot).AssumeInitAndTakeValue(); |
Lukasz Anforowicz | 5227499 | 2023-03-08 12:29:28 -0800 | [diff] [blame] | 3741 | } |
| 3742 | } |
| 3743 | ); |
| 3744 | assert_rs_matches!( |
Lukasz Anforowicz | 73a2c0d | 2023-03-22 14:07:00 -0700 | [diff] [blame] | 3745 | result.rs_details, |
Lukasz Anforowicz | 5227499 | 2023-03-08 12:29:28 -0800 | [diff] [blame] | 3746 | quote! { |
| 3747 | #[no_mangle] |
| 3748 | extern "C" |
Lukasz Anforowicz | a3b7db0 | 2023-03-09 17:34:05 -0800 | [diff] [blame] | 3749 | fn ...( |
| 3750 | i: i32, |
| 3751 | __ret_slot: &mut ::core::mem::MaybeUninit<::rust_out::S> |
| 3752 | ) -> () { |
| 3753 | __ret_slot.write(::rust_out::create(i)); |
Lukasz Anforowicz | 5227499 | 2023-03-08 12:29:28 -0800 | [diff] [blame] | 3754 | } |
| 3755 | } |
| 3756 | ); |
| 3757 | }); |
| 3758 | } |
| 3759 | |
Lukasz Anforowicz | e465b9e | 2023-02-06 15:00:25 -0800 | [diff] [blame] | 3760 | /// `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 Anforowicz | 4c19ad9 | 2022-12-16 15:23:14 -0800 | [diff] [blame] | 3764 | /// |
Lukasz Anforowicz | e465b9e | 2023-02-06 15:00:25 -0800 | [diff] [blame] | 3765 | /// This test almost entirely replicates `test_format_item_fn_rust_abi`, |
Googler | 47fd957 | 2023-01-20 09:57:32 -0800 | [diff] [blame] | 3766 | /// except for the `extern "vectorcall"` part in the `test_src` test |
| 3767 | /// input. |
Lukasz Anforowicz | 4c19ad9 | 2022-12-16 15:23:14 -0800 | [diff] [blame] | 3768 | /// |
Googler | 47fd957 | 2023-01-20 09:57:32 -0800 | [diff] [blame] | 3769 | /// 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 Anforowicz | 4c19ad9 | 2022-12-16 15:23:14 -0800 | [diff] [blame] | 3773 | /// (see also `format_cc_call_conv_as_clang_attribute` in |
| 3774 | /// `rs_bindings_from_cc/src_code_gen.rs`) |
Lukasz Anforowicz | 2f849cf | 2022-11-17 15:52:39 -0800 | [diff] [blame] | 3775 | #[test] |
Lukasz Anforowicz | e465b9e | 2023-02-06 15:00:25 -0800 | [diff] [blame] | 3776 | fn test_format_item_fn_vectorcall_abi() { |
Lukasz Anforowicz | 2f849cf | 2022-11-17 15:52:39 -0800 | [diff] [blame] | 3777 | let test_src = r#" |
| 3778 | #![feature(abi_vectorcall)] |
| 3779 | pub extern "vectorcall" fn add(x: f64, y: f64) -> f64 { x * y } |
| 3780 | "#; |
Lukasz Anforowicz | e465b9e | 2023-02-06 15:00:25 -0800 | [diff] [blame] | 3781 | test_format_item(test_src, "add", |result| { |
Lukasz Anforowicz | 73a2c0d | 2023-03-22 14:07:00 -0700 | [diff] [blame] | 3782 | let result = result.unwrap().unwrap(); |
| 3783 | let main_api = &result.main_api; |
Lukasz Anforowicz | 6753d40 | 2023-02-10 16:41:23 -0800 | [diff] [blame] | 3784 | assert!(main_api.prereqs.is_empty()); |
Lukasz Anforowicz | 2f849cf | 2022-11-17 15:52:39 -0800 | [diff] [blame] | 3785 | assert_cc_matches!( |
Lukasz Anforowicz | 6753d40 | 2023-02-10 16:41:23 -0800 | [diff] [blame] | 3786 | main_api.tokens, |
| 3787 | quote! { |
Lukasz Anforowicz | b9e515a | 2023-07-05 12:48:22 -0700 | [diff] [blame] | 3788 | double add(double x, double y); |
Lukasz Anforowicz | 6753d40 | 2023-02-10 16:41:23 -0800 | [diff] [blame] | 3789 | } |
| 3790 | ); |
Lukasz Anforowicz | 73a2c0d | 2023-03-22 14:07:00 -0700 | [diff] [blame] | 3791 | assert!(result.cc_details.prereqs.is_empty()); |
Lukasz Anforowicz | 6753d40 | 2023-02-10 16:41:23 -0800 | [diff] [blame] | 3792 | assert_cc_matches!( |
Lukasz Anforowicz | 73a2c0d | 2023-03-22 14:07:00 -0700 | [diff] [blame] | 3793 | result.cc_details.tokens, |
Lukasz Anforowicz | 2f849cf | 2022-11-17 15:52:39 -0800 | [diff] [blame] | 3794 | quote! { |
Lukasz Anforowicz | bad9963 | 2022-11-18 16:39:13 -0800 | [diff] [blame] | 3795 | namespace __crubit_internal { |
Lukasz Anforowicz | 73edf15 | 2023-04-04 12:05:00 -0700 | [diff] [blame] | 3796 | extern "C" double ...(double, double); |
Lukasz Anforowicz | bad9963 | 2022-11-18 16:39:13 -0800 | [diff] [blame] | 3797 | } |
Googler | 47fd957 | 2023-01-20 09:57:32 -0800 | [diff] [blame] | 3798 | ... |
Lukasz Anforowicz | 2f849cf | 2022-11-17 15:52:39 -0800 | [diff] [blame] | 3799 | inline double add(double x, double y) { |
Lukasz Anforowicz | b4beb39 | 2022-12-01 16:49:11 -0800 | [diff] [blame] | 3800 | return __crubit_internal::...(x, y); |
Lukasz Anforowicz | 2f849cf | 2022-11-17 15:52:39 -0800 | [diff] [blame] | 3801 | } |
| 3802 | } |
| 3803 | ); |
Lukasz Anforowicz | 2f849cf | 2022-11-17 15:52:39 -0800 | [diff] [blame] | 3804 | assert_rs_matches!( |
Lukasz Anforowicz | 73a2c0d | 2023-03-22 14:07:00 -0700 | [diff] [blame] | 3805 | result.rs_details, |
Lukasz Anforowicz | 2f849cf | 2022-11-17 15:52:39 -0800 | [diff] [blame] | 3806 | quote! { |
| 3807 | #[no_mangle] |
| 3808 | extern "C" |
Lukasz Anforowicz | b4beb39 | 2022-12-01 16:49:11 -0800 | [diff] [blame] | 3809 | fn ...(x: f64, y: f64) -> f64 { |
Lukasz Anforowicz | 8b475a4 | 2022-11-29 09:33:02 -0800 | [diff] [blame] | 3810 | ::rust_out::add(x, y) |
Lukasz Anforowicz | 2f849cf | 2022-11-17 15:52:39 -0800 | [diff] [blame] | 3811 | } |
| 3812 | } |
| 3813 | ); |
| 3814 | }); |
Lukasz Anforowicz | e433306 | 2022-10-17 14:47:53 -0700 | [diff] [blame] | 3815 | } |
| 3816 | |
| 3817 | #[test] |
Lukasz Anforowicz | e465b9e | 2023-02-06 15:00:25 -0800 | [diff] [blame] | 3818 | fn test_format_item_unsupported_fn_variadic() { |
Lukasz Anforowicz | e433306 | 2022-10-17 14:47:53 -0700 | [diff] [blame] | 3819 | let test_src = r#" |
| 3820 | #![feature(c_variadic)] |
Lukasz Anforowicz | 7123f21 | 2022-10-20 08:51:04 -0700 | [diff] [blame] | 3821 | |
| 3822 | #[no_mangle] |
Lukasz Anforowicz | e433306 | 2022-10-17 14:47:53 -0700 | [diff] [blame] | 3823 | pub unsafe extern "C" fn variadic_function(_fmt: *const u8, ...) {} |
| 3824 | "#; |
Lukasz Anforowicz | e465b9e | 2023-02-06 15:00:25 -0800 | [diff] [blame] | 3825 | test_format_item(test_src, "variadic_function", |result| { |
Lukasz Anforowicz | 13794df | 2022-10-21 07:56:34 -0700 | [diff] [blame] | 3826 | // TODO(b/254097223): Add support for variadic functions. |
Lukasz Anforowicz | 4c19ad9 | 2022-12-16 15:23:14 -0800 | [diff] [blame] | 3827 | let err = result.unwrap_err(); |
Lukasz Anforowicz | e433306 | 2022-10-17 14:47:53 -0700 | [diff] [blame] | 3828 | assert_eq!(err, "C variadic functions are not supported (b/254097223)"); |
| 3829 | }); |
| 3830 | } |
| 3831 | |
| 3832 | #[test] |
Lukasz Anforowicz | e465b9e | 2023-02-06 15:00:25 -0800 | [diff] [blame] | 3833 | fn test_format_item_fn_params() { |
Lukasz Anforowicz | 903fc63 | 2022-10-25 08:55:33 -0700 | [diff] [blame] | 3834 | let test_src = r#" |
| 3835 | #[allow(unused_variables)] |
| 3836 | #[no_mangle] |
| 3837 | pub extern "C" fn foo(b: bool, f: f64) {} |
| 3838 | "#; |
Lukasz Anforowicz | e465b9e | 2023-02-06 15:00:25 -0800 | [diff] [blame] | 3839 | test_format_item(test_src, "foo", |result| { |
Lukasz Anforowicz | 73a2c0d | 2023-03-22 14:07:00 -0700 | [diff] [blame] | 3840 | let result = result.unwrap().unwrap(); |
| 3841 | let main_api = &result.main_api; |
Lukasz Anforowicz | 0b8a02f | 2023-02-10 15:55:40 -0800 | [diff] [blame] | 3842 | assert!(main_api.prereqs.is_empty()); |
Lukasz Anforowicz | 903fc63 | 2022-10-25 08:55:33 -0700 | [diff] [blame] | 3843 | assert_cc_matches!( |
Lukasz Anforowicz | 0b8a02f | 2023-02-10 15:55:40 -0800 | [diff] [blame] | 3844 | main_api.tokens, |
Lukasz Anforowicz | 903fc63 | 2022-10-25 08:55:33 -0700 | [diff] [blame] | 3845 | quote! { |
Googler | 47fd957 | 2023-01-20 09:57:32 -0800 | [diff] [blame] | 3846 | ... |
Lukasz Anforowicz | 903fc63 | 2022-10-25 08:55:33 -0700 | [diff] [blame] | 3847 | extern "C" void foo(bool b, double f); |
| 3848 | } |
| 3849 | ); |
| 3850 | }); |
| 3851 | } |
| 3852 | |
| 3853 | #[test] |
Lukasz Anforowicz | e465b9e | 2023-02-06 15:00:25 -0800 | [diff] [blame] | 3854 | fn test_format_item_fn_param_name_reserved_keyword() { |
Lukasz Anforowicz | 903fc63 | 2022-10-25 08:55:33 -0700 | [diff] [blame] | 3855 | let test_src = r#" |
| 3856 | #[allow(unused_variables)] |
| 3857 | #[no_mangle] |
| 3858 | pub extern "C" fn some_function(reinterpret_cast: f64) {} |
| 3859 | "#; |
Lukasz Anforowicz | e465b9e | 2023-02-06 15:00:25 -0800 | [diff] [blame] | 3860 | test_format_item(test_src, "some_function", |result| { |
Lukasz Anforowicz | 73a2c0d | 2023-03-22 14:07:00 -0700 | [diff] [blame] | 3861 | let result = result.unwrap().unwrap(); |
| 3862 | let main_api = &result.main_api; |
Lukasz Anforowicz | 0b8a02f | 2023-02-10 15:55:40 -0800 | [diff] [blame] | 3863 | assert!(main_api.prereqs.is_empty()); |
Lukasz Anforowicz | 903fc63 | 2022-10-25 08:55:33 -0700 | [diff] [blame] | 3864 | assert_cc_matches!( |
Lukasz Anforowicz | 0b8a02f | 2023-02-10 15:55:40 -0800 | [diff] [blame] | 3865 | main_api.tokens, |
Lukasz Anforowicz | 903fc63 | 2022-10-25 08:55:33 -0700 | [diff] [blame] | 3866 | quote! { |
Googler | 47fd957 | 2023-01-20 09:57:32 -0800 | [diff] [blame] | 3867 | ... |
Lukasz Anforowicz | 903fc63 | 2022-10-25 08:55:33 -0700 | [diff] [blame] | 3868 | extern "C" void some_function(double __param_0); |
| 3869 | } |
| 3870 | ); |
| 3871 | }); |
| 3872 | } |
| 3873 | |
| 3874 | #[test] |
Lukasz Anforowicz | e465b9e | 2023-02-06 15:00:25 -0800 | [diff] [blame] | 3875 | fn test_format_item_fn_with_multiple_anonymous_parameter_names() { |
Lukasz Anforowicz | c51aeb1 | 2022-11-07 10:56:18 -0800 | [diff] [blame] | 3876 | let test_src = r#" |
Lukasz Anforowicz | 2f849cf | 2022-11-17 15:52:39 -0800 | [diff] [blame] | 3877 | pub fn foo(_: f64, _: f64) {} |
Lukasz Anforowicz | c51aeb1 | 2022-11-07 10:56:18 -0800 | [diff] [blame] | 3878 | "#; |
Lukasz Anforowicz | e465b9e | 2023-02-06 15:00:25 -0800 | [diff] [blame] | 3879 | test_format_item(test_src, "foo", |result| { |
Lukasz Anforowicz | 73a2c0d | 2023-03-22 14:07:00 -0700 | [diff] [blame] | 3880 | let result = result.unwrap().unwrap(); |
| 3881 | let main_api = &result.main_api; |
Lukasz Anforowicz | 6753d40 | 2023-02-10 16:41:23 -0800 | [diff] [blame] | 3882 | assert!(main_api.prereqs.is_empty()); |
Lukasz Anforowicz | c51aeb1 | 2022-11-07 10:56:18 -0800 | [diff] [blame] | 3883 | assert_cc_matches!( |
Lukasz Anforowicz | 6753d40 | 2023-02-10 16:41:23 -0800 | [diff] [blame] | 3884 | main_api.tokens, |
| 3885 | quote! { |
Lukasz Anforowicz | b9e515a | 2023-07-05 12:48:22 -0700 | [diff] [blame] | 3886 | void foo(double __param_0, double __param_1); |
Lukasz Anforowicz | 6753d40 | 2023-02-10 16:41:23 -0800 | [diff] [blame] | 3887 | } |
| 3888 | ); |
Lukasz Anforowicz | 73a2c0d | 2023-03-22 14:07:00 -0700 | [diff] [blame] | 3889 | assert!(result.cc_details.prereqs.is_empty()); |
Lukasz Anforowicz | 6753d40 | 2023-02-10 16:41:23 -0800 | [diff] [blame] | 3890 | assert_cc_matches!( |
Lukasz Anforowicz | 73a2c0d | 2023-03-22 14:07:00 -0700 | [diff] [blame] | 3891 | result.cc_details.tokens, |
Lukasz Anforowicz | c51aeb1 | 2022-11-07 10:56:18 -0800 | [diff] [blame] | 3892 | quote! { |
Lukasz Anforowicz | bad9963 | 2022-11-18 16:39:13 -0800 | [diff] [blame] | 3893 | namespace __crubit_internal { |
Lukasz Anforowicz | 73edf15 | 2023-04-04 12:05:00 -0700 | [diff] [blame] | 3894 | extern "C" void ...(double, double); |
Lukasz Anforowicz | bad9963 | 2022-11-18 16:39:13 -0800 | [diff] [blame] | 3895 | } |
Googler | 47fd957 | 2023-01-20 09:57:32 -0800 | [diff] [blame] | 3896 | ... |
Lukasz Anforowicz | 2f849cf | 2022-11-17 15:52:39 -0800 | [diff] [blame] | 3897 | inline void foo(double __param_0, double __param_1) { |
Lukasz Anforowicz | 7589483 | 2022-11-22 18:25:28 -0800 | [diff] [blame] | 3898 | return __crubit_internal::...(__param_0, __param_1); |
Lukasz Anforowicz | 2f849cf | 2022-11-17 15:52:39 -0800 | [diff] [blame] | 3899 | } |
| 3900 | } |
| 3901 | ); |
Lukasz Anforowicz | 2f849cf | 2022-11-17 15:52:39 -0800 | [diff] [blame] | 3902 | assert_rs_matches!( |
Lukasz Anforowicz | 73a2c0d | 2023-03-22 14:07:00 -0700 | [diff] [blame] | 3903 | result.rs_details, |
Lukasz Anforowicz | 2f849cf | 2022-11-17 15:52:39 -0800 | [diff] [blame] | 3904 | quote! { |
| 3905 | #[no_mangle] |
| 3906 | extern "C" fn ...(__param_0: f64, __param_1: f64) -> () { |
Lukasz Anforowicz | 8b475a4 | 2022-11-29 09:33:02 -0800 | [diff] [blame] | 3907 | ::rust_out::foo(__param_0, __param_1) |
Lukasz Anforowicz | 2f849cf | 2022-11-17 15:52:39 -0800 | [diff] [blame] | 3908 | } |
Lukasz Anforowicz | c51aeb1 | 2022-11-07 10:56:18 -0800 | [diff] [blame] | 3909 | } |
| 3910 | ); |
| 3911 | }); |
| 3912 | } |
| 3913 | |
Lukasz Anforowicz | c51aeb1 | 2022-11-07 10:56:18 -0800 | [diff] [blame] | 3914 | #[test] |
Lukasz Anforowicz | e465b9e | 2023-02-06 15:00:25 -0800 | [diff] [blame] | 3915 | fn test_format_item_fn_with_destructuring_parameter_name() { |
Lukasz Anforowicz | 7589483 | 2022-11-22 18:25:28 -0800 | [diff] [blame] | 3916 | 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 Anforowicz | e465b9e | 2023-02-06 15:00:25 -0800 | [diff] [blame] | 3928 | test_format_item(test_src, "func", |result| { |
Lukasz Anforowicz | 73a2c0d | 2023-03-22 14:07:00 -0700 | [diff] [blame] | 3929 | let result = result.unwrap().unwrap(); |
| 3930 | let main_api = &result.main_api; |
Lukasz Anforowicz | 7589483 | 2022-11-22 18:25:28 -0800 | [diff] [blame] | 3931 | assert_cc_matches!( |
Lukasz Anforowicz | 6753d40 | 2023-02-10 16:41:23 -0800 | [diff] [blame] | 3932 | main_api.tokens, |
| 3933 | quote! { |
Lukasz Anforowicz | b9e515a | 2023-07-05 12:48:22 -0700 | [diff] [blame] | 3934 | std::int32_t func(::rust_out::S __param_0); |
Lukasz Anforowicz | 6753d40 | 2023-02-10 16:41:23 -0800 | [diff] [blame] | 3935 | } |
| 3936 | ); |
| 3937 | assert_cc_matches!( |
Lukasz Anforowicz | 73a2c0d | 2023-03-22 14:07:00 -0700 | [diff] [blame] | 3938 | result.cc_details.tokens, |
Lukasz Anforowicz | 7589483 | 2022-11-22 18:25:28 -0800 | [diff] [blame] | 3939 | quote! { |
| 3940 | namespace __crubit_internal { |
Lukasz Anforowicz | 73edf15 | 2023-04-04 12:05:00 -0700 | [diff] [blame] | 3941 | extern "C" std::int32_t ...(::rust_out::S*); |
Lukasz Anforowicz | 7589483 | 2022-11-22 18:25:28 -0800 | [diff] [blame] | 3942 | } |
Googler | 47fd957 | 2023-01-20 09:57:32 -0800 | [diff] [blame] | 3943 | ... |
Lukasz Anforowicz | 8b475a4 | 2022-11-29 09:33:02 -0800 | [diff] [blame] | 3944 | inline std::int32_t func(::rust_out::S __param_0) { |
Lukasz Anforowicz | d082f35 | 2023-03-09 17:46:11 -0800 | [diff] [blame] | 3945 | return __crubit_internal::...(&__param_0); |
Lukasz Anforowicz | 7589483 | 2022-11-22 18:25:28 -0800 | [diff] [blame] | 3946 | } |
| 3947 | } |
| 3948 | ); |
| 3949 | assert_rs_matches!( |
Lukasz Anforowicz | 73a2c0d | 2023-03-22 14:07:00 -0700 | [diff] [blame] | 3950 | result.rs_details, |
Lukasz Anforowicz | 7589483 | 2022-11-22 18:25:28 -0800 | [diff] [blame] | 3951 | quote! { |
| 3952 | #[no_mangle] |
Lukasz Anforowicz | d082f35 | 2023-03-09 17:46:11 -0800 | [diff] [blame] | 3953 | 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 Anforowicz | 7589483 | 2022-11-22 18:25:28 -0800 | [diff] [blame] | 3957 | } |
| 3958 | } |
| 3959 | ); |
| 3960 | }); |
| 3961 | } |
| 3962 | |
| 3963 | #[test] |
Lukasz Anforowicz | e465b9e | 2023-02-06 15:00:25 -0800 | [diff] [blame] | 3964 | fn test_format_item_unsupported_fn_param_type() { |
Lukasz Anforowicz | e433306 | 2022-10-17 14:47:53 -0700 | [diff] [blame] | 3965 | let test_src = r#" |
Lukasz Anforowicz | eb58a49 | 2023-01-07 08:25:48 -0800 | [diff] [blame] | 3966 | pub fn foo(_param: (i32, i32)) {} |
Lukasz Anforowicz | e433306 | 2022-10-17 14:47:53 -0700 | [diff] [blame] | 3967 | "#; |
Lukasz Anforowicz | e465b9e | 2023-02-06 15:00:25 -0800 | [diff] [blame] | 3968 | test_format_item(test_src, "foo", |result| { |
Lukasz Anforowicz | 4c19ad9 | 2022-12-16 15:23:14 -0800 | [diff] [blame] | 3969 | let err = result.unwrap_err(); |
Devin Jeanpierre | 81aec50 | 2023-04-04 15:33:39 -0700 | [diff] [blame] | 3970 | assert_eq!( |
| 3971 | err, |
| 3972 | "Error handling parameter #0: \ |
| 3973 | Tuples are not supported yet: (i32, i32) (b/254099023)" |
| 3974 | ); |
Lukasz Anforowicz | 903fc63 | 2022-10-25 08:55:33 -0700 | [diff] [blame] | 3975 | }); |
| 3976 | } |
| 3977 | |
| 3978 | #[test] |
Lukasz Anforowicz | e465b9e | 2023-02-06 15:00:25 -0800 | [diff] [blame] | 3979 | fn test_format_item_unsupported_fn_param_type_unit() { |
Lukasz Anforowicz | 903fc63 | 2022-10-25 08:55:33 -0700 | [diff] [blame] | 3980 | let test_src = r#" |
| 3981 | #[no_mangle] |
| 3982 | pub fn fn_with_params(_param: ()) {} |
| 3983 | "#; |
Lukasz Anforowicz | e465b9e | 2023-02-06 15:00:25 -0800 | [diff] [blame] | 3984 | test_format_item(test_src, "fn_with_params", |result| { |
Lukasz Anforowicz | 4c19ad9 | 2022-12-16 15:23:14 -0800 | [diff] [blame] | 3985 | let err = result.unwrap_err(); |
Devin Jeanpierre | 81aec50 | 2023-04-04 15:33:39 -0700 | [diff] [blame] | 3986 | assert_eq!( |
| 3987 | err, |
| 3988 | "Error handling parameter #0: \ |
| 3989 | `()` / `void` is only supported as a return type (b/254507801)" |
| 3990 | ); |
Lukasz Anforowicz | 903fc63 | 2022-10-25 08:55:33 -0700 | [diff] [blame] | 3991 | }); |
| 3992 | } |
| 3993 | |
| 3994 | #[test] |
Lukasz Anforowicz | e465b9e | 2023-02-06 15:00:25 -0800 | [diff] [blame] | 3995 | fn test_format_item_unsupported_fn_param_type_never() { |
Lukasz Anforowicz | 903fc63 | 2022-10-25 08:55:33 -0700 | [diff] [blame] | 3996 | let test_src = r#" |
| 3997 | #![feature(never_type)] |
| 3998 | |
| 3999 | #[no_mangle] |
| 4000 | pub extern "C" fn fn_with_params(_param: !) {} |
| 4001 | "#; |
Lukasz Anforowicz | e465b9e | 2023-02-06 15:00:25 -0800 | [diff] [blame] | 4002 | test_format_item(test_src, "fn_with_params", |result| { |
Lukasz Anforowicz | 4c19ad9 | 2022-12-16 15:23:14 -0800 | [diff] [blame] | 4003 | let err = result.unwrap_err(); |
Lukasz Anforowicz | 903fc63 | 2022-10-25 08:55:33 -0700 | [diff] [blame] | 4004 | assert_eq!( |
| 4005 | err, |
Lukasz Anforowicz | a691cf5 | 2023-03-08 12:24:33 -0800 | [diff] [blame] | 4006 | "Error handling parameter #0: \ |
Lukasz Anforowicz | 903fc63 | 2022-10-25 08:55:33 -0700 | [diff] [blame] | 4007 | The never type `!` is only supported as a return type (b/254507801)" |
| 4008 | ); |
Lukasz Anforowicz | e433306 | 2022-10-17 14:47:53 -0700 | [diff] [blame] | 4009 | }); |
| 4010 | } |
| 4011 | |
Lukasz Anforowicz | 7589483 | 2022-11-22 18:25:28 -0800 | [diff] [blame] | 4012 | /// 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 Anforowicz | e465b9e | 2023-02-06 15:00:25 -0800 | [diff] [blame] | 4016 | fn test_format_item_struct_with_fields() { |
Lukasz Anforowicz | 7589483 | 2022-11-22 18:25:28 -0800 | [diff] [blame] | 4017 | 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 Anforowicz | e465b9e | 2023-02-06 15:00:25 -0800 | [diff] [blame] | 4026 | test_format_item(test_src, "SomeStruct", |result| { |
Lukasz Anforowicz | 73a2c0d | 2023-03-22 14:07:00 -0700 | [diff] [blame] | 4027 | let result = result.unwrap().unwrap(); |
| 4028 | let main_api = &result.main_api; |
Lukasz Anforowicz | 2eb2429 | 2023-03-14 09:38:21 -0700 | [diff] [blame] | 4029 | assert!(!main_api.prereqs.is_empty()); |
Lukasz Anforowicz | 7589483 | 2022-11-22 18:25:28 -0800 | [diff] [blame] | 4030 | assert_cc_matches!( |
Lukasz Anforowicz | 0b8a02f | 2023-02-10 15:55:40 -0800 | [diff] [blame] | 4031 | main_api.tokens, |
Lukasz Anforowicz | 7589483 | 2022-11-22 18:25:28 -0800 | [diff] [blame] | 4032 | quote! { |
Googler | 47fd957 | 2023-01-20 09:57:32 -0800 | [diff] [blame] | 4033 | ... |
Devin Jeanpierre | 64ac8ad | 2023-05-30 17:22:55 -0700 | [diff] [blame] | 4034 | struct CRUBIT_INTERNAL_RUST_TYPE(...) alignas(4) SomeStruct final { |
Lukasz Anforowicz | 7589483 | 2022-11-22 18:25:28 -0800 | [diff] [blame] | 4035 | public: |
Lukasz Anforowicz | 9924c43 | 2023-04-04 12:51:22 -0700 | [diff] [blame] | 4036 | __COMMENT__ "`SomeStruct` doesn't implement the `Default` trait" |
Lukasz Anforowicz | 7589483 | 2022-11-22 18:25:28 -0800 | [diff] [blame] | 4037 | SomeStruct() = delete; |
| 4038 | |
Lukasz Anforowicz | 57c7014 | 2023-07-07 08:35:04 -0700 | [diff] [blame] | 4039 | __COMMENT__ "No custom `Drop` impl and no custom \"drop glue\" required" |
| 4040 | ~SomeStruct() = default; |
Lukasz Anforowicz | 7589483 | 2022-11-22 18:25:28 -0800 | [diff] [blame] | 4041 | SomeStruct(SomeStruct&&) = default; |
Lukasz Anforowicz | c12fc18 | 2023-06-06 08:57:47 -0700 | [diff] [blame] | 4042 | SomeStruct& operator=(SomeStruct&&) = default; |
Lukasz Anforowicz | 7589483 | 2022-11-22 18:25:28 -0800 | [diff] [blame] | 4043 | |
Lukasz Anforowicz | 57c7014 | 2023-07-07 08:35:04 -0700 | [diff] [blame] | 4044 | __COMMENT__ "`SomeStruct` doesn't implement the `Clone` trait" |
| 4045 | SomeStruct(const SomeStruct&) = delete; |
| 4046 | SomeStruct& operator=(const SomeStruct&) = delete; |
Lukasz Anforowicz | 8250222 | 2023-06-23 09:27:14 -0700 | [diff] [blame] | 4047 | public: union { ... std::int32_t x; }; |
| 4048 | public: union { ... std::int32_t y; }; |
Lukasz Anforowicz | 7589483 | 2022-11-22 18:25:28 -0800 | [diff] [blame] | 4049 | private: |
Lukasz Anforowicz | b9e515a | 2023-07-05 12:48:22 -0700 | [diff] [blame] | 4050 | static void __crubit_field_offset_assertions(); |
Lukasz Anforowicz | 7589483 | 2022-11-22 18:25:28 -0800 | [diff] [blame] | 4051 | }; |
Lukasz Anforowicz | 0b8a02f | 2023-02-10 15:55:40 -0800 | [diff] [blame] | 4052 | } |
| 4053 | ); |
| 4054 | assert_cc_matches!( |
Lukasz Anforowicz | 73a2c0d | 2023-03-22 14:07:00 -0700 | [diff] [blame] | 4055 | result.cc_details.tokens, |
Lukasz Anforowicz | 0b8a02f | 2023-02-10 15:55:40 -0800 | [diff] [blame] | 4056 | quote! { |
Lukasz Anforowicz | 7589483 | 2022-11-22 18:25:28 -0800 | [diff] [blame] | 4057 | static_assert(sizeof(SomeStruct) == 8, ...); |
| 4058 | static_assert(alignof(SomeStruct) == 4, ...); |
Lukasz Anforowicz | e0a778c | 2023-06-06 09:00:32 -0700 | [diff] [blame] | 4059 | static_assert(std::is_trivially_move_constructible_v<SomeStruct>); |
| 4060 | static_assert(std::is_trivially_move_assignable_v<SomeStruct>); |
Lukasz Anforowicz | e3eb1cf | 2023-03-14 09:56:00 -0700 | [diff] [blame] | 4061 | inline void SomeStruct::__crubit_field_offset_assertions() { |
| 4062 | static_assert(0 == offsetof(SomeStruct, x)); |
| 4063 | static_assert(4 == offsetof(SomeStruct, y)); |
| 4064 | } |
Lukasz Anforowicz | 7589483 | 2022-11-22 18:25:28 -0800 | [diff] [blame] | 4065 | } |
| 4066 | ); |
| 4067 | assert_rs_matches!( |
Lukasz Anforowicz | 73a2c0d | 2023-03-22 14:07:00 -0700 | [diff] [blame] | 4068 | result.rs_details, |
Lukasz Anforowicz | 7589483 | 2022-11-22 18:25:28 -0800 | [diff] [blame] | 4069 | quote! { |
Lukasz Anforowicz | 8b475a4 | 2022-11-29 09:33:02 -0800 | [diff] [blame] | 4070 | const _: () = assert!(::std::mem::size_of::<::rust_out::SomeStruct>() == 8); |
| 4071 | const _: () = assert!(::std::mem::align_of::<::rust_out::SomeStruct>() == 4); |
Lukasz Anforowicz | cf60f52 | 2023-03-14 10:03:55 -0700 | [diff] [blame] | 4072 | const _: () = assert!( memoffset::offset_of!(::rust_out::SomeStruct, x) == 0); |
| 4073 | const _: () = assert!( memoffset::offset_of!(::rust_out::SomeStruct, y) == 4); |
Lukasz Anforowicz | 7589483 | 2022-11-22 18:25:28 -0800 | [diff] [blame] | 4074 | } |
| 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 Anforowicz | e465b9e | 2023-02-06 15:00:25 -0800 | [diff] [blame] | 4082 | fn test_format_item_struct_with_tuple() { |
Lukasz Anforowicz | 7589483 | 2022-11-22 18:25:28 -0800 | [diff] [blame] | 4083 | let test_src = r#" |
Lukasz Anforowicz | cf60f52 | 2023-03-14 10:03:55 -0700 | [diff] [blame] | 4084 | pub struct TupleStruct(pub i32, pub i32); |
Lukasz Anforowicz | 7589483 | 2022-11-22 18:25:28 -0800 | [diff] [blame] | 4085 | const _: () = assert!(std::mem::size_of::<TupleStruct>() == 8); |
| 4086 | const _: () = assert!(std::mem::align_of::<TupleStruct>() == 4); |
| 4087 | "#; |
Lukasz Anforowicz | e465b9e | 2023-02-06 15:00:25 -0800 | [diff] [blame] | 4088 | test_format_item(test_src, "TupleStruct", |result| { |
Lukasz Anforowicz | 73a2c0d | 2023-03-22 14:07:00 -0700 | [diff] [blame] | 4089 | let result = result.unwrap().unwrap(); |
| 4090 | let main_api = &result.main_api; |
Lukasz Anforowicz | 2eb2429 | 2023-03-14 09:38:21 -0700 | [diff] [blame] | 4091 | assert!(!main_api.prereqs.is_empty()); |
Lukasz Anforowicz | 7589483 | 2022-11-22 18:25:28 -0800 | [diff] [blame] | 4092 | assert_cc_matches!( |
Lukasz Anforowicz | 0b8a02f | 2023-02-10 15:55:40 -0800 | [diff] [blame] | 4093 | main_api.tokens, |
Lukasz Anforowicz | 7589483 | 2022-11-22 18:25:28 -0800 | [diff] [blame] | 4094 | quote! { |
Googler | 47fd957 | 2023-01-20 09:57:32 -0800 | [diff] [blame] | 4095 | ... |
Devin Jeanpierre | 64ac8ad | 2023-05-30 17:22:55 -0700 | [diff] [blame] | 4096 | struct CRUBIT_INTERNAL_RUST_TYPE(...) alignas(4) TupleStruct final { |
Lukasz Anforowicz | 7589483 | 2022-11-22 18:25:28 -0800 | [diff] [blame] | 4097 | public: |
Lukasz Anforowicz | 9924c43 | 2023-04-04 12:51:22 -0700 | [diff] [blame] | 4098 | __COMMENT__ "`TupleStruct` doesn't implement the `Default` trait" |
Lukasz Anforowicz | 7589483 | 2022-11-22 18:25:28 -0800 | [diff] [blame] | 4099 | TupleStruct() = delete; |
| 4100 | |
Lukasz Anforowicz | 57c7014 | 2023-07-07 08:35:04 -0700 | [diff] [blame] | 4101 | __COMMENT__ "No custom `Drop` impl and no custom \"drop glue\" required" |
| 4102 | ~TupleStruct() = default; |
Lukasz Anforowicz | 7589483 | 2022-11-22 18:25:28 -0800 | [diff] [blame] | 4103 | TupleStruct(TupleStruct&&) = default; |
Lukasz Anforowicz | c12fc18 | 2023-06-06 08:57:47 -0700 | [diff] [blame] | 4104 | TupleStruct& operator=(TupleStruct&&) = default; |
Lukasz Anforowicz | 7589483 | 2022-11-22 18:25:28 -0800 | [diff] [blame] | 4105 | |
Lukasz Anforowicz | 57c7014 | 2023-07-07 08:35:04 -0700 | [diff] [blame] | 4106 | __COMMENT__ "`TupleStruct` doesn't implement the `Clone` trait" |
| 4107 | TupleStruct(const TupleStruct&) = delete; |
| 4108 | TupleStruct& operator=(const TupleStruct&) = delete; |
Lukasz Anforowicz | 8250222 | 2023-06-23 09:27:14 -0700 | [diff] [blame] | 4109 | public: union { ... std::int32_t __field0; }; |
| 4110 | public: union { ... std::int32_t __field1; }; |
Lukasz Anforowicz | 7589483 | 2022-11-22 18:25:28 -0800 | [diff] [blame] | 4111 | private: |
Lukasz Anforowicz | b9e515a | 2023-07-05 12:48:22 -0700 | [diff] [blame] | 4112 | static void __crubit_field_offset_assertions(); |
Lukasz Anforowicz | 7589483 | 2022-11-22 18:25:28 -0800 | [diff] [blame] | 4113 | }; |
Lukasz Anforowicz | 0b8a02f | 2023-02-10 15:55:40 -0800 | [diff] [blame] | 4114 | } |
| 4115 | ); |
| 4116 | assert_cc_matches!( |
Lukasz Anforowicz | 73a2c0d | 2023-03-22 14:07:00 -0700 | [diff] [blame] | 4117 | result.cc_details.tokens, |
Lukasz Anforowicz | 0b8a02f | 2023-02-10 15:55:40 -0800 | [diff] [blame] | 4118 | quote! { |
Lukasz Anforowicz | 7589483 | 2022-11-22 18:25:28 -0800 | [diff] [blame] | 4119 | static_assert(sizeof(TupleStruct) == 8, ...); |
| 4120 | static_assert(alignof(TupleStruct) == 4, ...); |
Lukasz Anforowicz | e0a778c | 2023-06-06 09:00:32 -0700 | [diff] [blame] | 4121 | static_assert(std::is_trivially_move_constructible_v<TupleStruct>); |
| 4122 | static_assert(std::is_trivially_move_assignable_v<TupleStruct>); |
Lukasz Anforowicz | e3eb1cf | 2023-03-14 09:56:00 -0700 | [diff] [blame] | 4123 | inline void TupleStruct::__crubit_field_offset_assertions() { |
| 4124 | static_assert(0 == offsetof(TupleStruct, __field0)); |
| 4125 | static_assert(4 == offsetof(TupleStruct, __field1)); |
| 4126 | } |
Lukasz Anforowicz | 7589483 | 2022-11-22 18:25:28 -0800 | [diff] [blame] | 4127 | } |
| 4128 | ); |
| 4129 | assert_rs_matches!( |
Lukasz Anforowicz | 73a2c0d | 2023-03-22 14:07:00 -0700 | [diff] [blame] | 4130 | result.rs_details, |
Lukasz Anforowicz | 7589483 | 2022-11-22 18:25:28 -0800 | [diff] [blame] | 4131 | quote! { |
Lukasz Anforowicz | 8b475a4 | 2022-11-29 09:33:02 -0800 | [diff] [blame] | 4132 | const _: () = assert!(::std::mem::size_of::<::rust_out::TupleStruct>() == 8); |
| 4133 | const _: () = assert!(::std::mem::align_of::<::rust_out::TupleStruct>() == 4); |
Lukasz Anforowicz | cf60f52 | 2023-03-14 10:03:55 -0700 | [diff] [blame] | 4134 | const _: () = assert!( memoffset::offset_of!(::rust_out::TupleStruct, 0) == 0); |
| 4135 | const _: () = assert!( memoffset::offset_of!(::rust_out::TupleStruct, 1) == 4); |
Lukasz Anforowicz | 7589483 | 2022-11-22 18:25:28 -0800 | [diff] [blame] | 4136 | } |
| 4137 | ); |
| 4138 | }); |
| 4139 | } |
| 4140 | |
Lukasz Anforowicz | 2eb2429 | 2023-03-14 09:38:21 -0700 | [diff] [blame] | 4141 | /// 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 Anforowicz | 73a2c0d | 2023-03-22 14:07:00 -0700 | [diff] [blame] | 4156 | let result = result.unwrap().unwrap(); |
| 4157 | let main_api = &result.main_api; |
Lukasz Anforowicz | 2eb2429 | 2023-03-14 09:38:21 -0700 | [diff] [blame] | 4158 | assert!(!main_api.prereqs.is_empty()); |
| 4159 | assert_cc_matches!( |
| 4160 | main_api.tokens, |
| 4161 | quote! { |
| 4162 | ... |
Devin Jeanpierre | 64ac8ad | 2023-05-30 17:22:55 -0700 | [diff] [blame] | 4163 | struct CRUBIT_INTERNAL_RUST_TYPE(...) alignas(4) SomeStruct final { |
Lukasz Anforowicz | 2eb2429 | 2023-03-14 09:38:21 -0700 | [diff] [blame] | 4164 | ... |
Lukasz Anforowicz | 68c8882 | 2023-06-01 16:06:15 -0700 | [diff] [blame] | 4165 | // 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 Anforowicz | 8250222 | 2023-06-23 09:27:14 -0700 | [diff] [blame] | 4168 | public: union { ... std::int32_t field2; }; |
| 4169 | public: union { ... std::int16_t field1; }; |
| 4170 | public: union { ... std::int16_t field3; }; |
Lukasz Anforowicz | 2eb2429 | 2023-03-14 09:38:21 -0700 | [diff] [blame] | 4171 | private: |
Lukasz Anforowicz | b9e515a | 2023-07-05 12:48:22 -0700 | [diff] [blame] | 4172 | static void __crubit_field_offset_assertions(); |
Lukasz Anforowicz | 2eb2429 | 2023-03-14 09:38:21 -0700 | [diff] [blame] | 4173 | }; |
| 4174 | } |
| 4175 | ); |
| 4176 | assert_cc_matches!( |
Lukasz Anforowicz | 73a2c0d | 2023-03-22 14:07:00 -0700 | [diff] [blame] | 4177 | result.cc_details.tokens, |
Lukasz Anforowicz | 2eb2429 | 2023-03-14 09:38:21 -0700 | [diff] [blame] | 4178 | quote! { |
| 4179 | static_assert(sizeof(SomeStruct) == 8, ...); |
| 4180 | static_assert(alignof(SomeStruct) == 4, ...); |
Lukasz Anforowicz | e0a778c | 2023-06-06 09:00:32 -0700 | [diff] [blame] | 4181 | static_assert(std::is_trivially_move_constructible_v<SomeStruct>); |
| 4182 | static_assert(std::is_trivially_move_assignable_v<SomeStruct>); |
Lukasz Anforowicz | e3eb1cf | 2023-03-14 09:56:00 -0700 | [diff] [blame] | 4183 | 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 Anforowicz | 2eb2429 | 2023-03-14 09:38:21 -0700 | [diff] [blame] | 4188 | } |
| 4189 | ); |
| 4190 | assert_rs_matches!( |
Lukasz Anforowicz | 73a2c0d | 2023-03-22 14:07:00 -0700 | [diff] [blame] | 4191 | result.rs_details, |
Lukasz Anforowicz | 2eb2429 | 2023-03-14 09:38:21 -0700 | [diff] [blame] | 4192 | quote! { |
| 4193 | const _: () = assert!(::std::mem::size_of::<::rust_out::SomeStruct>() == 8); |
| 4194 | const _: () = assert!(::std::mem::align_of::<::rust_out::SomeStruct>() == 4); |
Lukasz Anforowicz | cf60f52 | 2023-03-14 10:03:55 -0700 | [diff] [blame] | 4195 | 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 Anforowicz | 2eb2429 | 2023-03-14 09:38:21 -0700 | [diff] [blame] | 4201 | } |
| 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 Anforowicz | 73a2c0d | 2023-03-22 14:07:00 -0700 | [diff] [blame] | 4218 | let result = result.unwrap().unwrap(); |
| 4219 | let main_api = &result.main_api; |
Lukasz Anforowicz | 2eb2429 | 2023-03-14 09:38:21 -0700 | [diff] [blame] | 4220 | assert!(!main_api.prereqs.is_empty()); |
| 4221 | assert_cc_matches!( |
| 4222 | main_api.tokens, |
| 4223 | quote! { |
| 4224 | ... |
Devin Jeanpierre | 64ac8ad | 2023-05-30 17:22:55 -0700 | [diff] [blame] | 4225 | struct CRUBIT_INTERNAL_RUST_TYPE(...) alignas(1) __attribute__((packed)) SomeStruct final { |
Lukasz Anforowicz | 2eb2429 | 2023-03-14 09:38:21 -0700 | [diff] [blame] | 4226 | ... |
Lukasz Anforowicz | 8250222 | 2023-06-23 09:27:14 -0700 | [diff] [blame] | 4227 | public: union { ... std::uint16_t field1; }; |
| 4228 | public: union { ... std::uint32_t field2; }; |
Lukasz Anforowicz | 68c8882 | 2023-06-01 16:06:15 -0700 | [diff] [blame] | 4229 | private: |
Lukasz Anforowicz | b9e515a | 2023-07-05 12:48:22 -0700 | [diff] [blame] | 4230 | static void __crubit_field_offset_assertions(); |
Lukasz Anforowicz | 2eb2429 | 2023-03-14 09:38:21 -0700 | [diff] [blame] | 4231 | }; |
| 4232 | } |
| 4233 | ); |
| 4234 | assert_cc_matches!( |
Lukasz Anforowicz | 73a2c0d | 2023-03-22 14:07:00 -0700 | [diff] [blame] | 4235 | result.cc_details.tokens, |
Lukasz Anforowicz | 2eb2429 | 2023-03-14 09:38:21 -0700 | [diff] [blame] | 4236 | quote! { |
| 4237 | static_assert(sizeof(SomeStruct) == 6, ...); |
| 4238 | static_assert(alignof(SomeStruct) == 1, ...); |
Lukasz Anforowicz | e0a778c | 2023-06-06 09:00:32 -0700 | [diff] [blame] | 4239 | static_assert(std::is_trivially_move_constructible_v<SomeStruct>); |
| 4240 | static_assert(std::is_trivially_move_assignable_v<SomeStruct>); |
Lukasz Anforowicz | e3eb1cf | 2023-03-14 09:56:00 -0700 | [diff] [blame] | 4241 | inline void SomeStruct::__crubit_field_offset_assertions() { |
| 4242 | static_assert(0 == offsetof(SomeStruct, field1)); |
| 4243 | static_assert(2 == offsetof(SomeStruct, field2)); |
| 4244 | } |
Lukasz Anforowicz | 2eb2429 | 2023-03-14 09:38:21 -0700 | [diff] [blame] | 4245 | } |
| 4246 | ); |
| 4247 | assert_rs_matches!( |
Lukasz Anforowicz | 73a2c0d | 2023-03-22 14:07:00 -0700 | [diff] [blame] | 4248 | result.rs_details, |
Lukasz Anforowicz | 2eb2429 | 2023-03-14 09:38:21 -0700 | [diff] [blame] | 4249 | quote! { |
| 4250 | const _: () = assert!(::std::mem::size_of::<::rust_out::SomeStruct>() == 6); |
| 4251 | const _: () = assert!(::std::mem::align_of::<::rust_out::SomeStruct>() == 1); |
Lukasz Anforowicz | cf60f52 | 2023-03-14 10:03:55 -0700 | [diff] [blame] | 4252 | const _: () = assert!( memoffset::offset_of!(::rust_out::SomeStruct, field1) |
| 4253 | == 0); |
| 4254 | const _: () = assert!( memoffset::offset_of!(::rust_out::SomeStruct, field2) |
| 4255 | == 2); |
Lukasz Anforowicz | 2eb2429 | 2023-03-14 09:38:21 -0700 | [diff] [blame] | 4256 | } |
| 4257 | ); |
| 4258 | }); |
| 4259 | } |
| 4260 | |
Lukasz Anforowicz | 7589483 | 2022-11-22 18:25:28 -0800 | [diff] [blame] | 4261 | #[test] |
Lukasz Anforowicz | 735bfb8 | 2023-03-15 16:18:44 -0700 | [diff] [blame] | 4262 | 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 Anforowicz | 73a2c0d | 2023-03-22 14:07:00 -0700 | [diff] [blame] | 4272 | let result = result.unwrap().unwrap(); |
| 4273 | let main_api = &result.main_api; |
Lukasz Anforowicz | 735bfb8 | 2023-03-15 16:18:44 -0700 | [diff] [blame] | 4274 | assert!(!main_api.prereqs.is_empty()); |
| 4275 | assert_cc_matches!( |
| 4276 | main_api.tokens, |
| 4277 | quote! { |
| 4278 | ... |
Devin Jeanpierre | 64ac8ad | 2023-05-30 17:22:55 -0700 | [diff] [blame] | 4279 | struct CRUBIT_INTERNAL_RUST_TYPE(...) alignas(4) SomeStruct final { |
Lukasz Anforowicz | 735bfb8 | 2023-03-15 16:18:44 -0700 | [diff] [blame] | 4280 | ... |
Lukasz Anforowicz | 8250222 | 2023-06-23 09:27:14 -0700 | [diff] [blame] | 4281 | public: union { ... std::uint32_t f2; }; |
| 4282 | public: union { ... std::uint8_t f1; }; |
Lukasz Anforowicz | 68c8882 | 2023-06-01 16:06:15 -0700 | [diff] [blame] | 4283 | private: unsigned char __padding0[3]; |
| 4284 | private: |
Lukasz Anforowicz | b9e515a | 2023-07-05 12:48:22 -0700 | [diff] [blame] | 4285 | static void __crubit_field_offset_assertions(); |
Lukasz Anforowicz | 735bfb8 | 2023-03-15 16:18:44 -0700 | [diff] [blame] | 4286 | }; |
| 4287 | } |
| 4288 | ); |
| 4289 | assert_cc_matches!( |
Lukasz Anforowicz | 73a2c0d | 2023-03-22 14:07:00 -0700 | [diff] [blame] | 4290 | result.cc_details.tokens, |
Lukasz Anforowicz | 735bfb8 | 2023-03-15 16:18:44 -0700 | [diff] [blame] | 4291 | quote! { |
| 4292 | static_assert(sizeof(SomeStruct) == 8, ...); |
| 4293 | static_assert(alignof(SomeStruct) == 4, ...); |
Lukasz Anforowicz | e0a778c | 2023-06-06 09:00:32 -0700 | [diff] [blame] | 4294 | static_assert(std::is_trivially_move_constructible_v<SomeStruct>); |
| 4295 | static_assert(std::is_trivially_move_assignable_v<SomeStruct>); |
Lukasz Anforowicz | 735bfb8 | 2023-03-15 16:18:44 -0700 | [diff] [blame] | 4296 | 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 Anforowicz | 73a2c0d | 2023-03-22 14:07:00 -0700 | [diff] [blame] | 4303 | result.rs_details, |
Lukasz Anforowicz | 735bfb8 | 2023-03-15 16:18:44 -0700 | [diff] [blame] | 4304 | 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 Anforowicz | 43f06c6 | 2023-02-10 17:14:19 -0800 | [diff] [blame] | 4315 | fn test_format_item_static_method() { |
| 4316 | let test_src = r#" |
Lukasz Anforowicz | 56d94fc | 2023-02-17 12:23:20 -0800 | [diff] [blame] | 4317 | /// No-op `f32` placeholder is used, because ZSTs are not supported |
| 4318 | /// (b/258259459). |
Lukasz Anforowicz | 43f06c6 | 2023-02-10 17:14:19 -0800 | [diff] [blame] | 4319 | 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 Anforowicz | 73a2c0d | 2023-03-22 14:07:00 -0700 | [diff] [blame] | 4328 | let result = result.unwrap().unwrap(); |
| 4329 | let main_api = &result.main_api; |
Lukasz Anforowicz | 43f06c6 | 2023-02-10 17:14:19 -0800 | [diff] [blame] | 4330 | assert_cc_matches!( |
| 4331 | main_api.tokens, |
| 4332 | quote! { |
| 4333 | ... |
| 4334 | struct ... Math final { |
| 4335 | ... |
| 4336 | public: |
| 4337 | ... |
Lukasz Anforowicz | b9e515a | 2023-07-05 12:48:22 -0700 | [diff] [blame] | 4338 | static float add_i32(float x, float y); |
Lukasz Anforowicz | 43f06c6 | 2023-02-10 17:14:19 -0800 | [diff] [blame] | 4339 | ... |
| 4340 | }; |
| 4341 | } |
| 4342 | ); |
| 4343 | assert_cc_matches!( |
Lukasz Anforowicz | 73a2c0d | 2023-03-22 14:07:00 -0700 | [diff] [blame] | 4344 | result.cc_details.tokens, |
Lukasz Anforowicz | 43f06c6 | 2023-02-10 17:14:19 -0800 | [diff] [blame] | 4345 | quote! { |
| 4346 | namespace __crubit_internal { |
Lukasz Anforowicz | 73edf15 | 2023-04-04 12:05:00 -0700 | [diff] [blame] | 4347 | extern "C" float ... (float, float); |
Lukasz Anforowicz | 43f06c6 | 2023-02-10 17:14:19 -0800 | [diff] [blame] | 4348 | } |
| 4349 | inline float Math::add_i32(float x, float y) { |
| 4350 | return __crubit_internal::...(x, y); |
| 4351 | } |
| 4352 | } |
| 4353 | ); |
| 4354 | assert_rs_matches!( |
Lukasz Anforowicz | 73a2c0d | 2023-03-22 14:07:00 -0700 | [diff] [blame] | 4355 | result.rs_details, |
Lukasz Anforowicz | 43f06c6 | 2023-02-10 17:14:19 -0800 | [diff] [blame] | 4356 | 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 Anforowicz | 56d94fc | 2023-02-17 12:23:20 -0800 | [diff] [blame] | 4367 | 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 Anforowicz | 73a2c0d | 2023-03-22 14:07:00 -0700 | [diff] [blame] | 4383 | let result = result.unwrap().unwrap(); |
| 4384 | let main_api = &result.main_api; |
Lukasz Anforowicz | 56d94fc | 2023-02-17 12:23:20 -0800 | [diff] [blame] | 4385 | 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 Anforowicz | 73a2c0d | 2023-03-22 14:07:00 -0700 | [diff] [blame] | 4400 | assert_cc_not_matches!(result.cc_details.tokens, quote! { SomeStruct::generic_method },); |
| 4401 | assert_rs_not_matches!(result.rs_details, quote! { generic_method },); |
Lukasz Anforowicz | 56d94fc | 2023-02-17 12:23:20 -0800 | [diff] [blame] | 4402 | }); |
| 4403 | } |
| 4404 | |
| 4405 | #[test] |
Lukasz Anforowicz | ed96fcb | 2023-06-12 11:39:45 -0700 | [diff] [blame] | 4406 | fn test_format_item_static_method_with_generic_lifetime_parameters_at_fn_level() { |
Lukasz Anforowicz | 56d94fc | 2023-02-17 12:23:20 -0800 | [diff] [blame] | 4407 | 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 Anforowicz | 73a2c0d | 2023-03-22 14:07:00 -0700 | [diff] [blame] | 4417 | let result = result.unwrap().unwrap(); |
| 4418 | let main_api = &result.main_api; |
Lukasz Anforowicz | 56d94fc | 2023-02-17 12:23:20 -0800 | [diff] [blame] | 4419 | assert_cc_matches!( |
| 4420 | main_api.tokens, |
| 4421 | quote! { |
| 4422 | ... |
| 4423 | struct ... SomeStruct final { |
| 4424 | ... |
Lukasz Anforowicz | b9e515a | 2023-07-05 12:48:22 -0700 | [diff] [blame] | 4425 | static std::int32_t fn_taking_reference( |
Lukasz Anforowicz | aa2de7e | 2023-06-15 11:32:05 -0700 | [diff] [blame] | 4426 | std::int32_t const& [[clang::annotate_type("lifetime", "a")]] x); |
Lukasz Anforowicz | 56d94fc | 2023-02-17 12:23:20 -0800 | [diff] [blame] | 4427 | ... |
| 4428 | }; |
| 4429 | ... |
| 4430 | } |
| 4431 | ); |
Lukasz Anforowicz | aa2de7e | 2023-06-15 11:32:05 -0700 | [diff] [blame] | 4432 | assert_cc_matches!( |
Lukasz Anforowicz | 73a2c0d | 2023-03-22 14:07:00 -0700 | [diff] [blame] | 4433 | result.cc_details.tokens, |
Lukasz Anforowicz | aa2de7e | 2023-06-15 11:32:05 -0700 | [diff] [blame] | 4434 | 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 Anforowicz | 56d94fc | 2023-02-17 12:23:20 -0800 | [diff] [blame] | 4444 | ); |
Lukasz Anforowicz | aa2de7e | 2023-06-15 11:32:05 -0700 | [diff] [blame] | 4445 | 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 Anforowicz | 56d94fc | 2023-02-17 12:23:20 -0800 | [diff] [blame] | 4454 | }); |
| 4455 | } |
| 4456 | |
| 4457 | #[test] |
Lukasz Anforowicz | ed96fcb | 2023-06-12 11:39:45 -0700 | [diff] [blame] | 4458 | 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 Anforowicz | ae1ae17 | 2023-07-05 12:11:24 -0700 | [diff] [blame] | 4494 | 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 Anforowicz | b9e515a | 2023-07-05 12:48:22 -0700 | [diff] [blame] | 4504 | float into_f32() &&; |
Lukasz Anforowicz | ae1ae17 | 2023-07-05 12:11:24 -0700 | [diff] [blame] | 4505 | ... |
| 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 Anforowicz | ed96fcb | 2023-06-12 11:39:45 -0700 | [diff] [blame] | 4533 | #[test] |
Lukasz Anforowicz | ae1ae17 | 2023-07-05 12:11:24 -0700 | [diff] [blame] | 4534 | fn test_format_item_method_taking_self_by_value_implicit_type() { |
Lukasz Anforowicz | 56d94fc | 2023-02-17 12:23:20 -0800 | [diff] [blame] | 4535 | 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 Anforowicz | ae1ae17 | 2023-07-05 12:11:24 -0700 | [diff] [blame] | 4544 | 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 Anforowicz | 56d94fc | 2023-02-17 12:23:20 -0800 | [diff] [blame] | 4560 | } |
Lukasz Anforowicz | ae1ae17 | 2023-07-05 12:11:24 -0700 | [diff] [blame] | 4561 | "#; |
| 4562 | test_format_item_method_taking_self_by_value(test_src); |
Lukasz Anforowicz | 56d94fc | 2023-02-17 12:23:20 -0800 | [diff] [blame] | 4563 | } |
| 4564 | |
Lukasz Anforowicz | ec4c8c7 | 2023-07-05 12:30:57 -0700 | [diff] [blame] | 4565 | 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 Anforowicz | ec4c8c7 | 2023-07-05 12:30:57 -0700 | [diff] [blame] | 4575 | 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 Anforowicz | 56d94fc | 2023-02-17 12:23:20 -0800 | [diff] [blame] | 4607 | #[test] |
Lukasz Anforowicz | ec4c8c7 | 2023-07-05 12:30:57 -0700 | [diff] [blame] | 4608 | fn test_format_item_method_taking_self_by_const_ref_implicit_type() { |
Lukasz Anforowicz | 56d94fc | 2023-02-17 12:23:20 -0800 | [diff] [blame] | 4609 | 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 Anforowicz | ec4c8c7 | 2023-07-05 12:30:57 -0700 | [diff] [blame] | 4618 | 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 Anforowicz | b9e515a | 2023-07-05 12:48:22 -0700 | [diff] [blame] | 4645 | void set_f32(float new_value) |
Lukasz Anforowicz | ec4c8c7 | 2023-07-05 12:30:57 -0700 | [diff] [blame] | 4646 | [[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 Anforowicz | 56d94fc | 2023-02-17 12:23:20 -0800 | [diff] [blame] | 4722 | test_format_item(test_src, "SomeStruct", |result| { |
Lukasz Anforowicz | 73a2c0d | 2023-03-22 14:07:00 -0700 | [diff] [blame] | 4723 | let result = result.unwrap().unwrap(); |
| 4724 | let main_api = &result.main_api; |
Lukasz Anforowicz | 56d94fc | 2023-02-17 12:23:20 -0800 | [diff] [blame] | 4725 | let unsupported_msg = "Error generating bindings for `SomeStruct::get_f32` \ |
Lukasz Anforowicz | ec4c8c7 | 2023-07-05 12:30:57 -0700 | [diff] [blame] | 4726 | defined at <crubit_unittests.rs>;l=7: \ |
| 4727 | Error handling parameter #0: \ |
| 4728 | Generic types are not supported yet (b/259749095)"; |
Lukasz Anforowicz | 56d94fc | 2023-02-17 12:23:20 -0800 | [diff] [blame] | 4729 | 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 Anforowicz | 73a2c0d | 2023-03-22 14:07:00 -0700 | [diff] [blame] | 4741 | assert_cc_not_matches!(result.cc_details.tokens, quote! { SomeStruct::get_f32 },); |
| 4742 | assert_rs_not_matches!(result.rs_details, quote! { get_f32 },); |
Lukasz Anforowicz | 56d94fc | 2023-02-17 12:23:20 -0800 | [diff] [blame] | 4743 | }); |
| 4744 | } |
| 4745 | |
| 4746 | #[test] |
Lukasz Anforowicz | ae1ae17 | 2023-07-05 12:11:24 -0700 | [diff] [blame] | 4747 | 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 Anforowicz | 56d94fc | 2023-02-17 12:23:20 -0800 | [diff] [blame] | 4766 | 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 Anforowicz | 73a2c0d | 2023-03-22 14:07:00 -0700 | [diff] [blame] | 4778 | assert_cc_not_matches!(result.cc_details.tokens, quote! { SomeStruct::set_f32 },); |
| 4779 | assert_rs_not_matches!(result.rs_details, quote! { set_f32 },); |
Lukasz Anforowicz | 56d94fc | 2023-02-17 12:23:20 -0800 | [diff] [blame] | 4780 | }); |
| 4781 | } |
| 4782 | |
| 4783 | #[test] |
Lukasz Anforowicz | 9924c43 | 2023-04-04 12:51:22 -0700 | [diff] [blame] | 4784 | 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 Anforowicz | b9e515a | 2023-07-05 12:48:22 -0700 | [diff] [blame] | 4800 | Point(); |
Lukasz Anforowicz | 9924c43 | 2023-04-04 12:51:22 -0700 | [diff] [blame] | 4801 | ... |
| 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 Anforowicz | b9e515a | 2023-07-05 12:48:22 -0700 | [diff] [blame] | 4811 | inline Point::Point() { |
Lukasz Anforowicz | 9924c43 | 2023-04-04 12:51:22 -0700 | [diff] [blame] | 4812 | ...(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 Anforowicz | 901161a | 2023-06-01 15:39:58 -0700 | [diff] [blame] | 4831 | 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 Anforowicz | f18a857 | 2023-06-06 08:42:14 -0700 | [diff] [blame] | 4857 | // Trivial copy doesn't require any C++ details except `static_assert`s. |
Lukasz Anforowicz | 901161a | 2023-06-01 15:39:58 -0700 | [diff] [blame] | 4858 | 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 Anforowicz | f18a857 | 2023-06-06 08:42:14 -0700 | [diff] [blame] | 4863 | 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 Anforowicz | 901161a | 2023-06-01 15:39:58 -0700 | [diff] [blame] | 4872 | assert_rs_not_matches!(result.rs_details, quote! { Copy }); |
| 4873 | assert_rs_not_matches!(result.rs_details, quote! { copy }); |
| 4874 | }); |
| 4875 | } |
| 4876 | |
Lukasz Anforowicz | 1b665a4 | 2023-06-20 13:04:54 -0700 | [diff] [blame] | 4877 | /// 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 Anforowicz | 901161a | 2023-06-01 15:39:58 -0700 | [diff] [blame] | 4883 | #[test] |
| 4884 | fn test_format_item_struct_with_clone_trait() { |
| 4885 | let test_src = r#" |
Lukasz Anforowicz | 901161a | 2023-06-01 15:39:58 -0700 | [diff] [blame] | 4886 | pub struct Point(i32, i32); |
Lukasz Anforowicz | 1b665a4 | 2023-06-20 13:04:54 -0700 | [diff] [blame] | 4887 | impl Clone for Point { |
| 4888 | fn clone(&self) -> Self { |
| 4889 | unimplemented!() |
| 4890 | } |
| 4891 | } |
Lukasz Anforowicz | 901161a | 2023-06-01 15:39:58 -0700 | [diff] [blame] | 4892 | "#; |
Lukasz Anforowicz | 901161a | 2023-06-01 15:39:58 -0700 | [diff] [blame] | 4893 | 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 Anforowicz | 1b665a4 | 2023-06-20 13:04:54 -0700 | [diff] [blame] | 4904 | __COMMENT__ "Clone::clone" |
Lukasz Anforowicz | b9e515a | 2023-07-05 12:48:22 -0700 | [diff] [blame] | 4905 | Point(const Point&); |
Lukasz Anforowicz | 901161a | 2023-06-01 15:39:58 -0700 | [diff] [blame] | 4906 | |
Lukasz Anforowicz | 1b665a4 | 2023-06-20 13:04:54 -0700 | [diff] [blame] | 4907 | __COMMENT__ "Clone::clone_from" |
Lukasz Anforowicz | b9e515a | 2023-07-05 12:48:22 -0700 | [diff] [blame] | 4908 | Point& operator=(const Point&); |
Lukasz Anforowicz | 901161a | 2023-06-01 15:39:58 -0700 | [diff] [blame] | 4909 | ... |
| 4910 | }; |
| 4911 | } |
| 4912 | ); |
Lukasz Anforowicz | 1b665a4 | 2023-06-20 13:04:54 -0700 | [diff] [blame] | 4913 | 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 Anforowicz | b9e515a | 2023-07-05 12:48:22 -0700 | [diff] [blame] | 4928 | inline Point::Point(const Point& other) { |
Lukasz Anforowicz | 1b665a4 | 2023-06-20 13:04:54 -0700 | [diff] [blame] | 4929 | __crubit_internal::...(other, this); |
| 4930 | } |
Lukasz Anforowicz | b9e515a | 2023-07-05 12:48:22 -0700 | [diff] [blame] | 4931 | inline Point& Point::operator=(const Point& other) { |
Lukasz Anforowicz | 1b665a4 | 2023-06-20 13:04:54 -0700 | [diff] [blame] | 4932 | 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 Anforowicz | 901161a | 2023-06-01 15:39:58 -0700 | [diff] [blame] | 4962 | }); |
| 4963 | } |
| 4964 | |
| 4965 | #[test] |
Lukasz Anforowicz | e465b9e | 2023-02-06 15:00:25 -0800 | [diff] [blame] | 4966 | fn test_format_item_unsupported_struct_with_name_that_is_reserved_keyword() { |
Lukasz Anforowicz | 7589483 | 2022-11-22 18:25:28 -0800 | [diff] [blame] | 4967 | 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 Anforowicz | e465b9e | 2023-02-06 15:00:25 -0800 | [diff] [blame] | 4974 | test_format_item(test_src, "reinterpret_cast", |result| { |
Lukasz Anforowicz | 4c19ad9 | 2022-12-16 15:23:14 -0800 | [diff] [blame] | 4975 | let err = result.unwrap_err(); |
Lukasz Anforowicz | 7589483 | 2022-11-22 18:25:28 -0800 | [diff] [blame] | 4976 | 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 Anforowicz | 672c515 | 2023-03-15 16:23:00 -0700 | [diff] [blame] | 4986 | fn test_format_item_struct_with_unsupported_field_type() { |
Lukasz Anforowicz | 2eb2429 | 2023-03-14 09:38:21 -0700 | [diff] [blame] | 4987 | let test_src = r#" |
| 4988 | pub struct SomeStruct { |
Lukasz Anforowicz | 672c515 | 2023-03-15 16:23:00 -0700 | [diff] [blame] | 4989 | pub successful_field: i32, |
| 4990 | pub unsupported_field: Option<[i32; 3]>, |
Lukasz Anforowicz | 2eb2429 | 2023-03-14 09:38:21 -0700 | [diff] [blame] | 4991 | } |
| 4992 | "#; |
| 4993 | test_format_item(test_src, "SomeStruct", |result| { |
Lukasz Anforowicz | 73a2c0d | 2023-03-22 14:07:00 -0700 | [diff] [blame] | 4994 | let result = result.unwrap().unwrap(); |
| 4995 | let main_api = &result.main_api; |
Lukasz Anforowicz | 672c515 | 2023-03-15 16:23:00 -0700 | [diff] [blame] | 4996 | let broken_field_msg = "Field type has been replaced with a blob of bytes: \ |
Lukasz Anforowicz | 2eb2429 | 2023-03-14 09:38:21 -0700 | [diff] [blame] | 4997 | Generic types are not supported yet (b/259749095)"; |
Lukasz Anforowicz | 2eb2429 | 2023-03-14 09:38:21 -0700 | [diff] [blame] | 4998 | assert_cc_matches!( |
| 4999 | main_api.tokens, |
| 5000 | quote! { |
| 5001 | ... |
| 5002 | struct ... SomeStruct final { |
| 5003 | ... |
| 5004 | private: |
| 5005 | __COMMENT__ #broken_field_msg |
Lukasz Anforowicz | 672c515 | 2023-03-15 16:23:00 -0700 | [diff] [blame] | 5006 | unsigned char unsupported_field[16]; |
Lukasz Anforowicz | 68c8882 | 2023-06-01 16:06:15 -0700 | [diff] [blame] | 5007 | public: |
Lukasz Anforowicz | 8250222 | 2023-06-23 09:27:14 -0700 | [diff] [blame] | 5008 | union { ... std::int32_t successful_field; }; |
Lukasz Anforowicz | 68c8882 | 2023-06-01 16:06:15 -0700 | [diff] [blame] | 5009 | private: |
Lukasz Anforowicz | b9e515a | 2023-07-05 12:48:22 -0700 | [diff] [blame] | 5010 | static void __crubit_field_offset_assertions(); |
Lukasz Anforowicz | 2eb2429 | 2023-03-14 09:38:21 -0700 | [diff] [blame] | 5011 | }; |
| 5012 | ... |
| 5013 | } |
| 5014 | ); |
| 5015 | assert_cc_matches!( |
Lukasz Anforowicz | 73a2c0d | 2023-03-22 14:07:00 -0700 | [diff] [blame] | 5016 | result.cc_details.tokens, |
Lukasz Anforowicz | 2eb2429 | 2023-03-14 09:38:21 -0700 | [diff] [blame] | 5017 | quote! { |
Lukasz Anforowicz | 672c515 | 2023-03-15 16:23:00 -0700 | [diff] [blame] | 5018 | static_assert(sizeof(SomeStruct) == 20, ...); |
Lukasz Anforowicz | 2eb2429 | 2023-03-14 09:38:21 -0700 | [diff] [blame] | 5019 | static_assert(alignof(SomeStruct) == 4, ...); |
Lukasz Anforowicz | e0a778c | 2023-06-06 09:00:32 -0700 | [diff] [blame] | 5020 | static_assert(std::is_trivially_move_constructible_v<SomeStruct>); |
| 5021 | static_assert(std::is_trivially_move_assignable_v<SomeStruct>); |
Lukasz Anforowicz | 672c515 | 2023-03-15 16:23:00 -0700 | [diff] [blame] | 5022 | 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 Anforowicz | 2eb2429 | 2023-03-14 09:38:21 -0700 | [diff] [blame] | 5026 | } |
| 5027 | ); |
| 5028 | assert_rs_matches!( |
Lukasz Anforowicz | 73a2c0d | 2023-03-22 14:07:00 -0700 | [diff] [blame] | 5029 | result.rs_details, |
Lukasz Anforowicz | 2eb2429 | 2023-03-14 09:38:21 -0700 | [diff] [blame] | 5030 | quote! { |
Lukasz Anforowicz | 672c515 | 2023-03-15 16:23:00 -0700 | [diff] [blame] | 5031 | const _: () = assert!(::std::mem::size_of::<::rust_out::SomeStruct>() == 20); |
Lukasz Anforowicz | 2eb2429 | 2023-03-14 09:38:21 -0700 | [diff] [blame] | 5032 | const _: () = assert!(::std::mem::align_of::<::rust_out::SomeStruct>() == 4); |
Lukasz Anforowicz | 672c515 | 2023-03-15 16:23:00 -0700 | [diff] [blame] | 5033 | 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 Anforowicz | 2eb2429 | 2023-03-14 09:38:21 -0700 | [diff] [blame] | 5037 | } |
| 5038 | ); |
| 5039 | }); |
| 5040 | } |
| 5041 | |
Lukasz Anforowicz | ed96fcb | 2023-06-12 11:39:45 -0700 | [diff] [blame] | 5042 | /// 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 Anforowicz | aa2de7e | 2023-06-15 11:32:05 -0700 | [diff] [blame] | 5065 | Can't format `&'static i32`, because references \ |
| 5066 | are only supported in function parameter types and \ |
| 5067 | return types (b/286256327)"; |
Lukasz Anforowicz | ed96fcb | 2023-06-12 11:39:45 -0700 | [diff] [blame] | 5068 | 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 Anforowicz | 2eb2429 | 2023-03-14 09:38:21 -0700 | [diff] [blame] | 5081 | #[test] |
Lukasz Anforowicz | 35f7374 | 2023-07-07 09:18:26 -0700 | [diff] [blame] | 5082 | fn test_format_item_unsupported_struct_with_custom_drop_impl_and_no_default_impl() { |
Lukasz Anforowicz | 7589483 | 2022-11-22 18:25:28 -0800 | [diff] [blame] | 5083 | 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 Anforowicz | e465b9e | 2023-02-06 15:00:25 -0800 | [diff] [blame] | 5093 | test_format_item(test_src, "StructWithCustomDropImpl", |result| { |
Lukasz Anforowicz | 4c19ad9 | 2022-12-16 15:23:14 -0800 | [diff] [blame] | 5094 | let err = result.unwrap_err(); |
Lukasz Anforowicz | 7589483 | 2022-11-22 18:25:28 -0800 | [diff] [blame] | 5095 | assert_eq!(err, "`Drop` trait and \"drop glue\" are not supported yet (b/258251148)"); |
| 5096 | }); |
| 5097 | } |
| 5098 | |
| 5099 | #[test] |
Lukasz Anforowicz | 35f7374 | 2023-07-07 09:18:26 -0700 | [diff] [blame] | 5100 | fn test_format_item_unsupported_struct_with_custom_drop_glue_and_no_default_impl() { |
Lukasz Anforowicz | 7589483 | 2022-11-22 18:25:28 -0800 | [diff] [blame] | 5101 | 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 Anforowicz | e465b9e | 2023-02-06 15:00:25 -0800 | [diff] [blame] | 5117 | test_format_item(test_src, "StructRequiringCustomDropGlue", |result| { |
Lukasz Anforowicz | 4c19ad9 | 2022-12-16 15:23:14 -0800 | [diff] [blame] | 5118 | let err = result.unwrap_err(); |
Lukasz Anforowicz | 7589483 | 2022-11-22 18:25:28 -0800 | [diff] [blame] | 5119 | assert_eq!(err, "`Drop` trait and \"drop glue\" are not supported yet (b/258251148)"); |
| 5120 | }); |
| 5121 | } |
| 5122 | |
Lukasz Anforowicz | 35f7374 | 2023-07-07 09:18:26 -0700 | [diff] [blame] | 5123 | #[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 Anforowicz | 4c19ad9 | 2022-12-16 15:23:14 -0800 | [diff] [blame] | 5168 | /// 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 Anforowicz | 7589483 | 2022-11-22 18:25:28 -0800 | [diff] [blame] | 5171 | #[test] |
Lukasz Anforowicz | 2eb2429 | 2023-03-14 09:38:21 -0700 | [diff] [blame] | 5172 | fn test_format_item_unsupported_struct_zero_sized_type_with_no_fields() { |
Lukasz Anforowicz | 7589483 | 2022-11-22 18:25:28 -0800 | [diff] [blame] | 5173 | 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 Anforowicz | e465b9e | 2023-02-06 15:00:25 -0800 | [diff] [blame] | 5179 | test_format_item(test_src, name, |result| { |
Lukasz Anforowicz | 4c19ad9 | 2022-12-16 15:23:14 -0800 | [diff] [blame] | 5180 | let err = result.unwrap_err(); |
Lukasz Anforowicz | 7589483 | 2022-11-22 18:25:28 -0800 | [diff] [blame] | 5181 | assert_eq!(err, "Zero-sized types (ZSTs) are not supported (b/258259459)"); |
| 5182 | }); |
| 5183 | } |
| 5184 | } |
| 5185 | |
Lukasz Anforowicz | 2eb2429 | 2023-03-14 09:38:21 -0700 | [diff] [blame] | 5186 | #[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 Jeanpierre | 9e15d0b | 2023-04-06 13:18:22 -0700 | [diff] [blame] | 5201 | #[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 Anforowicz | 68c8882 | 2023-06-01 16:06:15 -0700 | [diff] [blame] | 5226 | private: |
Devin Jeanpierre | 9e15d0b | 2023-04-06 13:18:22 -0700 | [diff] [blame] | 5227 | __COMMENT__ #broken_field_msg |
| 5228 | [[no_unique_address]] struct{} zst2; |
Lukasz Anforowicz | 68c8882 | 2023-06-01 16:06:15 -0700 | [diff] [blame] | 5229 | public: |
Lukasz Anforowicz | 8250222 | 2023-06-23 09:27:14 -0700 | [diff] [blame] | 5230 | union { ... std::int32_t successful_field; }; |
Lukasz Anforowicz | 68c8882 | 2023-06-01 16:06:15 -0700 | [diff] [blame] | 5231 | private: |
Lukasz Anforowicz | b9e515a | 2023-07-05 12:48:22 -0700 | [diff] [blame] | 5232 | static void __crubit_field_offset_assertions(); |
Devin Jeanpierre | 9e15d0b | 2023-04-06 13:18:22 -0700 | [diff] [blame] | 5233 | }; |
| 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 Anforowicz | e0a778c | 2023-06-06 09:00:32 -0700 | [diff] [blame] | 5242 | static_assert(std::is_trivially_move_constructible_v<SomeStruct>); |
| 5243 | static_assert(std::is_trivially_move_assignable_v<SomeStruct>); |
Devin Jeanpierre | 9e15d0b | 2023-04-06 13:18:22 -0700 | [diff] [blame] | 5244 | 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 Anforowicz | 74e090d | 2023-04-25 15:14:14 -0700 | [diff] [blame] | 5267 | #[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 Anforowicz | f4b3760 | 2023-06-01 16:16:34 -0700 | [diff] [blame] | 5283 | #[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 Anforowicz | 8250222 | 2023-06-23 09:27:14 -0700 | [diff] [blame] | 5311 | union { |
| 5312 | __COMMENT__ #comment_for_successful_field |
| 5313 | std::int32_t successful_field; |
| 5314 | }; |
Lukasz Anforowicz | f4b3760 | 2023-06-01 16:16:34 -0700 | [diff] [blame] | 5315 | private: |
Lukasz Anforowicz | b9e515a | 2023-07-05 12:48:22 -0700 | [diff] [blame] | 5316 | static void __crubit_field_offset_assertions(); |
Lukasz Anforowicz | f4b3760 | 2023-06-01 16:16:34 -0700 | [diff] [blame] | 5317 | }; |
| 5318 | ... |
| 5319 | } |
| 5320 | ); |
| 5321 | }); |
| 5322 | } |
| 5323 | |
Lukasz Anforowicz | 7589483 | 2022-11-22 18:25:28 -0800 | [diff] [blame] | 5324 | /// 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 Anforowicz | 32ad956 | 2023-03-10 14:48:25 -0800 | [diff] [blame] | 5328 | fn test_format_item_enum_with_only_discriminant_items() { |
Lukasz Anforowicz | 7589483 | 2022-11-22 18:25:28 -0800 | [diff] [blame] | 5329 | 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 Anforowicz | e465b9e | 2023-02-06 15:00:25 -0800 | [diff] [blame] | 5339 | test_format_item(test_src, "SomeEnum", |result| { |
Lukasz Anforowicz | 73a2c0d | 2023-03-22 14:07:00 -0700 | [diff] [blame] | 5340 | let result = result.unwrap().unwrap(); |
| 5341 | let main_api = &result.main_api; |
Lukasz Anforowicz | 672c515 | 2023-03-15 16:23:00 -0700 | [diff] [blame] | 5342 | let no_fields_msg = "Field type has been replaced with a blob of bytes: \ |
Lukasz Anforowicz | 2eb2429 | 2023-03-14 09:38:21 -0700 | [diff] [blame] | 5343 | No support for bindings of individual fields of \ |
| 5344 | `union` (b/272801632) or `enum`"; |
Lukasz Anforowicz | 32ad956 | 2023-03-10 14:48:25 -0800 | [diff] [blame] | 5345 | assert_cc_matches!( |
| 5346 | main_api.tokens, |
| 5347 | quote! { |
| 5348 | ... |
Devin Jeanpierre | 64ac8ad | 2023-05-30 17:22:55 -0700 | [diff] [blame] | 5349 | struct CRUBIT_INTERNAL_RUST_TYPE(...) alignas(1) SomeEnum final { |
Lukasz Anforowicz | 32ad956 | 2023-03-10 14:48:25 -0800 | [diff] [blame] | 5350 | public: |
Lukasz Anforowicz | 9924c43 | 2023-04-04 12:51:22 -0700 | [diff] [blame] | 5351 | __COMMENT__ "`SomeEnum` doesn't implement the `Default` trait" |
Lukasz Anforowicz | 32ad956 | 2023-03-10 14:48:25 -0800 | [diff] [blame] | 5352 | SomeEnum() = delete; |
| 5353 | |
Lukasz Anforowicz | 57c7014 | 2023-07-07 08:35:04 -0700 | [diff] [blame] | 5354 | __COMMENT__ "No custom `Drop` impl and no custom \"drop glue\" required" |
| 5355 | ~SomeEnum() = default; |
Lukasz Anforowicz | 32ad956 | 2023-03-10 14:48:25 -0800 | [diff] [blame] | 5356 | SomeEnum(SomeEnum&&) = default; |
Lukasz Anforowicz | c12fc18 | 2023-06-06 08:57:47 -0700 | [diff] [blame] | 5357 | SomeEnum& operator=(SomeEnum&&) = default; |
Lukasz Anforowicz | 32ad956 | 2023-03-10 14:48:25 -0800 | [diff] [blame] | 5358 | |
Lukasz Anforowicz | 57c7014 | 2023-07-07 08:35:04 -0700 | [diff] [blame] | 5359 | __COMMENT__ "`SomeEnum` doesn't implement the `Clone` trait" |
| 5360 | SomeEnum(const SomeEnum&) = delete; |
| 5361 | SomeEnum& operator=(const SomeEnum&) = delete; |
Lukasz Anforowicz | 32ad956 | 2023-03-10 14:48:25 -0800 | [diff] [blame] | 5362 | private: |
Lukasz Anforowicz | 2eb2429 | 2023-03-14 09:38:21 -0700 | [diff] [blame] | 5363 | __COMMENT__ #no_fields_msg |
| 5364 | unsigned char __opaque_blob_of_bytes[1]; |
Lukasz Anforowicz | 68c8882 | 2023-06-01 16:06:15 -0700 | [diff] [blame] | 5365 | private: |
Lukasz Anforowicz | b9e515a | 2023-07-05 12:48:22 -0700 | [diff] [blame] | 5366 | static void __crubit_field_offset_assertions(); |
Lukasz Anforowicz | 32ad956 | 2023-03-10 14:48:25 -0800 | [diff] [blame] | 5367 | }; |
| 5368 | } |
| 5369 | ); |
| 5370 | assert_cc_matches!( |
Lukasz Anforowicz | 73a2c0d | 2023-03-22 14:07:00 -0700 | [diff] [blame] | 5371 | result.cc_details.tokens, |
Lukasz Anforowicz | 32ad956 | 2023-03-10 14:48:25 -0800 | [diff] [blame] | 5372 | quote! { |
| 5373 | static_assert(sizeof(SomeEnum) == 1, ...); |
| 5374 | static_assert(alignof(SomeEnum) == 1, ...); |
| 5375 | } |
| 5376 | ); |
| 5377 | assert_rs_matches!( |
Lukasz Anforowicz | 73a2c0d | 2023-03-22 14:07:00 -0700 | [diff] [blame] | 5378 | result.rs_details, |
Lukasz Anforowicz | 32ad956 | 2023-03-10 14:48:25 -0800 | [diff] [blame] | 5379 | 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 Anforowicz | 7589483 | 2022-11-22 18:25:28 -0800 | [diff] [blame] | 5384 | }); |
| 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 Anforowicz | 32ad956 | 2023-03-10 14:48:25 -0800 | [diff] [blame] | 5390 | fn test_format_item_enum_with_tuple_and_struct_items() { |
Lukasz Anforowicz | 7589483 | 2022-11-22 18:25:28 -0800 | [diff] [blame] | 5391 | 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 Anforowicz | e465b9e | 2023-02-06 15:00:25 -0800 | [diff] [blame] | 5400 | test_format_item(test_src, "Point", |result| { |
Lukasz Anforowicz | 73a2c0d | 2023-03-22 14:07:00 -0700 | [diff] [blame] | 5401 | let result = result.unwrap().unwrap(); |
| 5402 | let main_api = &result.main_api; |
Lukasz Anforowicz | 672c515 | 2023-03-15 16:23:00 -0700 | [diff] [blame] | 5403 | let no_fields_msg = "Field type has been replaced with a blob of bytes: \ |
Lukasz Anforowicz | 2eb2429 | 2023-03-14 09:38:21 -0700 | [diff] [blame] | 5404 | No support for bindings of individual fields of \ |
| 5405 | `union` (b/272801632) or `enum`"; |
Lukasz Anforowicz | 32ad956 | 2023-03-10 14:48:25 -0800 | [diff] [blame] | 5406 | assert_cc_matches!( |
| 5407 | main_api.tokens, |
| 5408 | quote! { |
| 5409 | ... |
Devin Jeanpierre | 64ac8ad | 2023-05-30 17:22:55 -0700 | [diff] [blame] | 5410 | struct CRUBIT_INTERNAL_RUST_TYPE(...) alignas(4) Point final { |
Lukasz Anforowicz | 32ad956 | 2023-03-10 14:48:25 -0800 | [diff] [blame] | 5411 | public: |
Lukasz Anforowicz | 9924c43 | 2023-04-04 12:51:22 -0700 | [diff] [blame] | 5412 | __COMMENT__ "`Point` doesn't implement the `Default` trait" |
Lukasz Anforowicz | 32ad956 | 2023-03-10 14:48:25 -0800 | [diff] [blame] | 5413 | Point() = delete; |
| 5414 | |
Lukasz Anforowicz | 57c7014 | 2023-07-07 08:35:04 -0700 | [diff] [blame] | 5415 | __COMMENT__ "No custom `Drop` impl and no custom \"drop glue\" required" |
| 5416 | ~Point() = default; |
Lukasz Anforowicz | 32ad956 | 2023-03-10 14:48:25 -0800 | [diff] [blame] | 5417 | Point(Point&&) = default; |
Lukasz Anforowicz | c12fc18 | 2023-06-06 08:57:47 -0700 | [diff] [blame] | 5418 | Point& operator=(Point&&) = default; |
Lukasz Anforowicz | 32ad956 | 2023-03-10 14:48:25 -0800 | [diff] [blame] | 5419 | |
Lukasz Anforowicz | 57c7014 | 2023-07-07 08:35:04 -0700 | [diff] [blame] | 5420 | __COMMENT__ "`Point` doesn't implement the `Clone` trait" |
| 5421 | Point(const Point&) = delete; |
| 5422 | Point& operator=(const Point&) = delete; |
Lukasz Anforowicz | 32ad956 | 2023-03-10 14:48:25 -0800 | [diff] [blame] | 5423 | private: |
Lukasz Anforowicz | 2eb2429 | 2023-03-14 09:38:21 -0700 | [diff] [blame] | 5424 | __COMMENT__ #no_fields_msg |
| 5425 | unsigned char __opaque_blob_of_bytes[12]; |
Lukasz Anforowicz | 68c8882 | 2023-06-01 16:06:15 -0700 | [diff] [blame] | 5426 | private: |
Lukasz Anforowicz | b9e515a | 2023-07-05 12:48:22 -0700 | [diff] [blame] | 5427 | static void __crubit_field_offset_assertions(); |
Lukasz Anforowicz | 32ad956 | 2023-03-10 14:48:25 -0800 | [diff] [blame] | 5428 | }; |
| 5429 | } |
| 5430 | ); |
| 5431 | assert_cc_matches!( |
Lukasz Anforowicz | 73a2c0d | 2023-03-22 14:07:00 -0700 | [diff] [blame] | 5432 | result.cc_details.tokens, |
Lukasz Anforowicz | 32ad956 | 2023-03-10 14:48:25 -0800 | [diff] [blame] | 5433 | quote! { |
| 5434 | static_assert(sizeof(Point) == 12, ...); |
| 5435 | static_assert(alignof(Point) == 4, ...); |
| 5436 | } |
| 5437 | ); |
| 5438 | assert_rs_matches!( |
Lukasz Anforowicz | 73a2c0d | 2023-03-22 14:07:00 -0700 | [diff] [blame] | 5439 | result.rs_details, |
Lukasz Anforowicz | 32ad956 | 2023-03-10 14:48:25 -0800 | [diff] [blame] | 5440 | 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 Anforowicz | 7589483 | 2022-11-22 18:25:28 -0800 | [diff] [blame] | 5445 | }); |
| 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 Anforowicz | e465b9e | 2023-02-06 15:00:25 -0800 | [diff] [blame] | 5451 | fn test_format_item_unsupported_enum_zero_variants() { |
Lukasz Anforowicz | 7589483 | 2022-11-22 18:25:28 -0800 | [diff] [blame] | 5452 | let test_src = r#" |
| 5453 | pub enum ZeroVariantEnum {} |
| 5454 | "#; |
Lukasz Anforowicz | e465b9e | 2023-02-06 15:00:25 -0800 | [diff] [blame] | 5455 | test_format_item(test_src, "ZeroVariantEnum", |result| { |
Lukasz Anforowicz | 4c19ad9 | 2022-12-16 15:23:14 -0800 | [diff] [blame] | 5456 | let err = result.unwrap_err(); |
Lukasz Anforowicz | 32ad956 | 2023-03-10 14:48:25 -0800 | [diff] [blame] | 5457 | assert_eq!(err, "Zero-sized types (ZSTs) are not supported (b/258259459)"); |
Lukasz Anforowicz | 7589483 | 2022-11-22 18:25:28 -0800 | [diff] [blame] | 5458 | }); |
| 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 Anforowicz | e465b9e | 2023-02-06 15:00:25 -0800 | [diff] [blame] | 5464 | fn test_format_item_union() { |
Lukasz Anforowicz | 7589483 | 2022-11-22 18:25:28 -0800 | [diff] [blame] | 5465 | 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 Anforowicz | e465b9e | 2023-02-06 15:00:25 -0800 | [diff] [blame] | 5474 | test_format_item(test_src, "SomeUnion", |result| { |
Lukasz Anforowicz | 73a2c0d | 2023-03-22 14:07:00 -0700 | [diff] [blame] | 5475 | let result = result.unwrap().unwrap(); |
| 5476 | let main_api = &result.main_api; |
Lukasz Anforowicz | 672c515 | 2023-03-15 16:23:00 -0700 | [diff] [blame] | 5477 | let no_fields_msg = "Field type has been replaced with a blob of bytes: \ |
Lukasz Anforowicz | 2eb2429 | 2023-03-14 09:38:21 -0700 | [diff] [blame] | 5478 | No support for bindings of individual fields of \ |
| 5479 | `union` (b/272801632) or `enum`"; |
Lukasz Anforowicz | 7589483 | 2022-11-22 18:25:28 -0800 | [diff] [blame] | 5480 | assert_cc_matches!( |
Lukasz Anforowicz | 0b8a02f | 2023-02-10 15:55:40 -0800 | [diff] [blame] | 5481 | main_api.tokens, |
Lukasz Anforowicz | 7589483 | 2022-11-22 18:25:28 -0800 | [diff] [blame] | 5482 | quote! { |
Googler | 47fd957 | 2023-01-20 09:57:32 -0800 | [diff] [blame] | 5483 | ... |
Devin Jeanpierre | 64ac8ad | 2023-05-30 17:22:55 -0700 | [diff] [blame] | 5484 | union CRUBIT_INTERNAL_RUST_TYPE(...) alignas(8) SomeUnion final { |
Lukasz Anforowicz | 7589483 | 2022-11-22 18:25:28 -0800 | [diff] [blame] | 5485 | public: |
Lukasz Anforowicz | 9924c43 | 2023-04-04 12:51:22 -0700 | [diff] [blame] | 5486 | __COMMENT__ "`SomeUnion` doesn't implement the `Default` trait" |
Lukasz Anforowicz | 7589483 | 2022-11-22 18:25:28 -0800 | [diff] [blame] | 5487 | SomeUnion() = delete; |
| 5488 | |
Lukasz Anforowicz | 57c7014 | 2023-07-07 08:35:04 -0700 | [diff] [blame] | 5489 | __COMMENT__ "No custom `Drop` impl and no custom \"drop glue\" required" |
| 5490 | ~SomeUnion() = default; |
Lukasz Anforowicz | 7589483 | 2022-11-22 18:25:28 -0800 | [diff] [blame] | 5491 | SomeUnion(SomeUnion&&) = default; |
Lukasz Anforowicz | c12fc18 | 2023-06-06 08:57:47 -0700 | [diff] [blame] | 5492 | SomeUnion& operator=(SomeUnion&&) = default; |
Lukasz Anforowicz | 7589483 | 2022-11-22 18:25:28 -0800 | [diff] [blame] | 5493 | |
Lukasz Anforowicz | 57c7014 | 2023-07-07 08:35:04 -0700 | [diff] [blame] | 5494 | __COMMENT__ "`SomeUnion` doesn't implement the `Clone` trait" |
| 5495 | SomeUnion(const SomeUnion&) = delete; |
| 5496 | SomeUnion& operator=(const SomeUnion&) = delete; |
Lukasz Anforowicz | 7589483 | 2022-11-22 18:25:28 -0800 | [diff] [blame] | 5497 | private: |
Lukasz Anforowicz | 2eb2429 | 2023-03-14 09:38:21 -0700 | [diff] [blame] | 5498 | __COMMENT__ #no_fields_msg |
| 5499 | unsigned char __opaque_blob_of_bytes[8]; |
Lukasz Anforowicz | 68c8882 | 2023-06-01 16:06:15 -0700 | [diff] [blame] | 5500 | private: |
Lukasz Anforowicz | b9e515a | 2023-07-05 12:48:22 -0700 | [diff] [blame] | 5501 | static void __crubit_field_offset_assertions(); |
Lukasz Anforowicz | 7589483 | 2022-11-22 18:25:28 -0800 | [diff] [blame] | 5502 | }; |
Lukasz Anforowicz | 0b8a02f | 2023-02-10 15:55:40 -0800 | [diff] [blame] | 5503 | } |
| 5504 | ); |
| 5505 | assert_cc_matches!( |
Lukasz Anforowicz | 73a2c0d | 2023-03-22 14:07:00 -0700 | [diff] [blame] | 5506 | result.cc_details.tokens, |
Lukasz Anforowicz | 0b8a02f | 2023-02-10 15:55:40 -0800 | [diff] [blame] | 5507 | quote! { |
Lukasz Anforowicz | 7589483 | 2022-11-22 18:25:28 -0800 | [diff] [blame] | 5508 | static_assert(sizeof(SomeUnion) == 8, ...); |
| 5509 | static_assert(alignof(SomeUnion) == 8, ...); |
| 5510 | } |
| 5511 | ); |
| 5512 | assert_rs_matches!( |
Lukasz Anforowicz | 73a2c0d | 2023-03-22 14:07:00 -0700 | [diff] [blame] | 5513 | result.rs_details, |
Lukasz Anforowicz | 7589483 | 2022-11-22 18:25:28 -0800 | [diff] [blame] | 5514 | quote! { |
Lukasz Anforowicz | 8b475a4 | 2022-11-29 09:33:02 -0800 | [diff] [blame] | 5515 | const _: () = assert!(::std::mem::size_of::<::rust_out::SomeUnion>() == 8); |
| 5516 | const _: () = assert!(::std::mem::align_of::<::rust_out::SomeUnion>() == 8); |
Lukasz Anforowicz | 7589483 | 2022-11-22 18:25:28 -0800 | [diff] [blame] | 5517 | } |
| 5518 | ); |
| 5519 | }); |
| 5520 | } |
| 5521 | |
Lukasz Anforowicz | e433306 | 2022-10-17 14:47:53 -0700 | [diff] [blame] | 5522 | #[test] |
Lukasz Anforowicz | e465b9e | 2023-02-06 15:00:25 -0800 | [diff] [blame] | 5523 | fn test_format_item_doc_comments_union() { |
Googler | 04329a1 | 2022-12-02 00:56:33 -0800 | [diff] [blame] | 5524 | 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 Anforowicz | e465b9e | 2023-02-06 15:00:25 -0800 | [diff] [blame] | 5532 | test_format_item(test_src, "SomeUnionWithDocs", |result| { |
Lukasz Anforowicz | 73a2c0d | 2023-03-22 14:07:00 -0700 | [diff] [blame] | 5533 | let result = result.unwrap().unwrap(); |
| 5534 | let main_api = &result.main_api; |
Googler | 47fd957 | 2023-01-20 09:57:32 -0800 | [diff] [blame] | 5535 | let comment = " Doc for some union.\n\n\ |
| 5536 | Generated from: <crubit_unittests.rs>;l=3"; |
Googler | 04329a1 | 2022-12-02 00:56:33 -0800 | [diff] [blame] | 5537 | assert_cc_matches!( |
Lukasz Anforowicz | 0b8a02f | 2023-02-10 15:55:40 -0800 | [diff] [blame] | 5538 | main_api.tokens, |
Googler | 04329a1 | 2022-12-02 00:56:33 -0800 | [diff] [blame] | 5539 | quote! { |
| 5540 | __COMMENT__ #comment |
Lukasz Anforowicz | cc7a76b | 2023-02-28 14:19:42 -0800 | [diff] [blame] | 5541 | union ... SomeUnionWithDocs final { |
Googler | 04329a1 | 2022-12-02 00:56:33 -0800 | [diff] [blame] | 5542 | ... |
| 5543 | } |
| 5544 | ... |
| 5545 | } |
| 5546 | ); |
| 5547 | }); |
| 5548 | } |
| 5549 | |
| 5550 | #[test] |
Lukasz Anforowicz | e465b9e | 2023-02-06 15:00:25 -0800 | [diff] [blame] | 5551 | fn test_format_item_doc_comments_enum() { |
Googler | 04329a1 | 2022-12-02 00:56:33 -0800 | [diff] [blame] | 5552 | let test_src = r#" |
| 5553 | /** Doc for some enum. */ |
| 5554 | pub enum SomeEnumWithDocs { |
Lukasz Anforowicz | 32ad956 | 2023-03-10 14:48:25 -0800 | [diff] [blame] | 5555 | Kind1(i32), |
Googler | 04329a1 | 2022-12-02 00:56:33 -0800 | [diff] [blame] | 5556 | } |
| 5557 | "#; |
Lukasz Anforowicz | e465b9e | 2023-02-06 15:00:25 -0800 | [diff] [blame] | 5558 | test_format_item(test_src, "SomeEnumWithDocs", |result| { |
Lukasz Anforowicz | 73a2c0d | 2023-03-22 14:07:00 -0700 | [diff] [blame] | 5559 | let result = result.unwrap().unwrap(); |
| 5560 | let main_api = &result.main_api; |
Lukasz Anforowicz | 32ad956 | 2023-03-10 14:48:25 -0800 | [diff] [blame] | 5561 | 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 | ); |
Googler | 04329a1 | 2022-12-02 00:56:33 -0800 | [diff] [blame] | 5573 | }); |
| 5574 | } |
| 5575 | |
| 5576 | #[test] |
Lukasz Anforowicz | e465b9e | 2023-02-06 15:00:25 -0800 | [diff] [blame] | 5577 | fn test_format_item_doc_comments_struct() { |
Googler | 04329a1 | 2022-12-02 00:56:33 -0800 | [diff] [blame] | 5578 | let test_src = r#" |
| 5579 | #![allow(dead_code)] |
| 5580 | #[doc = "Doc for some struct."] |
| 5581 | pub struct SomeStructWithDocs { |
Lukasz Anforowicz | cc7a76b | 2023-02-28 14:19:42 -0800 | [diff] [blame] | 5582 | #[doc = "Doc for first field."] |
Googler | 04329a1 | 2022-12-02 00:56:33 -0800 | [diff] [blame] | 5583 | some_field : i32, |
| 5584 | } |
| 5585 | "#; |
Lukasz Anforowicz | e465b9e | 2023-02-06 15:00:25 -0800 | [diff] [blame] | 5586 | test_format_item(test_src, "SomeStructWithDocs", |result| { |
Lukasz Anforowicz | 73a2c0d | 2023-03-22 14:07:00 -0700 | [diff] [blame] | 5587 | let result = result.unwrap().unwrap(); |
| 5588 | let main_api = &result.main_api; |
Googler | 47fd957 | 2023-01-20 09:57:32 -0800 | [diff] [blame] | 5589 | let comment = "Doc for some struct.\n\n\ |
| 5590 | Generated from: <crubit_unittests.rs>;l=4"; |
Googler | 04329a1 | 2022-12-02 00:56:33 -0800 | [diff] [blame] | 5591 | assert_cc_matches!( |
Lukasz Anforowicz | 0b8a02f | 2023-02-10 15:55:40 -0800 | [diff] [blame] | 5592 | main_api.tokens, |
Googler | 04329a1 | 2022-12-02 00:56:33 -0800 | [diff] [blame] | 5593 | quote! { |
| 5594 | __COMMENT__ #comment |
| 5595 | struct ... SomeStructWithDocs final { |
| 5596 | ... |
| 5597 | } |
| 5598 | ... |
| 5599 | } |
| 5600 | ); |
| 5601 | }); |
| 5602 | } |
| 5603 | |
| 5604 | #[test] |
Lukasz Anforowicz | e465b9e | 2023-02-06 15:00:25 -0800 | [diff] [blame] | 5605 | fn test_format_item_doc_comments_tuple_struct() { |
Googler | 04329a1 | 2022-12-02 00:56:33 -0800 | [diff] [blame] | 5606 | let test_src = r#" |
| 5607 | /// Doc for some tuple struct. |
| 5608 | pub struct SomeTupleStructWithDocs(i32); |
| 5609 | "#; |
Lukasz Anforowicz | e465b9e | 2023-02-06 15:00:25 -0800 | [diff] [blame] | 5610 | test_format_item(test_src, "SomeTupleStructWithDocs", |result| { |
Lukasz Anforowicz | 73a2c0d | 2023-03-22 14:07:00 -0700 | [diff] [blame] | 5611 | let result = result.unwrap().unwrap(); |
| 5612 | let main_api = &result.main_api; |
Googler | 47fd957 | 2023-01-20 09:57:32 -0800 | [diff] [blame] | 5613 | let comment = " Doc for some tuple struct.\n\n\ |
| 5614 | Generated from: <crubit_unittests.rs>;l=3"; |
Googler | 04329a1 | 2022-12-02 00:56:33 -0800 | [diff] [blame] | 5615 | assert_cc_matches!( |
Lukasz Anforowicz | 0b8a02f | 2023-02-10 15:55:40 -0800 | [diff] [blame] | 5616 | main_api.tokens, |
Googler | 04329a1 | 2022-12-02 00:56:33 -0800 | [diff] [blame] | 5617 | quote! { |
| 5618 | __COMMENT__ #comment |
| 5619 | struct ... SomeTupleStructWithDocs final { |
| 5620 | ... |
| 5621 | } |
| 5622 | ... |
| 5623 | }, |
| 5624 | ); |
| 5625 | }); |
| 5626 | } |
| 5627 | |
| 5628 | #[test] |
Lukasz Anforowicz | e465b9e | 2023-02-06 15:00:25 -0800 | [diff] [blame] | 5629 | fn test_format_item_source_loc_macro_rules() { |
Googler | 47fd957 | 2023-01-20 09:57:32 -0800 | [diff] [blame] | 5630 | 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 Anforowicz | e465b9e | 2023-02-06 15:00:25 -0800 | [diff] [blame] | 5640 | test_format_item(test_src, "SomeTupleStructMacroForTesingSourceLoc", |result| { |
Lukasz Anforowicz | 73a2c0d | 2023-03-22 14:07:00 -0700 | [diff] [blame] | 5641 | let result = result.unwrap().unwrap(); |
| 5642 | let main_api = &result.main_api; |
Googler | 47fd957 | 2023-01-20 09:57:32 -0800 | [diff] [blame] | 5643 | let source_loc_comment = " Some doc on SomeTupleStructMacroForTesingSourceLoc.\n\n\ |
| 5644 | Generated from: <crubit_unittests.rs>;l=5"; |
| 5645 | assert_cc_matches!( |
Lukasz Anforowicz | 0b8a02f | 2023-02-10 15:55:40 -0800 | [diff] [blame] | 5646 | main_api.tokens, |
Googler | 47fd957 | 2023-01-20 09:57:32 -0800 | [diff] [blame] | 5647 | quote! { |
| 5648 | __COMMENT__ #source_loc_comment |
| 5649 | struct ... SomeTupleStructMacroForTesingSourceLoc final { |
| 5650 | ... |
| 5651 | } |
| 5652 | ... |
| 5653 | }, |
| 5654 | ); |
| 5655 | }); |
| 5656 | } |
| 5657 | |
| 5658 | #[test] |
Lukasz Anforowicz | e465b9e | 2023-02-06 15:00:25 -0800 | [diff] [blame] | 5659 | fn test_format_item_source_loc_with_no_doc_comment() { |
Googler | 47fd957 | 2023-01-20 09:57:32 -0800 | [diff] [blame] | 5660 | let test_src = r#" |
| 5661 | pub struct SomeTupleStructWithNoDocComment(i32); |
| 5662 | "#; |
Lukasz Anforowicz | e465b9e | 2023-02-06 15:00:25 -0800 | [diff] [blame] | 5663 | test_format_item(test_src, "SomeTupleStructWithNoDocComment", |result| { |
Lukasz Anforowicz | 73a2c0d | 2023-03-22 14:07:00 -0700 | [diff] [blame] | 5664 | let result = result.unwrap().unwrap(); |
| 5665 | let main_api = &result.main_api; |
Googler | 47fd957 | 2023-01-20 09:57:32 -0800 | [diff] [blame] | 5666 | let comment = "Generated from: <crubit_unittests.rs>;l=2"; |
| 5667 | assert_cc_matches!( |
Lukasz Anforowicz | 0b8a02f | 2023-02-10 15:55:40 -0800 | [diff] [blame] | 5668 | main_api.tokens, |
Googler | 47fd957 | 2023-01-20 09:57:32 -0800 | [diff] [blame] | 5669 | quote! { |
| 5670 | __COMMENT__ #comment |
| 5671 | struct ... SomeTupleStructWithNoDocComment final { |
| 5672 | ... |
| 5673 | } |
| 5674 | ... |
| 5675 | }, |
| 5676 | ); |
| 5677 | }); |
| 5678 | } |
| 5679 | |
| 5680 | #[test] |
Lukasz Anforowicz | e465b9e | 2023-02-06 15:00:25 -0800 | [diff] [blame] | 5681 | fn test_format_item_unsupported_static_value() { |
Lukasz Anforowicz | e433306 | 2022-10-17 14:47:53 -0700 | [diff] [blame] | 5682 | let test_src = r#" |
Lukasz Anforowicz | 7589483 | 2022-11-22 18:25:28 -0800 | [diff] [blame] | 5683 | #[no_mangle] |
| 5684 | pub static STATIC_VALUE: i32 = 42; |
Lukasz Anforowicz | e433306 | 2022-10-17 14:47:53 -0700 | [diff] [blame] | 5685 | "#; |
Lukasz Anforowicz | e465b9e | 2023-02-06 15:00:25 -0800 | [diff] [blame] | 5686 | test_format_item(test_src, "STATIC_VALUE", |result| { |
Lukasz Anforowicz | 4c19ad9 | 2022-12-16 15:23:14 -0800 | [diff] [blame] | 5687 | let err = result.unwrap_err(); |
Lukasz Anforowicz | 7589483 | 2022-11-22 18:25:28 -0800 | [diff] [blame] | 5688 | assert_eq!(err, "Unsupported rustc_hir::hir::ItemKind: static item"); |
Lukasz Anforowicz | 8a83341 | 2022-10-14 14:44:13 -0700 | [diff] [blame] | 5689 | }); |
| 5690 | } |
| 5691 | |
Lukasz Anforowicz | 08b8ae1 | 2023-02-06 10:51:09 -0800 | [diff] [blame] | 5692 | #[test] |
Lukasz Anforowicz | e465b9e | 2023-02-06 15:00:25 -0800 | [diff] [blame] | 5693 | fn test_format_item_unsupported_const_value() { |
Lukasz Anforowicz | 08b8ae1 | 2023-02-06 10:51:09 -0800 | [diff] [blame] | 5694 | let test_src = r#" |
| 5695 | pub const CONST_VALUE: i32 = 42; |
| 5696 | "#; |
Lukasz Anforowicz | e465b9e | 2023-02-06 15:00:25 -0800 | [diff] [blame] | 5697 | test_format_item(test_src, "CONST_VALUE", |result| { |
Lukasz Anforowicz | 08b8ae1 | 2023-02-06 10:51:09 -0800 | [diff] [blame] | 5698 | let err = result.unwrap_err(); |
| 5699 | assert_eq!(err, "Unsupported rustc_hir::hir::ItemKind: constant item"); |
| 5700 | }); |
| 5701 | } |
| 5702 | |
| 5703 | #[test] |
Lukasz Anforowicz | e465b9e | 2023-02-06 15:00:25 -0800 | [diff] [blame] | 5704 | fn test_format_item_unsupported_type_alias() { |
Lukasz Anforowicz | 08b8ae1 | 2023-02-06 10:51:09 -0800 | [diff] [blame] | 5705 | let test_src = r#" |
| 5706 | pub type TypeAlias = i32; |
| 5707 | "#; |
Lukasz Anforowicz | e465b9e | 2023-02-06 15:00:25 -0800 | [diff] [blame] | 5708 | test_format_item(test_src, "TypeAlias", |result| { |
Lukasz Anforowicz | 08b8ae1 | 2023-02-06 10:51:09 -0800 | [diff] [blame] | 5709 | // 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 Anforowicz | 1422976 | 2023-02-10 15:28:33 -0800 | [diff] [blame] | 5715 | #[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 Anforowicz | 73a2c0d | 2023-03-22 14:07:00 -0700 | [diff] [blame] | 5725 | let result = result.unwrap().unwrap(); |
| 5726 | let main_api = &result.main_api; |
Lukasz Anforowicz | 2eb2429 | 2023-03-14 09:38:21 -0700 | [diff] [blame] | 5727 | assert!(!main_api.prereqs.is_empty()); |
Lukasz Anforowicz | 1422976 | 2023-02-10 15:28:33 -0800 | [diff] [blame] | 5728 | let unsupported_msg = "Error generating bindings for `SomeStruct::CONST_VALUE` \ |
| 5729 | defined at <crubit_unittests.rs>;l=5: \ |
Lukasz Anforowicz | 56d94fc | 2023-02-17 12:23:20 -0800 | [diff] [blame] | 5730 | Unsupported `impl` item kind: Const"; |
Lukasz Anforowicz | 1422976 | 2023-02-10 15:28:33 -0800 | [diff] [blame] | 5731 | assert_cc_matches!( |
Lukasz Anforowicz | 0b8a02f | 2023-02-10 15:55:40 -0800 | [diff] [blame] | 5732 | main_api.tokens, |
Lukasz Anforowicz | 1422976 | 2023-02-10 15:28:33 -0800 | [diff] [blame] | 5733 | quote! { |
| 5734 | ... |
Devin Jeanpierre | 64ac8ad | 2023-05-30 17:22:55 -0700 | [diff] [blame] | 5735 | struct CRUBIT_INTERNAL_RUST_TYPE(...) alignas(4) SomeStruct final { |
Lukasz Anforowicz | 1422976 | 2023-02-10 15:28:33 -0800 | [diff] [blame] | 5736 | ... |
| 5737 | __COMMENT__ #unsupported_msg |
| 5738 | ... |
| 5739 | }; |
| 5740 | ... |
| 5741 | } |
| 5742 | ); |
| 5743 | }); |
| 5744 | } |
| 5745 | |
Lukasz Anforowicz | be25c76 | 2023-01-17 12:43:52 -0800 | [diff] [blame] | 5746 | /// `test_format_ret_ty_for_cc_successes` provides test coverage for cases |
Lukasz Anforowicz | 8574c9d | 2023-04-13 15:11:20 -0700 | [diff] [blame] | 5747 | /// 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 Anforowicz | 4f9f552 | 2022-10-17 15:03:37 -0700 | [diff] [blame] | 5750 | #[test] |
Lukasz Anforowicz | 8a68f50 | 2022-11-15 08:43:43 -0800 | [diff] [blame] | 5751 | fn test_format_ret_ty_for_cc_successes() { |
Lukasz Anforowicz | 903fc63 | 2022-10-25 08:55:33 -0700 | [diff] [blame] | 5752 | 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 Anforowicz | 5c1b3ad | 2023-04-13 17:05:00 -0700 | [diff] [blame] | 5759 | ( |
| 5760 | "extern \"C\" fn (f32, f32) -> f32", |
| 5761 | "crubit :: type_identity_t < float (float , float) > &", |
| 5762 | ), |
Lukasz Anforowicz | 903fc63 | 2022-10-25 08:55:33 -0700 | [diff] [blame] | 5763 | ]; |
Lukasz Anforowicz | 0adaa3e | 2023-06-12 10:25:18 -0700 | [diff] [blame] | 5764 | test_ty(TypeLocation::FnReturn, &testcases, quote! {}, |desc, tcx, ty, expected| { |
Lukasz Anforowicz | ed17d05 | 2022-11-02 12:07:28 -0700 | [diff] [blame] | 5765 | let actual = { |
Lukasz Anforowicz | be25c76 | 2023-01-17 12:43:52 -0800 | [diff] [blame] | 5766 | let input = bindings_input_for_tests(tcx); |
Lukasz Anforowicz | 8574c9d | 2023-04-13 15:11:20 -0700 | [diff] [blame] | 5767 | let cc_snippet = format_ty_for_cc(&input, ty, TypeLocation::FnReturn).unwrap(); |
Lukasz Anforowicz | 3744e50 | 2022-12-02 08:40:38 -0800 | [diff] [blame] | 5768 | cc_snippet.tokens.to_string() |
Lukasz Anforowicz | ed17d05 | 2022-11-02 12:07:28 -0700 | [diff] [blame] | 5769 | }; |
Lukasz Anforowicz | 903fc63 | 2022-10-25 08:55:33 -0700 | [diff] [blame] | 5770 | let expected = expected.parse::<TokenStream>().unwrap().to_string(); |
| 5771 | assert_eq!(actual, expected, "{desc}"); |
| 5772 | }); |
| 5773 | } |
| 5774 | |
Lukasz Anforowicz | 4c19ad9 | 2022-12-16 15:23:14 -0800 | [diff] [blame] | 5775 | /// `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 Anforowicz | 903fc63 | 2022-10-25 08:55:33 -0700 | [diff] [blame] | 5785 | #[test] |
Lukasz Anforowicz | 8a68f50 | 2022-11-15 08:43:43 -0800 | [diff] [blame] | 5786 | fn test_format_ty_for_cc_successes() { |
Lukasz Anforowicz | 4f9f552 | 2022-10-17 15:03:37 -0700 | [diff] [blame] | 5787 | let testcases = [ |
Lukasz Anforowicz | f4501b4 | 2023-01-07 09:41:29 -0800 | [diff] [blame] | 5788 | // ( <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 Anforowicz | a782bda | 2023-01-17 14:04:50 -0800 | [diff] [blame] | 5795 | ("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 Anforowicz | ec0b64e | 2023-02-17 14:31:12 -0800 | [diff] [blame] | 5805 | ("char", ("rs_std::rs_char", "\"crubit/support/for/tests/rs_std/rs_char.h\"", "", "")), |
Lukasz Anforowicz | f4501b4 | 2023-01-07 09:41:29 -0800 | [diff] [blame] | 5806 | ("SomeStruct", ("::rust_out::SomeStruct", "", "SomeStruct", "")), |
Lukasz Anforowicz | 32ad956 | 2023-03-10 14:48:25 -0800 | [diff] [blame] | 5807 | ("SomeEnum", ("::rust_out::SomeEnum", "", "SomeEnum", "")), |
Lukasz Anforowicz | f4501b4 | 2023-01-07 09:41:29 -0800 | [diff] [blame] | 5808 | ("SomeUnion", ("::rust_out::SomeUnion", "", "SomeUnion", "")), |
Lukasz Anforowicz | cf78302 | 2023-06-15 10:36:58 -0700 | [diff] [blame] | 5809 | ("*const i32", ("std :: int32_t const *", "<cstdint>", "", "")), |
Lukasz Anforowicz | a782bda | 2023-01-17 14:04:50 -0800 | [diff] [blame] | 5810 | ("*mut i32", ("std::int32_t*", "<cstdint>", "", "")), |
Lukasz Anforowicz | aa2de7e | 2023-06-15 11:32:05 -0700 | [diff] [blame] | 5811 | ( |
| 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 Anforowicz | f4501b4 | 2023-01-07 09:41:29 -0800 | [diff] [blame] | 5829 | // `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 Anforowicz | f75c391 | 2023-06-12 11:27:16 -0700 | [diff] [blame] | 5833 | // Testing propagation of `const` / `mut` qualifiers: |
Lukasz Anforowicz | cf78302 | 2023-06-15 10:36:58 -0700 | [diff] [blame] | 5834 | ("*mut *const f32", ("float const * *", "", "", "")), |
| 5835 | ("*const *mut f32", ("float * const *", "", "", "")), |
Lukasz Anforowicz | 5c1b3ad | 2023-04-13 17:05:00 -0700 | [diff] [blame] | 5836 | ( |
Lukasz Anforowicz | 0adaa3e | 2023-06-12 10:25:18 -0700 | [diff] [blame] | 5837 | // 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 Anforowicz | 5c1b3ad | 2023-04-13 17:05:00 -0700 | [diff] [blame] | 5840 | "extern \"C\" fn (f32, f32) -> f32", |
| 5841 | ( |
Lukasz Anforowicz | 0adaa3e | 2023-06-12 10:25:18 -0700 | [diff] [blame] | 5842 | "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 Anforowicz | cf78302 | 2023-06-15 10:36:58 -0700 | [diff] [blame] | 5854 | "crubit :: type_identity_t < float (float , float) > * const *", |
Lukasz Anforowicz | 5c1b3ad | 2023-04-13 17:05:00 -0700 | [diff] [blame] | 5855 | "\"crubit/support/for/tests/internal/cxx20_backports.h\"", |
| 5856 | "", |
| 5857 | "", |
| 5858 | ), |
| 5859 | ), |
Lukasz Anforowicz | 4f9f552 | 2022-10-17 15:03:37 -0700 | [diff] [blame] | 5860 | // Extra parens/sugar are expected to be ignored: |
Lukasz Anforowicz | f4501b4 | 2023-01-07 09:41:29 -0800 | [diff] [blame] | 5861 | ("(bool)", ("bool", "", "", "")), |
Lukasz Anforowicz | 4f9f552 | 2022-10-17 15:03:37 -0700 | [diff] [blame] | 5862 | ]; |
Lukasz Anforowicz | 4047272 | 2022-11-08 13:29:08 -0800 | [diff] [blame] | 5863 | let preamble = quote! { |
| 5864 | #![allow(unused_parens)] |
Lukasz Anforowicz | 7589483 | 2022-11-22 18:25:28 -0800 | [diff] [blame] | 5865 | |
| 5866 | pub struct SomeStruct { |
| 5867 | pub x: i32, |
| 5868 | pub y: i32, |
| 5869 | } |
Lukasz Anforowicz | 32ad956 | 2023-03-10 14:48:25 -0800 | [diff] [blame] | 5870 | pub enum SomeEnum { |
| 5871 | Cartesian{x: f64, y: f64}, |
| 5872 | Polar{angle: f64, dist: f64}, |
| 5873 | } |
Lukasz Anforowicz | 7589483 | 2022-11-22 18:25:28 -0800 | [diff] [blame] | 5874 | pub union SomeUnion { |
| 5875 | pub x: i32, |
| 5876 | pub y: i32, |
| 5877 | } |
Lukasz Anforowicz | 4047272 | 2022-11-08 13:29:08 -0800 | [diff] [blame] | 5878 | }; |
Lukasz Anforowicz | 5c9c00d | 2022-12-02 08:41:39 -0800 | [diff] [blame] | 5879 | test_ty( |
Lukasz Anforowicz | 0adaa3e | 2023-06-12 10:25:18 -0700 | [diff] [blame] | 5880 | TypeLocation::FnParam, |
Lukasz Anforowicz | 5c9c00d | 2022-12-02 08:41:39 -0800 | [diff] [blame] | 5881 | &testcases, |
| 5882 | preamble, |
Lukasz Anforowicz | f4501b4 | 2023-01-07 09:41:29 -0800 | [diff] [blame] | 5883 | |desc, tcx, ty, |
| 5884 | (expected_tokens, expected_include, expected_prereq_def, expected_prereq_fwd_decl)| { |
| 5885 | let (actual_tokens, actual_prereqs) = { |
Lukasz Anforowicz | be25c76 | 2023-01-17 12:43:52 -0800 | [diff] [blame] | 5886 | let input = bindings_input_for_tests(tcx); |
Lukasz Anforowicz | 0adaa3e | 2023-06-12 10:25:18 -0700 | [diff] [blame] | 5887 | let s = format_ty_for_cc(&input, ty, TypeLocation::FnParam).unwrap(); |
Lukasz Anforowicz | f4501b4 | 2023-01-07 09:41:29 -0800 | [diff] [blame] | 5888 | (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 Anforowicz | ed17d05 | 2022-11-02 12:07:28 -0700 | [diff] [blame] | 5892 | |
Lukasz Anforowicz | 5c9c00d | 2022-12-02 08:41:39 -0800 | [diff] [blame] | 5893 | let expected_tokens = expected_tokens.parse::<TokenStream>().unwrap().to_string(); |
| 5894 | assert_eq!(actual_tokens, expected_tokens, "{desc}"); |
Lukasz Anforowicz | ed17d05 | 2022-11-02 12:07:28 -0700 | [diff] [blame] | 5895 | |
Lukasz Anforowicz | f4501b4 | 2023-01-07 09:41:29 -0800 | [diff] [blame] | 5896 | if expected_include.is_empty() { |
Lukasz Anforowicz | aa2de7e | 2023-06-15 11:32:05 -0700 | [diff] [blame] | 5897 | assert!( |
| 5898 | actual_includes.is_empty(), |
| 5899 | "{desc}: `actual_includes` is unexpectedly non-empty: {actual_includes:?}", |
| 5900 | ); |
Lukasz Anforowicz | f4501b4 | 2023-01-07 09:41:29 -0800 | [diff] [blame] | 5901 | } else { |
Lukasz Anforowicz | a782bda | 2023-01-17 14:04:50 -0800 | [diff] [blame] | 5902 | let expected_include: TokenStream = expected_include.parse().unwrap(); |
Lukasz Anforowicz | f4501b4 | 2023-01-07 09:41:29 -0800 | [diff] [blame] | 5903 | assert_cc_matches!( |
| 5904 | format_cc_includes(&actual_includes), |
Lukasz Anforowicz | a782bda | 2023-01-17 14:04:50 -0800 | [diff] [blame] | 5905 | quote! { __HASH_TOKEN__ include #expected_include } |
Lukasz Anforowicz | f4501b4 | 2023-01-07 09:41:29 -0800 | [diff] [blame] | 5906 | ); |
| 5907 | } |
Lukasz Anforowicz | 5c9c00d | 2022-12-02 08:41:39 -0800 | [diff] [blame] | 5908 | |
| 5909 | if expected_prereq_def.is_empty() { |
Lukasz Anforowicz | aa2de7e | 2023-06-15 11:32:05 -0700 | [diff] [blame] | 5910 | assert!( |
| 5911 | actual_prereq_defs.is_empty(), |
| 5912 | "{desc}: `actual_prereq_defs` is unexpectedly non-empty", |
| 5913 | ); |
Lukasz Anforowicz | 5c9c00d | 2022-12-02 08:41:39 -0800 | [diff] [blame] | 5914 | } 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 Anforowicz | f4501b4 | 2023-01-07 09:41:29 -0800 | [diff] [blame] | 5919 | |
| 5920 | if expected_prereq_fwd_decl.is_empty() { |
Lukasz Anforowicz | aa2de7e | 2023-06-15 11:32:05 -0700 | [diff] [blame] | 5921 | assert!( |
| 5922 | actual_prereq_fwd_decls.is_empty(), |
| 5923 | "{desc}: `actual_prereq_fwd_decls` is unexpectedly non-empty", |
| 5924 | ); |
Lukasz Anforowicz | f4501b4 | 2023-01-07 09:41:29 -0800 | [diff] [blame] | 5925 | } 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 Anforowicz | 5c9c00d | 2022-12-02 08:41:39 -0800 | [diff] [blame] | 5931 | }, |
| 5932 | ); |
Lukasz Anforowicz | 4f9f552 | 2022-10-17 15:03:37 -0700 | [diff] [blame] | 5933 | } |
| 5934 | |
Lukasz Anforowicz | 4c19ad9 | 2022-12-16 15:23:14 -0800 | [diff] [blame] | 5935 | /// `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 Anforowicz | 0182c5c | 2022-12-29 10:08:50 -0800 | [diff] [blame] | 5946 | /// TODO(lukasza): Add test coverage (here and in the "for_rs" flavours) |
| 5947 | /// for: |
Lukasz Anforowicz | 4c19ad9 | 2022-12-16 15:23:14 -0800 | [diff] [blame] | 5948 | /// - 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 Anforowicz | 4f9f552 | 2022-10-17 15:03:37 -0700 | [diff] [blame] | 5955 | #[test] |
Lukasz Anforowicz | 8a68f50 | 2022-11-15 08:43:43 -0800 | [diff] [blame] | 5956 | fn test_format_ty_for_cc_failures() { |
Lukasz Anforowicz | 4f9f552 | 2022-10-17 15:03:37 -0700 | [diff] [blame] | 5957 | let testcases = [ |
| 5958 | // ( <Rust type>, <expected error message> ) |
Lukasz Anforowicz | 4f9f552 | 2022-10-17 15:03:37 -0700 | [diff] [blame] | 5959 | ( |
Lukasz Anforowicz | 903fc63 | 2022-10-25 08:55:33 -0700 | [diff] [blame] | 5960 | "()", // Empty TyKind::Tuple |
Devin Jeanpierre | 81aec50 | 2023-04-04 15:33:39 -0700 | [diff] [blame] | 5961 | "`()` / `void` is only supported as a return type (b/254507801)", |
Lukasz Anforowicz | 903fc63 | 2022-10-25 08:55:33 -0700 | [diff] [blame] | 5962 | ), |
| 5963 | ( |
| 5964 | // TODO(b/254507801): Expect `crubit::Never` instead (see the bug for more |
| 5965 | // details). |
| 5966 | "!", // TyKind::Never |
Devin Jeanpierre | 81aec50 | 2023-04-04 15:33:39 -0700 | [diff] [blame] | 5967 | "The never type `!` is only supported as a return type (b/254507801)", |
Lukasz Anforowicz | 903fc63 | 2022-10-25 08:55:33 -0700 | [diff] [blame] | 5968 | ), |
| 5969 | ( |
| 5970 | "(i32, i32)", // Non-empty TyKind::Tuple |
Lukasz Anforowicz | 13794df | 2022-10-21 07:56:34 -0700 | [diff] [blame] | 5971 | "Tuples are not supported yet: (i32, i32) (b/254099023)", |
Lukasz Anforowicz | 4f9f552 | 2022-10-17 15:03:37 -0700 | [diff] [blame] | 5972 | ), |
| 5973 | ( |
Lukasz Anforowicz | ed96fcb | 2023-06-12 11:39:45 -0700 | [diff] [blame] | 5974 | "&'static &'static i32", // TyKind::Ref (nested reference - referent of reference) |
Lukasz Anforowicz | aa2de7e | 2023-06-15 11:32:05 -0700 | [diff] [blame] | 5975 | "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 Anforowicz | ed96fcb | 2023-06-12 11:39:45 -0700 | [diff] [blame] | 5978 | ), |
| 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 Anforowicz | 4f9f552 | 2022-10-17 15:03:37 -0700 | [diff] [blame] | 5984 | "[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 Anforowicz | aa2de7e | 2023-06-15 11:32:05 -0700 | [diff] [blame] | 5989 | "Failed to format the referent of the reference type `&'static [i32]`: \ |
| 5990 | The following Rust type is not supported yet: [i32]", |
Lukasz Anforowicz | 4f9f552 | 2022-10-17 15:03:37 -0700 | [diff] [blame] | 5991 | ), |
| 5992 | ( |
| 5993 | "&'static str", // TyKind::Str (nested underneath TyKind::Ref) |
Lukasz Anforowicz | aa2de7e | 2023-06-15 11:32:05 -0700 | [diff] [blame] | 5994 | "Failed to format the referent of the reference type `&'static str`: \ |
| 5995 | The following Rust type is not supported yet: str", |
Lukasz Anforowicz | 4f9f552 | 2022-10-17 15:03:37 -0700 | [diff] [blame] | 5996 | ), |
| 5997 | ( |
Lukasz Anforowicz | 0182c5c | 2022-12-29 10:08:50 -0800 | [diff] [blame] | 5998 | "impl Eq", // TyKind::Alias |
Lukasz Anforowicz | 0adaa3e | 2023-06-12 10:25:18 -0700 | [diff] [blame] | 5999 | "The following Rust type is not supported yet: impl Eq", |
Lukasz Anforowicz | 4f9f552 | 2022-10-17 15:03:37 -0700 | [diff] [blame] | 6000 | ), |
| 6001 | ( |
Lukasz Anforowicz | 5c1b3ad | 2023-04-13 17:05:00 -0700 | [diff] [blame] | 6002 | "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 Anforowicz | 4f9f552 | 2022-10-17 15:03:37 -0700 | [diff] [blame] | 6017 | ), |
| 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 Anforowicz | 7589483 | 2022-11-22 18:25:28 -0800 | [diff] [blame] | 6022 | ( |
| 6023 | "StructWithCustomDrop", |
| 6024 | "Failed to generate bindings for the definition of `StructWithCustomDrop`: \ |
Devin Jeanpierre | 81aec50 | 2023-04-04 15:33:39 -0700 | [diff] [blame] | 6025 | `Drop` trait and \"drop glue\" are not supported yet (b/258251148)", |
Lukasz Anforowicz | 7589483 | 2022-11-22 18:25:28 -0800 | [diff] [blame] | 6026 | ), |
Devin Jeanpierre | 81aec50 | 2023-04-04 15:33:39 -0700 | [diff] [blame] | 6027 | ("ConstGenericStruct<42>", "Generic types are not supported yet (b/259749095)"), |
| 6028 | ("TypeGenericStruct<u8>", "Generic types are not supported yet (b/259749095)"), |
Lukasz Anforowicz | 7589483 | 2022-11-22 18:25:28 -0800 | [diff] [blame] | 6029 | ( |
| 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 | ), |
Googler | 7a77a80 | 2023-04-21 08:32:50 -0700 | [diff] [blame] | 6036 | ("LifetimeGenericStruct<'static>", "Generic types are not supported yet (b/259749095)"), |
Lukasz Anforowicz | cfe8455 | 2023-04-20 13:38:54 -0700 | [diff] [blame] | 6037 | ( |
| 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 | ), |
Googler | 7a77a80 | 2023-04-21 08:32:50 -0700 | [diff] [blame] | 6042 | ("Option<i8>", "Generic types are not supported yet (b/259749095)"), |
Lukasz Anforowicz | f36762a | 2023-03-02 18:43:07 -0800 | [diff] [blame] | 6043 | ( |
| 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 Anforowicz | 2eb2429 | 2023-03-14 09:38:21 -0700 | [diff] [blame] | 6059 | ( |
| 6060 | "*const Option<i8>", |
Lukasz Anforowicz | aa2de7e | 2023-06-15 11:32:05 -0700 | [diff] [blame] | 6061 | "Failed to format the pointee \ |
| 6062 | of the pointer type `*const std::option::Option<i8>`: \ |
Lukasz Anforowicz | 2eb2429 | 2023-03-14 09:38:21 -0700 | [diff] [blame] | 6063 | Generic types are not supported yet (b/259749095)", |
| 6064 | ), |
Lukasz Anforowicz | 4f9f552 | 2022-10-17 15:03:37 -0700 | [diff] [blame] | 6065 | ]; |
Lukasz Anforowicz | 4047272 | 2022-11-08 13:29:08 -0800 | [diff] [blame] | 6066 | let preamble = quote! { |
Lukasz Anforowicz | 7589483 | 2022-11-22 18:25:28 -0800 | [diff] [blame] | 6067 | #![feature(never_type)] |
| 6068 | |
Lukasz Anforowicz | 5c1b3ad | 2023-04-13 17:05:00 -0700 | [diff] [blame] | 6069 | pub struct SomeStruct { |
| 6070 | pub x: i32, |
| 6071 | pub y: i32, |
| 6072 | } |
| 6073 | |
Lukasz Anforowicz | 7589483 | 2022-11-22 18:25:28 -0800 | [diff] [blame] | 6074 | pub struct StructWithCustomDrop { |
Lukasz Anforowicz | 4047272 | 2022-11-08 13:29:08 -0800 | [diff] [blame] | 6075 | pub x: i32, |
| 6076 | pub y: i32, |
| 6077 | } |
Lukasz Anforowicz | 7589483 | 2022-11-22 18:25:28 -0800 | [diff] [blame] | 6078 | |
| 6079 | impl Drop for StructWithCustomDrop { |
| 6080 | fn drop(&mut self) {} |
Lukasz Anforowicz | 4047272 | 2022-11-08 13:29:08 -0800 | [diff] [blame] | 6081 | } |
Lukasz Anforowicz | 7589483 | 2022-11-22 18:25:28 -0800 | [diff] [blame] | 6082 | |
| 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 Anforowicz | 4047272 | 2022-11-08 13:29:08 -0800 | [diff] [blame] | 6093 | } |
Lukasz Anforowicz | f36762a | 2023-03-02 18:43:07 -0800 | [diff] [blame] | 6094 | |
| 6095 | mod private_submodule { |
| 6096 | pub struct PublicStructInPrivateModule; |
| 6097 | } |
| 6098 | pub use private_submodule::PublicStructInPrivateModule |
| 6099 | as PublicReexportOfStruct; |
Lukasz Anforowicz | 4047272 | 2022-11-08 13:29:08 -0800 | [diff] [blame] | 6100 | }; |
Lukasz Anforowicz | 0adaa3e | 2023-06-12 10:25:18 -0700 | [diff] [blame] | 6101 | test_ty(TypeLocation::FnParam, &testcases, preamble, |desc, tcx, ty, expected_msg| { |
Lukasz Anforowicz | be25c76 | 2023-01-17 12:43:52 -0800 | [diff] [blame] | 6102 | let input = bindings_input_for_tests(tcx); |
Lukasz Anforowicz | 0adaa3e | 2023-06-12 10:25:18 -0700 | [diff] [blame] | 6103 | let anyhow_err = format_ty_for_cc(&input, ty, TypeLocation::FnParam) |
| 6104 | .expect_err(&format!("Expecting error for: {desc}")); |
Lukasz Anforowicz | be25c76 | 2023-01-17 12:43:52 -0800 | [diff] [blame] | 6105 | let actual_msg = format!("{anyhow_err:#}"); |
| 6106 | assert_eq!(&actual_msg, *expected_msg, "{desc}"); |
Lukasz Anforowicz | 4f9f552 | 2022-10-17 15:03:37 -0700 | [diff] [blame] | 6107 | }); |
| 6108 | } |
| 6109 | |
Lukasz Anforowicz | 7860d0e | 2022-11-15 08:47:56 -0800 | [diff] [blame] | 6110 | #[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 Anforowicz | eb58a49 | 2023-01-07 08:25:48 -0800 | [diff] [blame] | 6133 | // ADTs: |
Lukasz Anforowicz | 8b475a4 | 2022-11-29 09:33:02 -0800 | [diff] [blame] | 6134 | ("SomeStruct", "::rust_out::SomeStruct"), |
| 6135 | ("SomeEnum", "::rust_out::SomeEnum"), |
| 6136 | ("SomeUnion", "::rust_out::SomeUnion"), |
Lukasz Anforowicz | eb58a49 | 2023-01-07 08:25:48 -0800 | [diff] [blame] | 6137 | // Type from another crate: |
Lukasz Anforowicz | 8b475a4 | 2022-11-29 09:33:02 -0800 | [diff] [blame] | 6138 | ("std::cmp::Ordering", "::core::cmp::Ordering"), |
Lukasz Anforowicz | eb58a49 | 2023-01-07 08:25:48 -0800 | [diff] [blame] | 6139 | // `const` and `mut` pointers: |
| 6140 | ("*const i32", "*const i32"), |
| 6141 | ("*mut i32", "*mut i32"), |
Lukasz Anforowicz | aa2de7e | 2023-06-15 11:32:05 -0700 | [diff] [blame] | 6142 | // References: |
| 6143 | ("&i32", "& '__anon1 i32"), |
| 6144 | ("&mut i32", "& '__anon1 mut i32"), |
| 6145 | ("&'_ i32", "& '__anon1 i32"), |
| 6146 | ("&'static i32", "& 'static i32"), |
Lukasz Anforowicz | eb58a49 | 2023-01-07 08:25:48 -0800 | [diff] [blame] | 6147 | // Pointer to an ADT: |
| 6148 | ("*mut SomeStruct", "* mut :: rust_out :: SomeStruct"), |
Lukasz Anforowicz | 5c1b3ad | 2023-04-13 17:05:00 -0700 | [diff] [blame] | 6149 | ("extern \"C\" fn(i32) -> i32", "extern \"C\" fn(i32) -> i32"), |
Lukasz Anforowicz | 7860d0e | 2022-11-15 08:47:56 -0800 | [diff] [blame] | 6150 | ]; |
Lukasz Anforowicz | 7589483 | 2022-11-22 18:25:28 -0800 | [diff] [blame] | 6151 | 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 Anforowicz | 0adaa3e | 2023-06-12 10:25:18 -0700 | [diff] [blame] | 6167 | test_ty(TypeLocation::FnParam, &testcases, preamble, |desc, tcx, ty, expected_tokens| { |
Lukasz Anforowicz | 5c9c00d | 2022-12-02 08:41:39 -0800 | [diff] [blame] | 6168 | 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 Anforowicz | 7860d0e | 2022-11-15 08:47:56 -0800 | [diff] [blame] | 6171 | }); |
| 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 Anforowicz | 7860d0e | 2022-11-15 08:47:56 -0800 | [diff] [blame] | 6185 | "[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 Anforowicz | aa2de7e | 2023-06-15 11:32:05 -0700 | [diff] [blame] | 6190 | "Failed to format the referent of the reference type `&'static [i32]`: \ |
| 6191 | The following Rust type is not supported yet: [i32]", |
Lukasz Anforowicz | 7860d0e | 2022-11-15 08:47:56 -0800 | [diff] [blame] | 6192 | ), |
| 6193 | ( |
| 6194 | "&'static str", // TyKind::Str (nested underneath TyKind::Ref) |
Lukasz Anforowicz | aa2de7e | 2023-06-15 11:32:05 -0700 | [diff] [blame] | 6195 | "Failed to format the referent of the reference type `&'static str`: \ |
| 6196 | The following Rust type is not supported yet: str", |
Lukasz Anforowicz | 7860d0e | 2022-11-15 08:47:56 -0800 | [diff] [blame] | 6197 | ), |
| 6198 | ( |
Lukasz Anforowicz | 0182c5c | 2022-12-29 10:08:50 -0800 | [diff] [blame] | 6199 | "impl Eq", // TyKind::Alias |
Lukasz Anforowicz | 0adaa3e | 2023-06-12 10:25:18 -0700 | [diff] [blame] | 6200 | "The following Rust type is not supported yet: impl Eq", |
Lukasz Anforowicz | 7860d0e | 2022-11-15 08:47:56 -0800 | [diff] [blame] | 6201 | ), |
| 6202 | ( |
Lukasz Anforowicz | 8b475a4 | 2022-11-29 09:33:02 -0800 | [diff] [blame] | 6203 | "Option<i8>", // TyKind::Adt - generic + different crate |
| 6204 | "Generic types are not supported yet (b/259749095)", |
| 6205 | ), |
Lukasz Anforowicz | 7860d0e | 2022-11-15 08:47:56 -0800 | [diff] [blame] | 6206 | ]; |
Lukasz Anforowicz | 7589483 | 2022-11-22 18:25:28 -0800 | [diff] [blame] | 6207 | let preamble = quote! {}; |
Lukasz Anforowicz | 0adaa3e | 2023-06-12 10:25:18 -0700 | [diff] [blame] | 6208 | 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 Anforowicz | 7860d0e | 2022-11-15 08:47:56 -0800 | [diff] [blame] | 6211 | let actual_err = format!("{anyhow_err:#}"); |
| 6212 | assert_eq!(&actual_err, *expected_err, "{desc}"); |
| 6213 | }); |
| 6214 | } |
| 6215 | |
Lukasz Anforowicz | 4047272 | 2022-11-08 13:29:08 -0800 | [diff] [blame] | 6216 | fn test_ty<TestFn, Expectation>( |
Lukasz Anforowicz | 0adaa3e | 2023-06-12 10:25:18 -0700 | [diff] [blame] | 6217 | type_location: TypeLocation, |
Lukasz Anforowicz | 4047272 | 2022-11-08 13:29:08 -0800 | [diff] [blame] | 6218 | testcases: &[(&str, Expectation)], |
| 6219 | preamble: TokenStream, |
| 6220 | test_fn: TestFn, |
| 6221 | ) where |
Lukasz Anforowicz | d16b6bf | 2022-11-22 18:35:08 -0800 | [diff] [blame] | 6222 | TestFn: for<'tcx> Fn( |
| 6223 | /* testcase_description: */ &str, |
| 6224 | TyCtxt<'tcx>, |
| 6225 | Ty<'tcx>, |
| 6226 | &Expectation, |
| 6227 | ) + Sync, |
Lukasz Anforowicz | 4f9f552 | 2022-10-17 15:03:37 -0700 | [diff] [blame] | 6228 | Expectation: Sync, |
| 6229 | { |
Lukasz Anforowicz | d0f0a84 | 2022-11-03 12:40:13 -0700 | [diff] [blame] | 6230 | for (index, (input, expected)) in testcases.iter().enumerate() { |
Lukasz Anforowicz | 4f9f552 | 2022-10-17 15:03:37 -0700 | [diff] [blame] | 6231 | let desc = format!("test #{index}: test input: `{input}`"); |
| 6232 | let input = { |
| 6233 | let ty_tokens: TokenStream = input.parse().unwrap(); |
Lukasz Anforowicz | 0adaa3e | 2023-06-12 10:25:18 -0700 | [diff] [blame] | 6234 | 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 Anforowicz | 4f9f552 | 2022-10-17 15:03:37 -0700 | [diff] [blame] | 6244 | }; |
| 6245 | input.to_string() |
| 6246 | }; |
Lukasz Anforowicz | 0bef264 | 2023-01-05 09:20:31 -0800 | [diff] [blame] | 6247 | run_compiler_for_testing(input, |tcx| { |
Lukasz Anforowicz | 4f9f552 | 2022-10-17 15:03:37 -0700 | [diff] [blame] | 6248 | let def_id = find_def_id_by_name(tcx, "test_function"); |
Lukasz Anforowicz | aa2de7e | 2023-06-15 11:32:05 -0700 | [diff] [blame] | 6249 | let sig = get_fn_sig(tcx, def_id); |
Lukasz Anforowicz | 0adaa3e | 2023-06-12 10:25:18 -0700 | [diff] [blame] | 6250 | let ty = match type_location { |
| 6251 | TypeLocation::FnReturn => sig.output(), |
| 6252 | TypeLocation::FnParam => sig.inputs()[0], |
| 6253 | TypeLocation::Other => unimplemented!(), |
| 6254 | }; |
Lukasz Anforowicz | 7589483 | 2022-11-22 18:25:28 -0800 | [diff] [blame] | 6255 | test_fn(&desc, tcx, ty, expected); |
Lukasz Anforowicz | 4f9f552 | 2022-10-17 15:03:37 -0700 | [diff] [blame] | 6256 | }); |
| 6257 | } |
| 6258 | } |
| 6259 | |
Lukasz Anforowicz | e465b9e | 2023-02-06 15:00:25 -0800 | [diff] [blame] | 6260 | /// Tests invoking `format_item` on the item with the specified `name` from |
Lukasz Anforowicz | 8a83341 | 2022-10-14 14:44:13 -0700 | [diff] [blame] | 6261 | /// the given Rust `source`. Returns the result of calling |
Lukasz Anforowicz | e465b9e | 2023-02-06 15:00:25 -0800 | [diff] [blame] | 6262 | /// `test_function` with `format_item`'s result as an argument. |
Lukasz Anforowicz | 8a83341 | 2022-10-14 14:44:13 -0700 | [diff] [blame] | 6263 | /// (`test_function` should typically `assert!` that it got the expected |
Lukasz Anforowicz | e465b9e | 2023-02-06 15:00:25 -0800 | [diff] [blame] | 6264 | /// result from `format_item`.) |
| 6265 | fn test_format_item<F, T>(source: &str, name: &str, test_function: F) -> T |
Lukasz Anforowicz | 8a83341 | 2022-10-14 14:44:13 -0700 | [diff] [blame] | 6266 | where |
Lukasz Anforowicz | 73a2c0d | 2023-03-22 14:07:00 -0700 | [diff] [blame] | 6267 | F: FnOnce(Result<Option<ApiSnippets>, String>) -> T + Send, |
Lukasz Anforowicz | 8a83341 | 2022-10-14 14:44:13 -0700 | [diff] [blame] | 6268 | T: Send, |
| 6269 | { |
Lukasz Anforowicz | 0bef264 | 2023-01-05 09:20:31 -0800 | [diff] [blame] | 6270 | run_compiler_for_testing(source, |tcx| { |
Lukasz Anforowicz | 8a83341 | 2022-10-14 14:44:13 -0700 | [diff] [blame] | 6271 | let def_id = find_def_id_by_name(tcx, name); |
Lukasz Anforowicz | e465b9e | 2023-02-06 15:00:25 -0800 | [diff] [blame] | 6272 | let result = format_item(&bindings_input_for_tests(tcx), def_id); |
Lukasz Anforowicz | e433306 | 2022-10-17 14:47:53 -0700 | [diff] [blame] | 6273 | |
| 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 Anforowicz | 8a83341 | 2022-10-14 14:44:13 -0700 | [diff] [blame] | 6279 | }) |
| 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 Anforowicz | d0f0a84 | 2022-11-03 12:40:13 -0700 | [diff] [blame] | 6288 | match *items_with_matching_name.as_slice() { |
| 6289 | [] => { |
Lukasz Anforowicz | 8a83341 | 2022-10-14 14:44:13 -0700 | [diff] [blame] | 6290 | 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 Anforowicz | d0f0a84 | 2022-11-03 12:40:13 -0700 | [diff] [blame] | 6296 | .join(",\n"); |
| 6297 | panic!("No items named `{name}`.\nInstead found:\n{found_names}"); |
Lukasz Anforowicz | 8a83341 | 2022-10-14 14:44:13 -0700 | [diff] [blame] | 6298 | } |
Lukasz Anforowicz | 61cb1e3 | 2022-11-04 09:08:35 -0700 | [diff] [blame] | 6299 | [item] => item.owner_id.def_id, |
Lukasz Anforowicz | 8a83341 | 2022-10-14 14:44:13 -0700 | [diff] [blame] | 6300 | _ => panic!("More than one item named `{name}`"), |
| 6301 | } |
| 6302 | } |
| 6303 | |
Lukasz Anforowicz | be25c76 | 2023-01-17 12:43:52 -0800 | [diff] [blame] | 6304 | fn bindings_input_for_tests(tcx: TyCtxt) -> Input { |
Lukasz Anforowicz | a782bda | 2023-01-17 14:04:50 -0800 | [diff] [blame] | 6305 | Input { |
| 6306 | tcx, |
| 6307 | crubit_support_path: "crubit/support/for/tests".into(), |
Lukasz Anforowicz | cfe8455 | 2023-04-20 13:38:54 -0700 | [diff] [blame] | 6308 | crate_name_to_include_path: Default::default(), |
Lukasz Anforowicz | a782bda | 2023-01-17 14:04:50 -0800 | [diff] [blame] | 6309 | _features: (), |
Lukasz Anforowicz | a782bda | 2023-01-17 14:04:50 -0800 | [diff] [blame] | 6310 | } |
Lukasz Anforowicz | be25c76 | 2023-01-17 12:43:52 -0800 | [diff] [blame] | 6311 | } |
| 6312 | |
| 6313 | /// Tests invoking `generate_bindings` on the given Rust `source`. |
Lukasz Anforowicz | 8a83341 | 2022-10-14 14:44:13 -0700 | [diff] [blame] | 6314 | /// 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 Anforowicz | 581fd75 | 2022-09-21 11:30:15 -0700 | [diff] [blame] | 6318 | where |
Lukasz Anforowicz | be25c76 | 2023-01-17 12:43:52 -0800 | [diff] [blame] | 6319 | F: FnOnce(Result<Output>) -> T + Send, |
Lukasz Anforowicz | 581fd75 | 2022-09-21 11:30:15 -0700 | [diff] [blame] | 6320 | T: Send, |
| 6321 | { |
Lukasz Anforowicz | be25c76 | 2023-01-17 12:43:52 -0800 | [diff] [blame] | 6322 | run_compiler_for_testing(source, |tcx| { |
| 6323 | test_function(generate_bindings(&bindings_input_for_tests(tcx))) |
| 6324 | }) |
Lukasz Anforowicz | bda1cfe | 2022-09-20 06:25:43 -0700 | [diff] [blame] | 6325 | } |
Lukasz Anforowicz | bda1cfe | 2022-09-20 06:25:43 -0700 | [diff] [blame] | 6326 | } |