cxx_gen/gen/
builtin.rs

1use crate::gen::block::Block;
2use crate::gen::ifndef;
3use crate::gen::out::{Content, OutFile};
4
5#[derive(Default, PartialEq)]
6pub(crate) struct Builtins<'a> {
7    pub panic: bool,
8    pub rust_string: bool,
9    pub rust_str: bool,
10    pub rust_slice: bool,
11    pub rust_box: bool,
12    pub rust_vec: bool,
13    pub rust_fn: bool,
14    pub rust_isize: bool,
15    pub opaque: bool,
16    pub layout: bool,
17    pub unsafe_bitcopy: bool,
18    pub unsafe_bitcopy_t: bool,
19    pub rust_error: bool,
20    pub manually_drop: bool,
21    pub maybe_uninit: bool,
22    pub trycatch: bool,
23    pub ptr_len: bool,
24    pub repr_fat: bool,
25    pub rust_str_new_unchecked: bool,
26    pub rust_str_repr: bool,
27    pub rust_slice_new: bool,
28    pub rust_slice_repr: bool,
29    pub relocatable: bool,
30    pub relocatable_or_array: bool,
31    pub friend_impl: bool,
32    pub is_complete: bool,
33    pub destroy: bool,
34    pub deleter_if: bool,
35    pub alignmax: bool,
36    pub content: Content<'a>,
37}
38
39impl<'a> Builtins<'a> {
40    pub(crate) fn new() -> Self {
41        Builtins::default()
42    }
43}
44
45pub(super) fn write(out: &mut OutFile) {
46    if out.builtin == Default::default() {
47        return;
48    }
49
50    let include = &mut out.include;
51    let builtin = &mut out.builtin;
52    let out = &mut builtin.content;
53
54    if builtin.rust_string {
55        include.array = true;
56        include.cstdint = true;
57        include.string = true;
58    }
59
60    if builtin.rust_str {
61        include.array = true;
62        include.cstdint = true;
63        include.string = true;
64        include.string_view = true;
65        builtin.friend_impl = true;
66    }
67
68    if builtin.rust_vec {
69        include.algorithm = true;
70        include.array = true;
71        include.cassert = true;
72        include.cstddef = true;
73        include.cstdint = true;
74        include.initializer_list = true;
75        include.iterator = true;
76        include.new = true;
77        include.stdexcept = true;
78        include.type_traits = true;
79        include.utility = true;
80        builtin.panic = true;
81        builtin.rust_slice = true;
82        builtin.unsafe_bitcopy_t = true;
83    }
84
85    if builtin.rust_slice {
86        include.array = true;
87        include.cassert = true;
88        include.cstddef = true;
89        include.cstdint = true;
90        include.iterator = true;
91        include.ranges = true;
92        include.stdexcept = true;
93        include.type_traits = true;
94        builtin.friend_impl = true;
95        builtin.layout = true;
96        builtin.panic = true;
97    }
98
99    if builtin.rust_box {
100        include.new = true;
101        include.type_traits = true;
102        include.utility = true;
103    }
104
105    if builtin.rust_fn {
106        include.utility = true;
107    }
108
109    if builtin.rust_error {
110        include.exception = true;
111        builtin.friend_impl = true;
112    }
113
114    if builtin.rust_isize {
115        include.basetsd = true;
116        include.sys_types = true;
117    }
118
119    if builtin.relocatable_or_array {
120        include.cstddef = true;
121        builtin.relocatable = true;
122    }
123
124    if builtin.relocatable {
125        include.type_traits = true;
126    }
127
128    if builtin.layout {
129        include.type_traits = true;
130        include.cstddef = true;
131        builtin.is_complete = true;
132    }
133
134    if builtin.is_complete {
135        include.cstddef = true;
136        include.type_traits = true;
137    }
138
139    if builtin.unsafe_bitcopy {
140        builtin.unsafe_bitcopy_t = true;
141    }
142
143    if builtin.trycatch {
144        builtin.ptr_len = true;
145    }
146
147    out.begin_block(Block::Namespace("rust"));
148    out.begin_block(Block::InlineNamespace("cxxbridge1"));
149
150    let cxx_header = include.has_cxx_header();
151    if !cxx_header {
152        writeln!(out, "// #include \"rust/cxx.h\"");
153
154        ifndef::write(out, builtin.panic, "CXXBRIDGE1_PANIC");
155
156        if builtin.rust_string {
157            out.next_section();
158            writeln!(out, "struct unsafe_bitcopy_t;");
159        }
160
161        if builtin.friend_impl {
162            out.begin_block(Block::AnonymousNamespace);
163            writeln!(out, "template <typename T>");
164            writeln!(out, "class impl;");
165            out.end_block(Block::AnonymousNamespace);
166        }
167
168        out.next_section();
169        if builtin.rust_str && !builtin.rust_string {
170            writeln!(out, "class String;");
171        }
172        if builtin.layout && !builtin.opaque {
173            writeln!(out, "class Opaque;");
174        }
175
176        if builtin.rust_slice {
177            out.next_section();
178            writeln!(out, "template <typename T>");
179            writeln!(out, "::std::size_t size_of();");
180            writeln!(out, "template <typename T>");
181            writeln!(out, "::std::size_t align_of();");
182        }
183
184        ifndef::write(out, builtin.rust_string, "CXXBRIDGE1_RUST_STRING");
185        ifndef::write(out, builtin.rust_str, "CXXBRIDGE1_RUST_STR");
186        ifndef::write(out, builtin.rust_slice, "CXXBRIDGE1_RUST_SLICE");
187        ifndef::write(out, builtin.rust_box, "CXXBRIDGE1_RUST_BOX");
188        ifndef::write(out, builtin.unsafe_bitcopy_t, "CXXBRIDGE1_RUST_BITCOPY_T");
189        ifndef::write(out, builtin.unsafe_bitcopy, "CXXBRIDGE1_RUST_BITCOPY");
190        ifndef::write(out, builtin.rust_vec, "CXXBRIDGE1_RUST_VEC");
191        ifndef::write(out, builtin.rust_fn, "CXXBRIDGE1_RUST_FN");
192        ifndef::write(out, builtin.rust_error, "CXXBRIDGE1_RUST_ERROR");
193        ifndef::write(out, builtin.rust_isize, "CXXBRIDGE1_RUST_ISIZE");
194        ifndef::write(out, builtin.opaque, "CXXBRIDGE1_RUST_OPAQUE");
195        ifndef::write(out, builtin.is_complete, "CXXBRIDGE1_IS_COMPLETE");
196        ifndef::write(out, builtin.layout, "CXXBRIDGE1_LAYOUT");
197        ifndef::write(out, builtin.relocatable, "CXXBRIDGE1_RELOCATABLE");
198    }
199
200    if builtin.rust_str_new_unchecked {
201        out.next_section();
202        writeln!(out, "class Str::uninit {{}};");
203        writeln!(out, "inline Str::Str(uninit) noexcept {{}}");
204    }
205
206    if builtin.rust_slice_new {
207        out.next_section();
208        writeln!(out, "template <typename T>");
209        writeln!(out, "class Slice<T>::uninit {{}};");
210        writeln!(out, "template <typename T>");
211        writeln!(out, "inline Slice<T>::Slice(uninit) noexcept {{}}");
212    }
213
214    out.begin_block(Block::Namespace("repr"));
215
216    if builtin.repr_fat {
217        include.array = true;
218        include.cstdint = true;
219        out.next_section();
220        writeln!(out, "using Fat = ::std::array<::std::uintptr_t, 2>;");
221    }
222
223    if builtin.ptr_len {
224        include.cstddef = true;
225        out.next_section();
226        writeln!(out, "struct PtrLen final {{");
227        writeln!(out, "  void *ptr;");
228        writeln!(out, "  ::std::size_t len;");
229        writeln!(out, "}};");
230    }
231
232    if builtin.alignmax {
233        include.cstddef = true;
234        out.next_section();
235        writeln!(out, "#ifndef CXXBRIDGE_ALIGNMAX");
236        writeln!(out, "#define CXXBRIDGE_ALIGNMAX");
237        // This would be cleaner as the following, but GCC does not implement
238        // that correctly. <https://gcc.gnu.org/bugzilla/show_bug.cgi?id=64236>
239        //
240        //     template <::std::size_t... N>
241        //     class alignas(N...) alignmax {};
242        //
243        // Next, it could be this, but MSVC does not implement this correctly.
244        //
245        //     template <::std::size_t... N>
246        //     class alignmax { alignas(N...) union {} members; };
247        //
248        writeln!(out, "template <::std::size_t N>");
249        writeln!(out, "class alignas(N) aligned {{}};");
250        writeln!(out, "template <typename... T>");
251        writeln!(
252            out,
253            "class alignmax_t {{ alignas(T...) union {{}} members; }};",
254        );
255        writeln!(out, "template <::std::size_t... N>");
256        writeln!(out, "using alignmax = alignmax_t<aligned<N>...>;");
257        writeln!(out, "#endif // CXXBRIDGE_ALIGNMAX");
258    }
259
260    out.end_block(Block::Namespace("repr"));
261
262    out.begin_block(Block::Namespace("detail"));
263
264    if builtin.maybe_uninit {
265        include.cstddef = true;
266        include.new = true;
267        out.next_section();
268        writeln!(out, "template <typename T, typename = void *>");
269        writeln!(out, "struct operator_new {{");
270        writeln!(
271            out,
272            "  void *operator()(::std::size_t sz) {{ return ::operator new(sz); }}",
273        );
274        writeln!(out, "}};");
275        out.next_section();
276        writeln!(out, "template <typename T>");
277        writeln!(
278            out,
279            "struct operator_new<T, decltype(T::operator new(sizeof(T)))> {{",
280        );
281        writeln!(
282            out,
283            "  void *operator()(::std::size_t sz) {{ return T::operator new(sz); }}",
284        );
285        writeln!(out, "}};");
286    }
287
288    if builtin.trycatch {
289        include.string = true;
290        out.next_section();
291        writeln!(out, "class Fail final {{");
292        writeln!(out, "  ::rust::repr::PtrLen &throw$;");
293        writeln!(out, "public:");
294        writeln!(
295            out,
296            "  Fail(::rust::repr::PtrLen &throw$) noexcept : throw$(throw$) {{}}",
297        );
298        writeln!(out, "  void operator()(char const *) noexcept;");
299        writeln!(out, "  void operator()(std::string const &) noexcept;");
300        writeln!(out, "}};");
301    }
302
303    out.end_block(Block::Namespace("detail"));
304
305    if builtin.manually_drop {
306        out.next_section();
307        include.utility = true;
308        writeln!(out, "template <typename T>");
309        writeln!(out, "union ManuallyDrop {{");
310        writeln!(out, "  T value;");
311        writeln!(
312            out,
313            "  ManuallyDrop(T &&value) : value(::std::move(value)) {{}}",
314        );
315        writeln!(out, "  ~ManuallyDrop() {{}}");
316        writeln!(out, "}};");
317    }
318
319    if builtin.maybe_uninit {
320        include.cstddef = true;
321        out.next_section();
322        writeln!(out, "template <typename T>");
323        writeln!(out, "union MaybeUninit {{");
324        writeln!(out, "  T value;");
325        writeln!(
326            out,
327            "  void *operator new(::std::size_t sz) {{ return detail::operator_new<T>{{}}(sz); }}",
328        );
329        writeln!(out, "  MaybeUninit() {{}}");
330        writeln!(out, "  ~MaybeUninit() {{}}");
331        writeln!(out, "}};");
332    }
333
334    out.begin_block(Block::AnonymousNamespace);
335
336    if builtin.rust_str_new_unchecked || builtin.rust_str_repr {
337        out.next_section();
338        writeln!(out, "template <>");
339        writeln!(out, "class impl<Str> final {{");
340        writeln!(out, "public:");
341        if builtin.rust_str_new_unchecked {
342            writeln!(
343                out,
344                "  static Str new_unchecked(repr::Fat repr) noexcept {{",
345            );
346            writeln!(out, "    Str str = Str::uninit{{}};");
347            writeln!(out, "    str.repr = repr;");
348            writeln!(out, "    return str;");
349            writeln!(out, "  }}");
350        }
351        if builtin.rust_str_repr {
352            writeln!(out, "  static repr::Fat repr(Str str) noexcept {{");
353            writeln!(out, "    return str.repr;");
354            writeln!(out, "  }}");
355        }
356        writeln!(out, "}};");
357    }
358
359    if builtin.rust_slice_new || builtin.rust_slice_repr {
360        out.next_section();
361        writeln!(out, "template <typename T>");
362        writeln!(out, "class impl<Slice<T>> final {{");
363        writeln!(out, "public:");
364        if builtin.rust_slice_new {
365            writeln!(out, "  static Slice<T> slice(repr::Fat repr) noexcept {{");
366            writeln!(out, "    Slice<T> slice = typename Slice<T>::uninit{{}};");
367            writeln!(out, "    slice.repr = repr;");
368            writeln!(out, "    return slice;");
369            writeln!(out, "  }}");
370        }
371        if builtin.rust_slice_repr {
372            writeln!(out, "  static repr::Fat repr(Slice<T> slice) noexcept {{");
373            writeln!(out, "    return slice.repr;");
374            writeln!(out, "  }}");
375        }
376        writeln!(out, "}};");
377    }
378
379    if builtin.rust_error {
380        out.next_section();
381        writeln!(out, "template <>");
382        writeln!(out, "class impl<Error> final {{");
383        writeln!(out, "public:");
384        writeln!(out, "  static Error error(repr::PtrLen repr) noexcept {{");
385        writeln!(out, "    Error error;");
386        writeln!(out, "    error.msg = static_cast<char const *>(repr.ptr);");
387        writeln!(out, "    error.len = repr.len;");
388        writeln!(out, "    return error;");
389        writeln!(out, "  }}");
390        writeln!(out, "}};");
391    }
392
393    if builtin.destroy {
394        out.next_section();
395        writeln!(out, "template <typename T>");
396        writeln!(out, "void destroy(T *ptr) {{");
397        writeln!(out, "  ptr->~T();");
398        writeln!(out, "}}");
399    }
400
401    if builtin.deleter_if {
402        out.next_section();
403        writeln!(out, "template <bool> struct deleter_if {{");
404        writeln!(out, "  template <typename T> void operator()(T *) {{}}");
405        writeln!(out, "}};");
406        out.next_section();
407        writeln!(out, "template <> struct deleter_if<true> {{");
408        writeln!(
409            out,
410            "  template <typename T> void operator()(T *ptr) {{ ptr->~T(); }}",
411        );
412        writeln!(out, "}};");
413    }
414
415    if builtin.relocatable_or_array {
416        out.next_section();
417        writeln!(out, "template <typename T>");
418        writeln!(out, "struct IsRelocatableOrArray : IsRelocatable<T> {{}};");
419        writeln!(out, "template <typename T, ::std::size_t N>");
420        writeln!(
421            out,
422            "struct IsRelocatableOrArray<T[N]> : IsRelocatableOrArray<T> {{}};",
423        );
424    }
425
426    out.end_block(Block::AnonymousNamespace);
427    out.end_block(Block::InlineNamespace("cxxbridge1"));
428
429    if builtin.trycatch {
430        out.begin_block(Block::Namespace("behavior"));
431        include.exception = true;
432        include.type_traits = true;
433        include.utility = true;
434        writeln!(out, "class missing {{}};");
435        writeln!(out, "missing trycatch(...);");
436        writeln!(out);
437        writeln!(out, "template <typename Try, typename Fail>");
438        writeln!(out, "static typename ::std::enable_if<");
439        writeln!(
440            out,
441            "    ::std::is_same<decltype(trycatch(::std::declval<Try>(), ::std::declval<Fail>())),",
442        );
443        writeln!(out, "                 missing>::value>::type");
444        writeln!(out, "trycatch(Try &&func, Fail &&fail) noexcept try {{");
445        writeln!(out, "  func();");
446        writeln!(out, "}} catch (::std::exception const &e) {{");
447        writeln!(out, "  fail(e.what());");
448        writeln!(out, "}}");
449        out.end_block(Block::Namespace("behavior"));
450    }
451
452    out.end_block(Block::Namespace("rust"));
453}