blob: 1f7adda9797ba3fb8803769bbf68ff5208a862af [file] [log] [blame]
David Tolnay754e21c2020-03-29 20:58:46 -07001use crate::gen::namespace::Namespace;
David Tolnay7db73692019-10-20 14:51:12 -04002use crate::gen::out::OutFile;
David Tolnay33d30292020-03-18 18:02:02 -07003use crate::gen::{include, Opt};
David Tolnay7db73692019-10-20 14:51:12 -04004use crate::syntax::atom::Atom::{self, *};
5use crate::syntax::{Api, ExternFn, Struct, Type, Types, Var};
6use proc_macro2::Ident;
7
David Tolnay33d30292020-03-18 18:02:02 -07008pub(super) fn gen(
David Tolnay754e21c2020-03-29 20:58:46 -07009 namespace: Namespace,
David Tolnay33d30292020-03-18 18:02:02 -070010 apis: &[Api],
11 types: &Types,
12 opt: Opt,
13 header: bool,
14) -> OutFile {
David Tolnay7db73692019-10-20 14:51:12 -040015 let mut out_file = OutFile::new(namespace.clone(), header);
16 let out = &mut out_file;
17
18 if header {
19 writeln!(out, "#pragma once");
20 }
21
David Tolnay33d30292020-03-18 18:02:02 -070022 out.include.extend(opt.include);
David Tolnay7db73692019-10-20 14:51:12 -040023 for api in apis {
24 if let Api::Include(include) = api {
David Tolnay9c68b1a2020-03-06 11:12:55 -080025 out.include.insert(include.value());
David Tolnay7db73692019-10-20 14:51:12 -040026 }
27 }
28
29 write_includes(out, types);
David Tolnayf51447e2020-03-06 14:14:27 -080030 write_include_cxxbridge(out, apis, types);
David Tolnay7db73692019-10-20 14:51:12 -040031
David Tolnay7db73692019-10-20 14:51:12 -040032 out.next_section();
33 for name in &namespace {
34 writeln!(out, "namespace {} {{", name);
35 }
36
David Tolnay7db73692019-10-20 14:51:12 -040037 out.next_section();
38 for api in apis {
39 match api {
40 Api::Struct(strct) => write_struct_decl(out, &strct.ident),
David Tolnay8861bee2020-01-20 18:39:24 -080041 Api::CxxType(ety) => write_struct_using(out, &ety.ident),
42 Api::RustType(ety) => write_struct_decl(out, &ety.ident),
David Tolnay7db73692019-10-20 14:51:12 -040043 _ => {}
44 }
45 }
46
47 for api in apis {
48 if let Api::Struct(strct) = api {
49 out.next_section();
50 write_struct(out, strct);
51 }
52 }
53
54 if !header {
55 out.begin_block("extern \"C\"");
David Tolnayebef4a22020-03-17 15:33:47 -070056 write_exception_glue(out, apis);
David Tolnay7db73692019-10-20 14:51:12 -040057 for api in apis {
58 let (efn, write): (_, fn(_, _, _)) = match api {
59 Api::CxxFunction(efn) => (efn, write_cxx_function_shim),
60 Api::RustFunction(efn) => (efn, write_rust_function_decl),
61 _ => continue,
62 };
63 out.next_section();
64 write(out, efn, types);
65 }
David Tolnay9ad1fbc2020-03-01 14:01:24 -080066 out.end_block("extern \"C\"");
David Tolnay7db73692019-10-20 14:51:12 -040067 }
68
69 for api in apis {
70 if let Api::RustFunction(efn) = api {
71 out.next_section();
72 write_rust_function_shim(out, efn, types);
73 }
74 }
75
76 out.next_section();
77 for name in namespace.iter().rev() {
78 writeln!(out, "}} // namespace {}", name);
79 }
80
81 if !header {
82 out.next_section();
83 write_generic_instantiations(out, types);
84 }
85
David Tolnay9c68b1a2020-03-06 11:12:55 -080086 out.prepend(out.include.to_string());
87
David Tolnay7db73692019-10-20 14:51:12 -040088 out_file
89}
90
91fn write_includes(out: &mut OutFile, types: &Types) {
David Tolnay7db73692019-10-20 14:51:12 -040092 for ty in types {
93 match ty {
94 Type::Ident(ident) => match Atom::from(ident) {
David Tolnay30430f12020-03-19 20:49:00 -070095 Some(U8) | Some(U16) | Some(U32) | Some(U64) | Some(I8) | Some(I16) | Some(I32)
96 | Some(I64) => out.include.cstdint = true,
97 Some(Usize) => out.include.cstddef = true,
David Tolnay9c68b1a2020-03-06 11:12:55 -080098 Some(CxxString) => out.include.string = true,
David Tolnay30430f12020-03-19 20:49:00 -070099 Some(Bool) | Some(Isize) | Some(F32) | Some(F64) | Some(RustString) | None => {}
David Tolnay7db73692019-10-20 14:51:12 -0400100 },
David Tolnay9c68b1a2020-03-06 11:12:55 -0800101 Type::RustBox(_) => out.include.type_traits = true,
102 Type::UniquePtr(_) => out.include.memory = true,
David Tolnay7db73692019-10-20 14:51:12 -0400103 _ => {}
104 }
105 }
David Tolnay7db73692019-10-20 14:51:12 -0400106}
107
David Tolnayf51447e2020-03-06 14:14:27 -0800108fn write_include_cxxbridge(out: &mut OutFile, apis: &[Api], types: &Types) {
David Tolnayb7a7cb62020-03-17 21:18:40 -0700109 let mut needs_rust_string = false;
110 let mut needs_rust_str = false;
David Tolnay7db73692019-10-20 14:51:12 -0400111 let mut needs_rust_box = false;
112 for ty in types {
David Tolnayb7a7cb62020-03-17 21:18:40 -0700113 match ty {
114 Type::RustBox(_) => {
115 out.include.type_traits = true;
116 needs_rust_box = true;
117 }
118 Type::Str(_) => {
119 out.include.cstdint = true;
120 out.include.string = true;
121 needs_rust_str = true;
122 }
123 ty if ty == RustString => {
124 out.include.array = true;
125 out.include.cstdint = true;
126 out.include.string = true;
127 needs_rust_string = true;
128 }
129 _ => {}
David Tolnay7db73692019-10-20 14:51:12 -0400130 }
131 }
132
David Tolnayb7a7cb62020-03-17 21:18:40 -0700133 let mut needs_rust_error = false;
134 let mut needs_unsafe_bitcopy = false;
David Tolnayf51447e2020-03-06 14:14:27 -0800135 let mut needs_manually_drop = false;
David Tolnay09011c32020-03-06 14:40:28 -0800136 let mut needs_maybe_uninit = false;
David Tolnay5d121442020-03-17 22:14:40 -0700137 let mut needs_trycatch = false;
David Tolnay09011c32020-03-06 14:40:28 -0800138 for api in apis {
David Tolnayb7a7cb62020-03-17 21:18:40 -0700139 match api {
140 Api::CxxFunction(efn) if !out.header => {
David Tolnay5d121442020-03-17 22:14:40 -0700141 if efn.throws {
142 needs_trycatch = true;
143 }
David Tolnayb7a7cb62020-03-17 21:18:40 -0700144 for arg in &efn.args {
145 if arg.ty == RustString {
146 needs_unsafe_bitcopy = true;
147 break;
148 }
David Tolnay09011c32020-03-06 14:40:28 -0800149 }
150 }
David Tolnayb7a7cb62020-03-17 21:18:40 -0700151 Api::RustFunction(efn) if !out.header => {
152 if efn.throws {
153 out.include.exception = true;
154 needs_rust_error = true;
155 }
156 for arg in &efn.args {
157 if arg.ty != RustString && types.needs_indirect_abi(&arg.ty) {
158 needs_manually_drop = true;
159 break;
160 }
161 }
162 if let Some(ret) = &efn.ret {
163 if types.needs_indirect_abi(ret) {
164 needs_maybe_uninit = true;
165 }
David Tolnayf51447e2020-03-06 14:14:27 -0800166 }
167 }
David Tolnayb7a7cb62020-03-17 21:18:40 -0700168 _ => {}
David Tolnayf51447e2020-03-06 14:14:27 -0800169 }
170 }
171
David Tolnay750755e2020-03-01 13:04:08 -0800172 out.begin_block("namespace rust");
David Tolnay8c730492020-03-13 01:29:06 -0700173 out.begin_block("inline namespace cxxbridge02");
David Tolnayf51447e2020-03-06 14:14:27 -0800174
David Tolnayb7a7cb62020-03-17 21:18:40 -0700175 if needs_rust_string
176 || needs_rust_str
177 || needs_rust_box
178 || needs_rust_error
179 || needs_unsafe_bitcopy
180 || needs_manually_drop
181 || needs_maybe_uninit
David Tolnay5d121442020-03-17 22:14:40 -0700182 || needs_trycatch
David Tolnayb7a7cb62020-03-17 21:18:40 -0700183 {
David Tolnay736cbca2020-03-11 16:49:18 -0700184 writeln!(out, "// #include \"rust/cxx.h\"");
David Tolnayf51447e2020-03-06 14:14:27 -0800185 }
186
David Tolnayd1402742020-03-25 22:21:42 -0700187 if needs_rust_string {
188 out.next_section();
189 writeln!(out, "struct unsafe_bitcopy_t;");
190 }
191
David Tolnayb7a7cb62020-03-17 21:18:40 -0700192 write_header_section(out, needs_rust_string, "CXXBRIDGE02_RUST_STRING");
193 write_header_section(out, needs_rust_str, "CXXBRIDGE02_RUST_STR");
194 write_header_section(out, needs_rust_box, "CXXBRIDGE02_RUST_BOX");
195 write_header_section(out, needs_rust_error, "CXXBRIDGE02_RUST_ERROR");
196 write_header_section(out, needs_unsafe_bitcopy, "CXXBRIDGE02_RUST_BITCOPY");
David Tolnayf51447e2020-03-06 14:14:27 -0800197
198 if needs_manually_drop {
199 out.next_section();
David Tolnay4791f1c2020-03-17 21:53:16 -0700200 out.include.utility = true;
David Tolnayf51447e2020-03-06 14:14:27 -0800201 writeln!(out, "template <typename T>");
202 writeln!(out, "union ManuallyDrop {{");
203 writeln!(out, " T value;");
204 writeln!(
205 out,
206 " ManuallyDrop(T &&value) : value(::std::move(value)) {{}}",
207 );
208 writeln!(out, " ~ManuallyDrop() {{}}");
209 writeln!(out, "}};");
210 }
211
David Tolnay09011c32020-03-06 14:40:28 -0800212 if needs_maybe_uninit {
213 out.next_section();
214 writeln!(out, "template <typename T>");
215 writeln!(out, "union MaybeUninit {{");
216 writeln!(out, " T value;");
217 writeln!(out, " MaybeUninit() {{}}");
218 writeln!(out, " ~MaybeUninit() {{}}");
219 writeln!(out, "}};");
220 }
221
David Tolnay3e3e0af2020-03-17 22:42:49 -0700222 out.end_block("namespace cxxbridge02");
223
David Tolnay5d121442020-03-17 22:14:40 -0700224 if needs_trycatch {
David Tolnay3e3e0af2020-03-17 22:42:49 -0700225 out.begin_block("namespace behavior");
David Tolnay5d121442020-03-17 22:14:40 -0700226 out.include.exception = true;
David Tolnay04722332020-03-18 11:31:54 -0700227 out.include.type_traits = true;
228 out.include.utility = true;
229 writeln!(out, "class missing {{}};");
230 writeln!(out, "missing trycatch(...);");
David Tolnay3e3e0af2020-03-17 22:42:49 -0700231 writeln!(out);
David Tolnay04722332020-03-18 11:31:54 -0700232 writeln!(out, "template <typename Try, typename Fail>");
233 writeln!(out, "static typename std::enable_if<");
David Tolnay3e3e0af2020-03-17 22:42:49 -0700234 writeln!(
235 out,
David Tolnay04722332020-03-18 11:31:54 -0700236 " std::is_same<decltype(trycatch(std::declval<Try>(), std::declval<Fail>())),",
David Tolnay3e3e0af2020-03-17 22:42:49 -0700237 );
David Tolnay04722332020-03-18 11:31:54 -0700238 writeln!(out, " missing>::value>::type");
239 writeln!(out, "trycatch(Try &&func, Fail &&fail) noexcept try {{");
David Tolnay5d121442020-03-17 22:14:40 -0700240 writeln!(out, " func();");
241 writeln!(out, "}} catch (const ::std::exception &e) {{");
242 writeln!(out, " fail(e.what());");
243 writeln!(out, "}}");
David Tolnay3e3e0af2020-03-17 22:42:49 -0700244 out.end_block("namespace behavior");
David Tolnay5d121442020-03-17 22:14:40 -0700245 }
246
David Tolnay9ad1fbc2020-03-01 14:01:24 -0800247 out.end_block("namespace rust");
David Tolnay7db73692019-10-20 14:51:12 -0400248}
249
David Tolnayb7a7cb62020-03-17 21:18:40 -0700250fn write_header_section(out: &mut OutFile, needed: bool, section: &str) {
251 if needed {
252 out.next_section();
253 for line in include::get(section).lines() {
254 if !line.trim_start().starts_with("//") {
255 writeln!(out, "{}", line);
256 }
257 }
258 }
259}
260
David Tolnay7db73692019-10-20 14:51:12 -0400261fn write_struct(out: &mut OutFile, strct: &Struct) {
262 for line in strct.doc.to_string().lines() {
263 writeln!(out, "//{}", line);
264 }
265 writeln!(out, "struct {} final {{", strct.ident);
266 for field in &strct.fields {
267 write!(out, " ");
268 write_type_space(out, &field.ty);
269 writeln!(out, "{};", field.ident);
270 }
271 writeln!(out, "}};");
272}
273
274fn write_struct_decl(out: &mut OutFile, ident: &Ident) {
275 writeln!(out, "struct {};", ident);
276}
277
David Tolnay8861bee2020-01-20 18:39:24 -0800278fn write_struct_using(out: &mut OutFile, ident: &Ident) {
279 writeln!(out, "using {} = {};", ident, ident);
280}
281
David Tolnayebef4a22020-03-17 15:33:47 -0700282fn write_exception_glue(out: &mut OutFile, apis: &[Api]) {
283 let mut has_cxx_throws = false;
284 for api in apis {
285 if let Api::CxxFunction(efn) = api {
286 if efn.throws {
287 has_cxx_throws = true;
288 break;
289 }
290 }
291 }
292
293 if has_cxx_throws {
294 out.next_section();
David Tolnaye68634c2020-03-18 12:03:40 -0700295 writeln!(
David Tolnayebef4a22020-03-17 15:33:47 -0700296 out,
297 "const char *cxxbridge02$exception(const char *, size_t);",
298 );
299 }
300}
301
David Tolnay7db73692019-10-20 14:51:12 -0400302fn write_cxx_function_shim(out: &mut OutFile, efn: &ExternFn, types: &Types) {
David Tolnayebef4a22020-03-17 15:33:47 -0700303 if efn.throws {
304 write!(out, "::rust::Str::Repr ");
305 } else {
David Tolnay99642622020-03-25 13:07:35 -0700306 write_extern_return_type_space(out, &efn.ret, types);
David Tolnayebef4a22020-03-17 15:33:47 -0700307 }
David Tolnayd815de02020-03-29 21:29:17 -0700308 write!(out, "{}cxxbridge02${}(", out.namespace, efn.ident);
David Tolnay7db73692019-10-20 14:51:12 -0400309 for (i, arg) in efn.args.iter().enumerate() {
310 if i > 0 {
311 write!(out, ", ");
312 }
David Tolnaya46a2372020-03-06 10:03:48 -0800313 if arg.ty == RustString {
314 write!(out, "const ");
315 }
David Tolnay7db73692019-10-20 14:51:12 -0400316 write_extern_arg(out, arg, types);
317 }
David Tolnay277e3cc2020-03-17 00:11:01 -0700318 let indirect_return = indirect_return(efn, types);
David Tolnay7db73692019-10-20 14:51:12 -0400319 if indirect_return {
320 if !efn.args.is_empty() {
321 write!(out, ", ");
322 }
David Tolnay99642622020-03-25 13:07:35 -0700323 write_indirect_return_type_space(out, efn.ret.as_ref().unwrap());
David Tolnay7db73692019-10-20 14:51:12 -0400324 write!(out, "*return$");
325 }
326 writeln!(out, ") noexcept {{");
327 write!(out, " ");
328 write_return_type(out, &efn.ret);
329 write!(out, "(*{}$)(", efn.ident);
330 for (i, arg) in efn.args.iter().enumerate() {
331 if i > 0 {
332 write!(out, ", ");
333 }
334 write_type(out, &arg.ty);
335 }
336 writeln!(out, ") = {};", efn.ident);
337 write!(out, " ");
David Tolnayebef4a22020-03-17 15:33:47 -0700338 if efn.throws {
339 writeln!(out, "::rust::Str::Repr throw$;");
David Tolnay3e3e0af2020-03-17 22:42:49 -0700340 writeln!(out, " ::rust::behavior::trycatch(");
David Tolnay5d121442020-03-17 22:14:40 -0700341 writeln!(out, " [&] {{");
342 write!(out, " ");
David Tolnayebef4a22020-03-17 15:33:47 -0700343 }
David Tolnay7db73692019-10-20 14:51:12 -0400344 if indirect_return {
345 write!(out, "new (return$) ");
David Tolnay99642622020-03-25 13:07:35 -0700346 write_indirect_return_type(out, efn.ret.as_ref().unwrap());
David Tolnay7db73692019-10-20 14:51:12 -0400347 write!(out, "(");
David Tolnay99642622020-03-25 13:07:35 -0700348 } else if efn.ret.is_some() {
David Tolnay7db73692019-10-20 14:51:12 -0400349 write!(out, "return ");
David Tolnay99642622020-03-25 13:07:35 -0700350 }
351 match &efn.ret {
352 Some(Type::Ref(_)) => write!(out, "&"),
353 Some(Type::Str(_)) if !indirect_return => write!(out, "::rust::Str::Repr("),
354 _ => {}
David Tolnay7db73692019-10-20 14:51:12 -0400355 }
356 write!(out, "{}$(", efn.ident);
357 for (i, arg) in efn.args.iter().enumerate() {
358 if i > 0 {
359 write!(out, ", ");
360 }
361 if let Type::RustBox(_) = &arg.ty {
362 write_type(out, &arg.ty);
363 write!(out, "::from_raw({})", arg.ident);
364 } else if let Type::UniquePtr(_) = &arg.ty {
365 write_type(out, &arg.ty);
366 write!(out, "({})", arg.ident);
David Tolnaya46a2372020-03-06 10:03:48 -0800367 } else if arg.ty == RustString {
David Tolnaycc3767f2020-03-06 10:41:51 -0800368 write!(
369 out,
370 "::rust::String(::rust::unsafe_bitcopy, *{})",
371 arg.ident,
372 );
David Tolnay7db73692019-10-20 14:51:12 -0400373 } else if types.needs_indirect_abi(&arg.ty) {
David Tolnay4791f1c2020-03-17 21:53:16 -0700374 out.include.utility = true;
David Tolnay7e219b82020-03-01 13:14:51 -0800375 write!(out, "::std::move(*{})", arg.ident);
David Tolnay7db73692019-10-20 14:51:12 -0400376 } else {
377 write!(out, "{}", arg.ident);
378 }
379 }
380 write!(out, ")");
381 match &efn.ret {
382 Some(Type::RustBox(_)) => write!(out, ".into_raw()"),
383 Some(Type::UniquePtr(_)) => write!(out, ".release()"),
David Tolnay99642622020-03-25 13:07:35 -0700384 Some(Type::Str(_)) if !indirect_return => write!(out, ")"),
David Tolnay7db73692019-10-20 14:51:12 -0400385 _ => {}
386 }
387 if indirect_return {
388 write!(out, ")");
389 }
390 writeln!(out, ";");
David Tolnayebef4a22020-03-17 15:33:47 -0700391 if efn.throws {
392 out.include.cstring = true;
David Tolnay5d121442020-03-17 22:14:40 -0700393 writeln!(out, " throw$.ptr = nullptr;");
394 writeln!(out, " }},");
David Tolnay82c16172020-03-17 22:54:12 -0700395 writeln!(out, " [&](const char *catch$) noexcept {{");
David Tolnay5d121442020-03-17 22:14:40 -0700396 writeln!(out, " throw$.len = ::std::strlen(catch$);");
David Tolnayebef4a22020-03-17 15:33:47 -0700397 writeln!(
398 out,
David Tolnay5d121442020-03-17 22:14:40 -0700399 " throw$.ptr = cxxbridge02$exception(catch$, throw$.len);",
David Tolnayebef4a22020-03-17 15:33:47 -0700400 );
David Tolnay5d121442020-03-17 22:14:40 -0700401 writeln!(out, " }});");
David Tolnayebef4a22020-03-17 15:33:47 -0700402 writeln!(out, " return throw$;");
403 }
David Tolnay7db73692019-10-20 14:51:12 -0400404 writeln!(out, "}}");
405}
406
407fn write_rust_function_decl(out: &mut OutFile, efn: &ExternFn, types: &Types) {
David Tolnay1e548172020-03-16 13:37:09 -0700408 if efn.throws {
409 write!(out, "::rust::Str::Repr ");
410 } else {
David Tolnay99642622020-03-25 13:07:35 -0700411 write_extern_return_type_space(out, &efn.ret, types);
David Tolnay1e548172020-03-16 13:37:09 -0700412 }
David Tolnayd815de02020-03-29 21:29:17 -0700413 write!(out, "{}cxxbridge02${}(", out.namespace, efn.ident);
David Tolnay7db73692019-10-20 14:51:12 -0400414 for (i, arg) in efn.args.iter().enumerate() {
415 if i > 0 {
416 write!(out, ", ");
417 }
418 write_extern_arg(out, arg, types);
419 }
David Tolnay277e3cc2020-03-17 00:11:01 -0700420 if indirect_return(efn, types) {
David Tolnay7db73692019-10-20 14:51:12 -0400421 if !efn.args.is_empty() {
422 write!(out, ", ");
423 }
424 write_return_type(out, &efn.ret);
425 write!(out, "*return$");
426 }
427 writeln!(out, ") noexcept;");
428}
429
430fn write_rust_function_shim(out: &mut OutFile, efn: &ExternFn, types: &Types) {
David Tolnay7db73692019-10-20 14:51:12 -0400431 for line in efn.doc.to_string().lines() {
432 writeln!(out, "//{}", line);
433 }
434 write_return_type(out, &efn.ret);
435 write!(out, "{}(", efn.ident);
436 for (i, arg) in efn.args.iter().enumerate() {
437 if i > 0 {
438 write!(out, ", ");
439 }
440 write_type_space(out, &arg.ty);
441 write!(out, "{}", arg.ident);
442 }
David Tolnay1e548172020-03-16 13:37:09 -0700443 write!(out, ")");
444 if !efn.throws {
445 write!(out, " noexcept");
446 }
David Tolnay7db73692019-10-20 14:51:12 -0400447 if out.header {
448 writeln!(out, ";");
449 } else {
450 writeln!(out, " {{");
David Tolnayf51447e2020-03-06 14:14:27 -0800451 for arg in &efn.args {
452 if arg.ty != RustString && types.needs_indirect_abi(&arg.ty) {
David Tolnay4791f1c2020-03-17 21:53:16 -0700453 out.include.utility = true;
David Tolnayf51447e2020-03-06 14:14:27 -0800454 write!(out, " ::rust::ManuallyDrop<");
455 write_type(out, &arg.ty);
456 writeln!(out, "> {}$(::std::move({0}));", arg.ident);
457 }
458 }
David Tolnay7db73692019-10-20 14:51:12 -0400459 write!(out, " ");
David Tolnay277e3cc2020-03-17 00:11:01 -0700460 let indirect_return = indirect_return(efn, types);
David Tolnay7db73692019-10-20 14:51:12 -0400461 if indirect_return {
David Tolnay09011c32020-03-06 14:40:28 -0800462 write!(out, "::rust::MaybeUninit<");
David Tolnay7db73692019-10-20 14:51:12 -0400463 write_type(out, efn.ret.as_ref().unwrap());
David Tolnay09011c32020-03-06 14:40:28 -0800464 writeln!(out, "> return$;");
David Tolnay7db73692019-10-20 14:51:12 -0400465 write!(out, " ");
David Tolnay4a441222020-01-25 16:24:27 -0800466 } else if let Some(ret) = &efn.ret {
David Tolnay7db73692019-10-20 14:51:12 -0400467 write!(out, "return ");
David Tolnay5cd8d612020-03-06 15:56:30 -0800468 match ret {
469 Type::RustBox(_) => {
470 write_type(out, ret);
471 write!(out, "::from_raw(");
472 }
David Tolnay4b3a66e2020-03-06 16:14:00 -0800473 Type::UniquePtr(_) => {
474 write_type(out, ret);
475 write!(out, "(");
476 }
David Tolnay5cd8d612020-03-06 15:56:30 -0800477 Type::Ref(_) => write!(out, "*"),
478 _ => {}
David Tolnay4a441222020-01-25 16:24:27 -0800479 }
David Tolnay7db73692019-10-20 14:51:12 -0400480 }
David Tolnay1e548172020-03-16 13:37:09 -0700481 if efn.throws {
482 write!(out, "::rust::Str::Repr error$ = ");
483 }
David Tolnayd815de02020-03-29 21:29:17 -0700484 write!(out, "{}cxxbridge02${}(", out.namespace, efn.ident);
David Tolnay7db73692019-10-20 14:51:12 -0400485 for (i, arg) in efn.args.iter().enumerate() {
486 if i > 0 {
487 write!(out, ", ");
488 }
David Tolnaybaae4432020-03-01 20:20:10 -0800489 match &arg.ty {
490 Type::Str(_) => write!(out, "::rust::Str::Repr("),
491 ty if types.needs_indirect_abi(ty) => write!(out, "&"),
492 _ => {}
David Tolnay7db73692019-10-20 14:51:12 -0400493 }
494 write!(out, "{}", arg.ident);
David Tolnayf51447e2020-03-06 14:14:27 -0800495 match &arg.ty {
David Tolnay17955e22020-01-20 17:58:24 -0800496 Type::RustBox(_) => write!(out, ".into_raw()"),
497 Type::UniquePtr(_) => write!(out, ".release()"),
David Tolnaybaae4432020-03-01 20:20:10 -0800498 Type::Str(_) => write!(out, ")"),
David Tolnayf51447e2020-03-06 14:14:27 -0800499 ty if ty != RustString && types.needs_indirect_abi(ty) => write!(out, "$.value"),
David Tolnay17955e22020-01-20 17:58:24 -0800500 _ => {}
501 }
David Tolnay7db73692019-10-20 14:51:12 -0400502 }
503 if indirect_return {
504 if !efn.args.is_empty() {
505 write!(out, ", ");
506 }
David Tolnay09011c32020-03-06 14:40:28 -0800507 write!(out, "&return$.value");
David Tolnay7db73692019-10-20 14:51:12 -0400508 }
David Tolnay5cd8d612020-03-06 15:56:30 -0800509 write!(out, ")");
510 if let Some(ret) = &efn.ret {
David Tolnay4b3a66e2020-03-06 16:14:00 -0800511 if let Type::RustBox(_) | Type::UniquePtr(_) = ret {
David Tolnay5cd8d612020-03-06 15:56:30 -0800512 write!(out, ")");
513 }
514 }
515 writeln!(out, ";");
David Tolnay1e548172020-03-16 13:37:09 -0700516 if efn.throws {
517 writeln!(out, " if (error$.ptr) {{");
518 writeln!(out, " throw ::rust::Error(error$);");
519 writeln!(out, " }}");
520 }
David Tolnay7db73692019-10-20 14:51:12 -0400521 if indirect_return {
David Tolnay4791f1c2020-03-17 21:53:16 -0700522 out.include.utility = true;
David Tolnay09011c32020-03-06 14:40:28 -0800523 writeln!(out, " return ::std::move(return$.value);");
David Tolnay7db73692019-10-20 14:51:12 -0400524 }
525 writeln!(out, "}}");
526 }
527}
528
529fn write_return_type(out: &mut OutFile, ty: &Option<Type>) {
530 match ty {
531 None => write!(out, "void "),
532 Some(ty) => write_type_space(out, ty),
533 }
534}
535
David Tolnay277e3cc2020-03-17 00:11:01 -0700536fn indirect_return(efn: &ExternFn, types: &Types) -> bool {
537 efn.ret
538 .as_ref()
David Tolnay1e548172020-03-16 13:37:09 -0700539 .map_or(false, |ret| efn.throws || types.needs_indirect_abi(ret))
David Tolnay277e3cc2020-03-17 00:11:01 -0700540}
541
David Tolnay99642622020-03-25 13:07:35 -0700542fn write_indirect_return_type(out: &mut OutFile, ty: &Type) {
543 match ty {
544 Type::RustBox(ty) | Type::UniquePtr(ty) => {
545 write_type_space(out, &ty.inner);
546 write!(out, "*");
547 }
548 Type::Ref(ty) => {
549 if ty.mutability.is_none() {
550 write!(out, "const ");
551 }
552 write_type(out, &ty.inner);
553 write!(out, " *");
554 }
555 Type::Str(_) => write!(out, "::rust::Str::Repr"),
556 _ => write_type(out, ty),
557 }
558}
559
560fn write_indirect_return_type_space(out: &mut OutFile, ty: &Type) {
561 write_indirect_return_type(out, ty);
562 match ty {
563 Type::RustBox(_) | Type::UniquePtr(_) | Type::Ref(_) => {}
564 Type::Str(_) => write!(out, " "),
565 _ => write_space_after_type(out, ty),
566 }
567}
568
569fn write_extern_return_type_space(out: &mut OutFile, ty: &Option<Type>, types: &Types) {
David Tolnay7db73692019-10-20 14:51:12 -0400570 match ty {
571 Some(Type::RustBox(ty)) | Some(Type::UniquePtr(ty)) => {
572 write_type_space(out, &ty.inner);
573 write!(out, "*");
574 }
David Tolnay4a441222020-01-25 16:24:27 -0800575 Some(Type::Ref(ty)) => {
576 if ty.mutability.is_none() {
577 write!(out, "const ");
578 }
579 write_type(out, &ty.inner);
580 write!(out, " *");
581 }
David Tolnay750755e2020-03-01 13:04:08 -0800582 Some(Type::Str(_)) => write!(out, "::rust::Str::Repr "),
David Tolnay7db73692019-10-20 14:51:12 -0400583 Some(ty) if types.needs_indirect_abi(ty) => write!(out, "void "),
584 _ => write_return_type(out, ty),
585 }
586}
587
588fn write_extern_arg(out: &mut OutFile, arg: &Var, types: &Types) {
589 match &arg.ty {
590 Type::RustBox(ty) | Type::UniquePtr(ty) => {
591 write_type_space(out, &ty.inner);
592 write!(out, "*");
593 }
David Tolnay750755e2020-03-01 13:04:08 -0800594 Type::Str(_) => write!(out, "::rust::Str::Repr "),
David Tolnay7db73692019-10-20 14:51:12 -0400595 _ => write_type_space(out, &arg.ty),
596 }
597 if types.needs_indirect_abi(&arg.ty) {
598 write!(out, "*");
599 }
600 write!(out, "{}", arg.ident);
601}
602
603fn write_type(out: &mut OutFile, ty: &Type) {
604 match ty {
605 Type::Ident(ident) => match Atom::from(ident) {
606 Some(Bool) => write!(out, "bool"),
607 Some(U8) => write!(out, "uint8_t"),
608 Some(U16) => write!(out, "uint16_t"),
609 Some(U32) => write!(out, "uint32_t"),
610 Some(U64) => write!(out, "uint64_t"),
611 Some(Usize) => write!(out, "size_t"),
612 Some(I8) => write!(out, "int8_t"),
613 Some(I16) => write!(out, "int16_t"),
614 Some(I32) => write!(out, "int32_t"),
615 Some(I64) => write!(out, "int64_t"),
616 Some(Isize) => write!(out, "ssize_t"),
David Tolnay3383ae72020-03-13 01:12:26 -0700617 Some(F32) => write!(out, "float"),
618 Some(F64) => write!(out, "double"),
David Tolnay7e219b82020-03-01 13:14:51 -0800619 Some(CxxString) => write!(out, "::std::string"),
David Tolnay750755e2020-03-01 13:04:08 -0800620 Some(RustString) => write!(out, "::rust::String"),
David Tolnay7db73692019-10-20 14:51:12 -0400621 None => write!(out, "{}", ident),
622 },
623 Type::RustBox(ty) => {
David Tolnay750755e2020-03-01 13:04:08 -0800624 write!(out, "::rust::Box<");
David Tolnay7db73692019-10-20 14:51:12 -0400625 write_type(out, &ty.inner);
626 write!(out, ">");
627 }
628 Type::UniquePtr(ptr) => {
David Tolnay7e219b82020-03-01 13:14:51 -0800629 write!(out, "::std::unique_ptr<");
David Tolnay7db73692019-10-20 14:51:12 -0400630 write_type(out, &ptr.inner);
631 write!(out, ">");
632 }
633 Type::Ref(r) => {
634 if r.mutability.is_none() {
635 write!(out, "const ");
636 }
637 write_type(out, &r.inner);
638 write!(out, " &");
639 }
640 Type::Str(_) => {
David Tolnay750755e2020-03-01 13:04:08 -0800641 write!(out, "::rust::Str");
David Tolnay7db73692019-10-20 14:51:12 -0400642 }
David Tolnay417305a2020-03-18 13:54:00 -0700643 Type::Fn(_) => unimplemented!(),
David Tolnay2fb14e92020-03-15 23:11:38 -0700644 Type::Void(_) => unreachable!(),
David Tolnay7db73692019-10-20 14:51:12 -0400645 }
646}
647
648fn write_type_space(out: &mut OutFile, ty: &Type) {
649 write_type(out, ty);
David Tolnay99642622020-03-25 13:07:35 -0700650 write_space_after_type(out, ty);
651}
652
653fn write_space_after_type(out: &mut OutFile, ty: &Type) {
David Tolnay7db73692019-10-20 14:51:12 -0400654 match ty {
655 Type::Ident(_) | Type::RustBox(_) | Type::UniquePtr(_) | Type::Str(_) => write!(out, " "),
656 Type::Ref(_) => {}
David Tolnay417305a2020-03-18 13:54:00 -0700657 Type::Fn(_) => unimplemented!(),
David Tolnay2fb14e92020-03-15 23:11:38 -0700658 Type::Void(_) => unreachable!(),
David Tolnay7db73692019-10-20 14:51:12 -0400659 }
660}
661
662fn write_generic_instantiations(out: &mut OutFile, types: &Types) {
663 fn allow_unique_ptr(ident: &Ident) -> bool {
664 Atom::from(ident).is_none()
665 }
666
667 out.begin_block("extern \"C\"");
668 for ty in types {
669 if let Type::RustBox(ty) = ty {
670 if let Type::Ident(inner) = &ty.inner {
671 out.next_section();
672 write_rust_box_extern(out, inner);
673 }
674 } else if let Type::UniquePtr(ptr) = ty {
675 if let Type::Ident(inner) = &ptr.inner {
676 if allow_unique_ptr(inner) {
677 out.next_section();
678 write_unique_ptr(out, inner);
679 }
680 }
681 }
682 }
David Tolnay9ad1fbc2020-03-01 14:01:24 -0800683 out.end_block("extern \"C\"");
David Tolnay7db73692019-10-20 14:51:12 -0400684
David Tolnay750755e2020-03-01 13:04:08 -0800685 out.begin_block("namespace rust");
David Tolnay8c730492020-03-13 01:29:06 -0700686 out.begin_block("inline namespace cxxbridge02");
David Tolnay7db73692019-10-20 14:51:12 -0400687 for ty in types {
688 if let Type::RustBox(ty) = ty {
689 if let Type::Ident(inner) = &ty.inner {
690 write_rust_box_impl(out, inner);
691 }
692 }
693 }
David Tolnay8c730492020-03-13 01:29:06 -0700694 out.end_block("namespace cxxbridge02");
David Tolnay9ad1fbc2020-03-01 14:01:24 -0800695 out.end_block("namespace rust");
David Tolnay7db73692019-10-20 14:51:12 -0400696}
697
698fn write_rust_box_extern(out: &mut OutFile, ident: &Ident) {
699 let mut inner = String::new();
700 for name in &out.namespace {
701 inner += name;
702 inner += "::";
703 }
704 inner += &ident.to_string();
705 let instance = inner.replace("::", "$");
706
David Tolnay8c730492020-03-13 01:29:06 -0700707 writeln!(out, "#ifndef CXXBRIDGE02_RUST_BOX_{}", instance);
708 writeln!(out, "#define CXXBRIDGE02_RUST_BOX_{}", instance);
David Tolnay7db73692019-10-20 14:51:12 -0400709 writeln!(
710 out,
David Tolnay8c730492020-03-13 01:29:06 -0700711 "void cxxbridge02$box${}$uninit(::rust::Box<{}> *ptr) noexcept;",
David Tolnay7db73692019-10-20 14:51:12 -0400712 instance, inner,
713 );
714 writeln!(
715 out,
David Tolnay8c730492020-03-13 01:29:06 -0700716 "void cxxbridge02$box${}$drop(::rust::Box<{}> *ptr) noexcept;",
David Tolnay7db73692019-10-20 14:51:12 -0400717 instance, inner,
718 );
David Tolnay8c730492020-03-13 01:29:06 -0700719 writeln!(out, "#endif // CXXBRIDGE02_RUST_BOX_{}", instance);
David Tolnay7db73692019-10-20 14:51:12 -0400720}
721
722fn write_rust_box_impl(out: &mut OutFile, ident: &Ident) {
723 let mut inner = String::new();
724 for name in &out.namespace {
725 inner += name;
726 inner += "::";
727 }
728 inner += &ident.to_string();
729 let instance = inner.replace("::", "$");
730
731 writeln!(out, "template <>");
David Tolnay324437a2020-03-01 13:02:24 -0800732 writeln!(out, "void Box<{}>::uninit() noexcept {{", inner);
David Tolnay8c730492020-03-13 01:29:06 -0700733 writeln!(out, " return cxxbridge02$box${}$uninit(this);", instance);
David Tolnay7db73692019-10-20 14:51:12 -0400734 writeln!(out, "}}");
735
736 writeln!(out, "template <>");
David Tolnay324437a2020-03-01 13:02:24 -0800737 writeln!(out, "void Box<{}>::drop() noexcept {{", inner);
David Tolnay8c730492020-03-13 01:29:06 -0700738 writeln!(out, " return cxxbridge02$box${}$drop(this);", instance);
David Tolnay7db73692019-10-20 14:51:12 -0400739 writeln!(out, "}}");
David Tolnay7db73692019-10-20 14:51:12 -0400740}
741
742fn write_unique_ptr(out: &mut OutFile, ident: &Ident) {
David Tolnay4791f1c2020-03-17 21:53:16 -0700743 out.include.utility = true;
744
David Tolnay7db73692019-10-20 14:51:12 -0400745 let mut inner = String::new();
746 for name in &out.namespace {
747 inner += name;
748 inner += "::";
749 }
750 inner += &ident.to_string();
751 let instance = inner.replace("::", "$");
752
David Tolnay8c730492020-03-13 01:29:06 -0700753 writeln!(out, "#ifndef CXXBRIDGE02_UNIQUE_PTR_{}", instance);
754 writeln!(out, "#define CXXBRIDGE02_UNIQUE_PTR_{}", instance);
David Tolnay7db73692019-10-20 14:51:12 -0400755 writeln!(
756 out,
David Tolnay7e219b82020-03-01 13:14:51 -0800757 "static_assert(sizeof(::std::unique_ptr<{}>) == sizeof(void *), \"\");",
David Tolnay7db73692019-10-20 14:51:12 -0400758 inner,
759 );
760 writeln!(
761 out,
David Tolnay7e219b82020-03-01 13:14:51 -0800762 "static_assert(alignof(::std::unique_ptr<{}>) == alignof(void *), \"\");",
David Tolnay7db73692019-10-20 14:51:12 -0400763 inner,
764 );
765 writeln!(
766 out,
David Tolnay8c730492020-03-13 01:29:06 -0700767 "void cxxbridge02$unique_ptr${}$null(::std::unique_ptr<{}> *ptr) noexcept {{",
David Tolnay7db73692019-10-20 14:51:12 -0400768 instance, inner,
769 );
David Tolnay7e219b82020-03-01 13:14:51 -0800770 writeln!(out, " new (ptr) ::std::unique_ptr<{}>();", inner);
David Tolnay7db73692019-10-20 14:51:12 -0400771 writeln!(out, "}}");
772 writeln!(
773 out,
David Tolnay8c730492020-03-13 01:29:06 -0700774 "void cxxbridge02$unique_ptr${}$new(::std::unique_ptr<{}> *ptr, {} *value) noexcept {{",
David Tolnay7db73692019-10-20 14:51:12 -0400775 instance, inner, inner,
776 );
777 writeln!(
778 out,
David Tolnay7e219b82020-03-01 13:14:51 -0800779 " new (ptr) ::std::unique_ptr<{}>(new {}(::std::move(*value)));",
David Tolnay7db73692019-10-20 14:51:12 -0400780 inner, inner,
781 );
782 writeln!(out, "}}");
783 writeln!(
784 out,
David Tolnay8c730492020-03-13 01:29:06 -0700785 "void cxxbridge02$unique_ptr${}$raw(::std::unique_ptr<{}> *ptr, {} *raw) noexcept {{",
David Tolnay7db73692019-10-20 14:51:12 -0400786 instance, inner, inner,
787 );
David Tolnay7e219b82020-03-01 13:14:51 -0800788 writeln!(out, " new (ptr) ::std::unique_ptr<{}>(raw);", inner);
David Tolnay7db73692019-10-20 14:51:12 -0400789 writeln!(out, "}}");
790 writeln!(
791 out,
David Tolnay8c730492020-03-13 01:29:06 -0700792 "const {} *cxxbridge02$unique_ptr${}$get(const ::std::unique_ptr<{}>& ptr) noexcept {{",
David Tolnay7db73692019-10-20 14:51:12 -0400793 inner, instance, inner,
794 );
795 writeln!(out, " return ptr.get();");
796 writeln!(out, "}}");
797 writeln!(
798 out,
David Tolnay8c730492020-03-13 01:29:06 -0700799 "{} *cxxbridge02$unique_ptr${}$release(::std::unique_ptr<{}>& ptr) noexcept {{",
David Tolnay7db73692019-10-20 14:51:12 -0400800 inner, instance, inner,
801 );
802 writeln!(out, " return ptr.release();");
803 writeln!(out, "}}");
804 writeln!(
805 out,
David Tolnay8c730492020-03-13 01:29:06 -0700806 "void cxxbridge02$unique_ptr${}$drop(::std::unique_ptr<{}> *ptr) noexcept {{",
David Tolnay7db73692019-10-20 14:51:12 -0400807 instance, inner,
808 );
809 writeln!(out, " ptr->~unique_ptr();");
810 writeln!(out, "}}");
David Tolnay8c730492020-03-13 01:29:06 -0700811 writeln!(out, "#endif // CXXBRIDGE02_UNIQUE_PTR_{}", instance);
David Tolnay7db73692019-10-20 14:51:12 -0400812}