cxx_gen/gen/
builtin.rs

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