Skip to main content

zngur_generator/
rust.rs

1use std::fmt::Write;
2
3use itertools::Itertools;
4use sha2::{Digest, Sha256};
5
6use crate::{
7    ZngurTrait, ZngurWellknownTrait, ZngurWellknownTraitData,
8    cpp::{CppFnSig, CppLayoutPolicy, CppPath, CppTraitDefinition, CppTraitMethod, CppType},
9};
10
11use zngur_def::*;
12
13pub trait IntoCpp {
14    fn into_cpp(&self, namespace: &str, crate_name: &str) -> CppType;
15}
16
17impl IntoCpp for RustPathAndGenerics {
18    fn into_cpp(&self, namespace: &str, crate_name: &str) -> CppType {
19        let RustPathAndGenerics {
20            path,
21            generics,
22            named_generics,
23        } = self;
24        let named_generics = named_generics.iter().sorted_by_key(|x| &x.0).map(|x| &x.1);
25        CppType {
26            path: CppPath::from_rust_path(path, namespace, crate_name),
27            generic_args: generics
28                .iter()
29                .chain(named_generics)
30                .map(|x| x.into_cpp(namespace, crate_name))
31                .collect(),
32            tail: None,
33        }
34    }
35}
36
37impl IntoCpp for RustTrait {
38    fn into_cpp(&self, namespace: &str, crate_name: &str) -> CppType {
39        match self {
40            RustTrait::Normal(pg) => pg.into_cpp(namespace, crate_name),
41            RustTrait::Fn {
42                name,
43                inputs,
44                output,
45            } => CppType {
46                path: CppPath::from(&*format!("{namespace}::{name}")),
47                generic_args: inputs
48                    .iter()
49                    .chain(Some(&**output))
50                    .map(|x| x.into_cpp(namespace, crate_name))
51                    .collect(),
52                tail: None,
53            },
54        }
55    }
56}
57
58impl IntoCpp for RustType {
59    fn into_cpp(&self, namespace: &str, crate_name: &str) -> CppType {
60        fn for_builtin(this: &RustType, namespace: &str, crate_name: &str) -> Option<CppType> {
61            match this {
62                RustType::Primitive(s) => match s {
63                    PrimitiveRustType::Uint(s) => Some(CppType::from(&*format!("uint{s}_t"))),
64                    PrimitiveRustType::Int(s) => Some(CppType::from(&*format!("int{s}_t"))),
65                    PrimitiveRustType::Float(32) => Some(CppType::from("float_t")),
66                    PrimitiveRustType::Float(64) => Some(CppType::from("double_t")),
67                    PrimitiveRustType::Float(_) => unreachable!(),
68                    PrimitiveRustType::Usize => Some(CppType::from("size_t")),
69                    PrimitiveRustType::Bool | PrimitiveRustType::Str | PrimitiveRustType::Char => {
70                        None
71                    }
72                },
73                RustType::Raw(Mutability::Mut, t) => Some(CppType::from(&*format!(
74                    "{}*",
75                    for_builtin(t, namespace, crate_name)?
76                        .to_string()
77                        .strip_prefix("::")?
78                ))),
79                RustType::Raw(Mutability::Not, t) => Some(CppType::from(&*format!(
80                    "{} const*",
81                    for_builtin(t, namespace, crate_name)?
82                        .to_string()
83                        .strip_prefix("::")?
84                ))),
85                _ => None,
86            }
87        }
88        if let Some(builtin) = for_builtin(self, namespace, crate_name) {
89            return builtin;
90        }
91        match self {
92            RustType::Primitive(s) => match s {
93                PrimitiveRustType::Bool => CppType::from(&*format!("{namespace}::Bool")),
94                PrimitiveRustType::Str => CppType::from(&*format!("{namespace}::Str")),
95                PrimitiveRustType::Char => CppType::from(&*format!("{namespace}::Char")),
96                _ => unreachable!(),
97            },
98            RustType::Boxed(t) => CppType {
99                path: CppPath::from(&*format!("{namespace}::Box")),
100                generic_args: vec![t.into_cpp(namespace, crate_name)],
101                tail: None,
102            },
103            RustType::Ref(m, t) => CppType {
104                path: match m {
105                    Mutability::Mut => CppPath::from(&*format!("{}::RefMut", namespace)),
106                    Mutability::Not => CppPath::from(&*format!("{}::Ref", namespace)),
107                },
108                generic_args: vec![t.into_cpp(namespace, crate_name)],
109                tail: None,
110            },
111            RustType::Slice(s) => CppType {
112                path: CppPath::from(&*format!("{namespace}::Slice")),
113                generic_args: vec![s.into_cpp(namespace, crate_name)],
114                tail: None,
115            },
116            RustType::Raw(m, t) => CppType {
117                path: match m {
118                    Mutability::Mut => CppPath::from(&*format!("{namespace}::RawMut")),
119                    Mutability::Not => CppPath::from(&*format!("{namespace}::Raw")),
120                },
121                generic_args: vec![t.into_cpp(namespace, crate_name)],
122                tail: None,
123            },
124            RustType::Adt(pg) => pg.into_cpp(namespace, crate_name),
125            RustType::Tuple(v) => {
126                if v.is_empty() {
127                    return CppType::from(&*format!("{namespace}::Unit"));
128                }
129                CppType {
130                    path: CppPath::from(&*format!("{namespace}::Tuple")),
131                    generic_args: v
132                        .into_iter()
133                        .map(|x| x.into_cpp(namespace, crate_name))
134                        .collect(),
135                    tail: None,
136                }
137            }
138            RustType::Dyn(tr, marker_bounds) => {
139                let tr_as_cpp_type = tr.into_cpp(namespace, crate_name);
140                CppType {
141                    path: CppPath::from(&*format!("{namespace}::Dyn")),
142                    generic_args: [tr_as_cpp_type]
143                        .into_iter()
144                        .chain(
145                            marker_bounds
146                                .iter()
147                                .map(|x| CppType::from(&*format!("{namespace}::{x}"))),
148                        )
149                        .collect(),
150                    tail: None,
151                }
152            }
153            RustType::Impl(_, _) => panic!("impl Trait is invalid in C++"),
154            RustType::TypeVar(_) => {
155                unreachable!("should not attempt to generate definition for unbound TypeVar")
156            }
157        }
158    }
159}
160
161pub struct RustFile {
162    pub text: String,
163    pub panic_to_exception: bool,
164    pub mangling_base: String,
165}
166
167impl RustFile {
168    pub fn new(mangling_base: &str) -> Self {
169        Self {
170            text: r#"
171macro_rules! __zngur_str_as_array {
172    ($s:expr) => {{
173        const VAL: &str = $s;
174        // SAFETY: `VAL` has at least size `N` because it's const len is right there.
175        const ARR: [u8; VAL.len()] = unsafe { *(VAL.as_bytes() as *const [u8]).cast() };
176        ARR
177    }};
178}
179
180pub const fn __zngur_usize_num_digits(val: usize) -> usize {
181    // docs currently say 64bit only but that's a bug
182    if val == 0 { 1 } else { val.ilog10() as usize + 1 }
183}
184
185pub const fn __zngur_usize_digit(val: usize, digit: usize) -> u8 {
186    let mut temp = val;
187    let mut i = 0;
188    while i < digit {
189        temp /= 10;
190        i += 1;
191    }
192    if temp == 0 && val > 0 {
193        ::core::panic!("no such digit!")
194    } else {
195        (temp % 10) as u8
196    }
197}
198
199pub const fn __zngur_digit_to_ascii(digit: u8) -> u8 {
200    ::core::assert!(digit <= 9);
201    digit + b'0'
202}
203
204pub const fn __zngur_usize_to_digit_array<const N: usize>(val: usize) -> [u8; N] {
205    let mut arr: [u8; N] = [0; N];
206    let mut i = 0;
207    while i < N {
208        arr[N - 1 - i] = __zngur_digit_to_ascii(__zngur_usize_digit(val, i));
209        i += 1;
210    }
211    arr
212}
213
214macro_rules! __zngur_usize_to_str {
215    ($x:expr) => {{
216        const VAL: usize = $x;
217        const ARR: [u8; __zngur_usize_num_digits(VAL)] = __zngur_usize_to_digit_array(VAL);
218        // SAFETY: `ARR` is an ascii byte array which is utf8 compliant
219        const STR: &str = unsafe { str::from_utf8_unchecked(&ARR) };
220        STR
221    }};
222}
223
224pub const fn __zngur_const_str_array_concat<const T: usize, const N: usize, const M: usize>(
225    x: [u8; N],
226    y: [u8; M],
227) -> [u8; T] {
228    ::core::assert!(N + M == T);
229    let mut arr: [u8; T] = [0; T];
230    let mut i = 0;
231    while i < N {
232        arr[i] = x[i];
233        i += 1;
234    }
235    while i - N < M {
236        arr[i] = y[i - N];
237        i += 1;
238    }
239    arr
240}
241
242macro_rules! __zngur_const_str_concat {
243
244    ( $x:expr, $y:expr $(,)? ) => {{
245        const X: &str = $x;
246        const Y: &str = $y;
247        const LEN: usize = X.len() + Y.len();
248        const ARR: [u8; LEN] = __zngur_const_str_array_concat::<LEN, {X.len()}, {Y.len()}>(
249            __zngur_str_as_array!(X),
250            __zngur_str_as_array!(Y),
251        );
252        // SAFETY: `ARR` is an concatenated utf8 byte array built from validated `const str&`
253        const STR: &str =  unsafe { str::from_utf8_unchecked(&ARR) };
254        STR
255    }};
256    ( $x:expr, $y:expr, $($rest:expr),+ $(,)? ) => {
257        __zngur_const_str_concat!($x, __zngur_const_str_concat!( $y, $($rest),+ ))
258    };
259
260}
261
262macro_rules! __zngur_assert_is_copy {
263    ($x:ty $(,)?) => {
264        const _: () = {
265            const fn static_assert_is_copy<T: Copy>() {}
266            static_assert_is_copy::<$x>();
267        };
268    };
269}
270
271macro_rules! __zngur_assert_size {
272    ($x:ty, $size:expr $(,)?) => {
273        const _: () = ::core::assert!(
274            $size == ::core::mem::size_of::<$x>(),
275            "{}",
276            __zngur_const_str_concat!(
277                "zngur declared size of ",
278                stringify!($x),
279                " is incorrect: expected ",
280                __zngur_usize_to_str!($size),
281                " , real size is ",
282                __zngur_usize_to_str!(::core::mem::size_of::<$x>()),
283            )
284        );
285    };
286}
287
288macro_rules! __zngur_assert_align {
289    ($x:ty, $align:expr $(,)?) => {
290        const _: () = ::core::assert!(
291            $align == ::core::mem::align_of::<$x>(),
292            "{}",
293            __zngur_const_str_concat!(
294                "zngur declared align of ",
295                stringify!($x),
296                " is incorrect: expected ",
297                __zngur_usize_to_str!($align),
298                " , real align is ",
299                __zngur_usize_to_str!(::core::mem::align_of::<$x>()),
300            )
301        );
302    };
303}
304
305macro_rules! __zngur_assert_size_conservative {
306    ($x:ty, $size:expr $(,)?) => {
307        const _: () = ::core::assert!(
308            $size >= ::core::mem::size_of::<$x>(),
309            "{}",
310            __zngur_const_str_concat!(
311                "zngur declared conservative size of ",
312                stringify!($x),
313                " is incorrect: expected size less than or equal to ",
314                __zngur_usize_to_str!($size),
315                " , real size is ",
316                __zngur_usize_to_str!(::core::mem::size_of::<$x>()),
317            )
318        );
319    };
320}
321
322macro_rules! __zngur_assert_align_conservative {
323    ($x:ty, $align:expr $(,)?) => {
324        const _: () = ::core::assert!(
325            $align >= ::core::mem::align_of::<$x>(),
326            "{}",
327            __zngur_const_str_concat!(
328                "zngur declared conservative align of ",
329                stringify!($x),
330                " is incorrect: expected align less than or equal to ",
331                __zngur_usize_to_str!($align),
332                " , real align is ",
333                __zngur_usize_to_str!(::core::mem::align_of::<$x>()),
334            )
335        );
336    };
337}
338
339macro_rules! __zngur_assert_has_field {
340    ($x:ty, $y:ty, $($field:tt)+ $(,)?) => {
341        const _: () = {
342            #[allow(dead_code)]
343            #[allow(mismatched_lifetime_syntaxes)]
344            fn check_field(value: $x) -> $y {
345                value.$($field)+
346            }
347        };
348    };
349}
350
351macro_rules! __zngur_assert_field_offset {
352    ($x:ty, $offset:expr, $($field:tt)+ $(,)?) => {
353        const _: () = ::core::assert!(
354            $offset == ::core::mem::offset_of!($x, $($field)+),
355            "{}",
356            __zngur_const_str_concat!(
357                "zngur declared offset of field ",
358                stringify!($($field)+),
359                " in ",
360                stringify!($x),
361                " is incorrect: expected offset of ",
362                __zngur_usize_to_str!($offset),
363                " , real offset is ",
364                __zngur_usize_to_str!(::core::mem::offset_of!($x, $($field)+)),
365            )
366        );
367    };
368}
369"#
370            .to_owned(),
371            panic_to_exception: false,
372            mangling_base: mangling_base.to_owned(),
373        }
374    }
375}
376
377impl Write for RustFile {
378    fn write_str(&mut self, s: &str) -> std::fmt::Result {
379        self.text.write_str(s)
380    }
381}
382
383macro_rules! w {
384    ($dst:expr, $($arg:tt)*) => {
385        { let _ = write!($dst, $($arg)*); }
386    };
387}
388
389macro_rules! wln {
390    ($dst:expr, $($arg:tt)*) => {
391        { let _ = writeln!($dst, $($arg)*); }
392    };
393}
394
395pub fn hash_of_sig(sig: &[RustType]) -> String {
396    let mut text = "".to_owned();
397    for elem in sig {
398        text += &format!("{elem}+");
399    }
400
401    let digset = Sha256::digest(&text);
402    hex::encode(&digset[..5])
403}
404
405fn mangle_name(name: &str, mangling_base: &str) -> String {
406    let mut name = "_zngur_"
407        .chars()
408        .chain(mangling_base.chars())
409        .chain(name.chars().filter(|c| !c.is_whitespace()))
410        .chain(Some('_'))
411        .collect::<String>();
412    let bads = [
413        (1, "::<", 'm'),
414        (1, ">::", 'n'),
415        (1, "->", 'a'),
416        (2, "&", 'r'),
417        (2, "=", 'e'),
418        (2, "<", 'x'),
419        (2, ">", 'y'),
420        (2, "[", 'j'),
421        (2, "]", 'k'),
422        (2, "::", 's'),
423        (2, ",", 'c'),
424        (2, "+", 'l'),
425        (2, "(", 'p'),
426        (2, ")", 'q'),
427        (2, "@", 'z'),
428        (2, "-", 'h'),
429    ];
430    while let Some((pos, which)) = bads.iter().filter_map(|x| Some((name.find(x.1)?, x))).min() {
431        name.replace_range(pos..pos + which.1.len(), "_");
432        w!(name, "{}{pos}", which.2);
433    }
434    name
435}
436
437impl RustFile {
438    fn mangle_name(&self, name: &str) -> String {
439        mangle_name(name, &self.mangling_base)
440    }
441
442    fn call_cpp_function(&mut self, name: &str, inputs: usize) {
443        for n in 0..inputs {
444            wln!(self, "let mut i{n} = ::core::mem::MaybeUninit::new(i{n});")
445        }
446        wln!(self, "let mut r = ::core::mem::MaybeUninit::uninit();");
447        w!(self, "{name}");
448        for n in 0..inputs {
449            w!(self, "i{n}.as_mut_ptr() as *mut u8, ");
450        }
451        wln!(self, "r.as_mut_ptr() as *mut u8);");
452        wln!(self, "r.assume_init()");
453    }
454
455    pub fn add_static_is_copy_assert(&mut self, ty: &RustType) {
456        wln!(self, r#"__zngur_assert_is_copy!({ty});"#);
457    }
458
459    pub fn add_static_size_assert(&mut self, ty: &RustType, size: usize) {
460        wln!(self, r#"__zngur_assert_size!({ty}, {size});"#);
461    }
462
463    pub fn add_static_align_assert(&mut self, ty: &RustType, align: usize) {
464        wln!(self, r#"__zngur_assert_align!({ty}, {align});"#);
465    }
466
467    pub fn add_static_size_upper_bound_assert(&mut self, ty: &RustType, size: usize) {
468        wln!(self, r#"__zngur_assert_size_conservative!({ty}, {size});"#);
469    }
470
471    pub fn add_static_align_upper_bound_assert(&mut self, ty: &RustType, align: usize) {
472        wln!(
473            self,
474            r#"__zngur_assert_align_conservative!({ty}, {align});"#
475        );
476    }
477
478    pub(crate) fn add_builder_for_dyn_trait(
479        &mut self,
480        tr: &ZngurTrait,
481        namespace: &str,
482        crate_name: &str,
483    ) -> CppTraitDefinition {
484        assert!(matches!(tr.tr, RustTrait::Normal { .. }));
485        let mut method_mangled_name = vec![];
486        wln!(self, r#"unsafe extern "C" {{"#);
487        for method in &tr.methods {
488            let name = self.mangle_name(&tr.tr.to_string())
489                + "_"
490                + &method.name
491                + "_"
492                + &hash_of_sig(&method.generics)
493                + "_"
494                + &hash_of_sig(&method.inputs);
495            wln!(
496                self,
497                r#"fn {name}(data: *mut u8, {} o: *mut u8);"#,
498                method
499                    .inputs
500                    .iter()
501                    .enumerate()
502                    .map(|(n, _)| format!("i{n}: *mut u8,"))
503                    .join(" ")
504            );
505            method_mangled_name.push(name);
506        }
507        wln!(self, "}}");
508        let link_name = self.add_builder_for_dyn_trait_owned(tr, &method_mangled_name);
509        let link_name_ref = self.add_builder_for_dyn_trait_borrowed(tr, &method_mangled_name);
510        CppTraitDefinition::Normal {
511            as_ty: tr.tr.into_cpp(namespace, crate_name),
512            methods: tr
513                .methods
514                .clone()
515                .into_iter()
516                .zip(method_mangled_name)
517                .map(|(x, rust_link_name)| CppTraitMethod {
518                    name: x.name,
519                    rust_link_name,
520                    inputs: x
521                        .inputs
522                        .into_iter()
523                        .map(|x| x.into_cpp(namespace, crate_name))
524                        .collect(),
525                    output: x.output.into_cpp(namespace, crate_name),
526                })
527                .collect(),
528            link_name,
529            link_name_ref,
530        }
531    }
532
533    fn add_builder_for_dyn_trait_owned(
534        &mut self,
535        tr: &ZngurTrait,
536        method_mangled_name: &[String],
537    ) -> String {
538        let trait_name = tr.tr.to_string();
539        let (trait_without_assocs, assocs) = tr.tr.clone().take_assocs();
540        let mangled_name = self.mangle_name(&trait_name);
541        wln!(
542            self,
543            r#"
544#[allow(non_snake_case)]
545#[unsafe(no_mangle)]
546pub extern "C" fn {mangled_name}(
547    data: *mut u8,
548    destructor: extern "C" fn(*mut u8),
549    o: *mut u8,
550) {{
551    struct Wrapper {{ 
552        data: *mut u8,
553        destructor: extern "C" fn(*mut u8),
554    }}
555    impl Drop for Wrapper {{
556        fn drop(&mut self) {{
557            (self.destructor)(self.data)
558        }}
559    }}
560    impl {trait_without_assocs} for Wrapper {{
561"#
562        );
563        for (name, ty) in assocs {
564            wln!(self, "        type {name} = {ty};");
565        }
566        for (method, rust_link_name) in tr.methods.iter().zip(method_mangled_name) {
567            w!(self, "        fn {}(", method.name);
568            match method.receiver {
569                crate::ZngurMethodReceiver::Static => {
570                    panic!("traits with static methods are not object safe");
571                }
572                crate::ZngurMethodReceiver::Ref(Mutability::Not) => w!(self, "&self"),
573                crate::ZngurMethodReceiver::Ref(Mutability::Mut) => w!(self, "&mut self"),
574                crate::ZngurMethodReceiver::Move => w!(self, "self"),
575            }
576            for (i, ty) in method.inputs.iter().enumerate() {
577                w!(self, ", i{i}: {ty}");
578            }
579            wln!(self, ") -> {} {{ unsafe {{", method.output);
580            wln!(self, "            let data = self.data;");
581            self.call_cpp_function(&format!("{rust_link_name}(data, "), method.inputs.len());
582            wln!(self, "        }} }}");
583        }
584        wln!(
585            self,
586            r#"
587    }}
588    unsafe {{ 
589        let this = Wrapper {{
590            data,
591            destructor,
592        }};
593        let r: Box<dyn {trait_name}> = Box::new(this);
594        std::ptr::write(o as *mut _, r)
595    }}
596}}"#
597        );
598        mangled_name
599    }
600
601    fn add_builder_for_dyn_trait_borrowed(
602        &mut self,
603        tr: &ZngurTrait,
604        method_mangled_name: &[String],
605    ) -> String {
606        let trait_name = tr.tr.to_string();
607        let (trait_without_assocs, assocs) = tr.tr.clone().take_assocs();
608        let mangled_name = self.mangle_name(&trait_name) + "_borrowed";
609        wln!(
610            self,
611            r#"
612#[allow(non_snake_case)]
613#[unsafe(no_mangle)]
614pub extern "C" fn {mangled_name}(
615    data: *mut u8,
616    o: *mut u8,
617) {{
618    struct Wrapper(());
619    impl {trait_without_assocs} for Wrapper {{
620"#
621        );
622        for (name, ty) in assocs {
623            wln!(self, "        type {name} = {ty};");
624        }
625        for (method, rust_link_name) in tr.methods.iter().zip(method_mangled_name) {
626            w!(self, "        fn {}(", method.name);
627            match method.receiver {
628                crate::ZngurMethodReceiver::Static => {
629                    panic!("traits with static methods are not object safe");
630                }
631                crate::ZngurMethodReceiver::Ref(Mutability::Not) => w!(self, "&self"),
632                crate::ZngurMethodReceiver::Ref(Mutability::Mut) => w!(self, "&mut self"),
633                crate::ZngurMethodReceiver::Move => w!(self, "self"),
634            }
635            for (i, ty) in method.inputs.iter().enumerate() {
636                w!(self, ", i{i}: {ty}");
637            }
638            wln!(self, ") -> {} {{ unsafe {{", method.output);
639            wln!(
640                self,
641                "            let data = ::std::mem::transmute::<_, *mut u8>(self);"
642            );
643            self.call_cpp_function(&format!("{rust_link_name}(data, "), method.inputs.len());
644            wln!(self, "        }} }}");
645        }
646        wln!(
647            self,
648            r#"
649    }}
650    unsafe {{ 
651        let this = data as *mut Wrapper;
652        let r: &dyn {trait_name} = &*this;
653        std::ptr::write(o as *mut _, r)
654    }}
655}}"#
656        );
657        mangled_name
658    }
659
660    pub fn add_builder_for_dyn_fn(
661        &mut self,
662        name: &str,
663        inputs: &[RustType],
664        output: &RustType,
665    ) -> String {
666        let mangled_name = self.mangle_name(&inputs.iter().chain(Some(output)).join(", "));
667        let trait_str = format!("{name}({}) -> {output}", inputs.iter().join(", "));
668        wln!(
669            self,
670            r#"
671#[allow(non_snake_case)]
672#[unsafe(no_mangle)]
673pub extern "C" fn {mangled_name}(
674    data: *mut u8,
675    destructor: extern "C" fn(*mut u8),
676    call: extern "C" fn(data: *mut u8, {} o: *mut u8),
677    o: *mut u8,
678) {{
679    struct ClosureData {{
680        data: *mut u8,
681        destructor: extern "C" fn(*mut u8),
682    }}
683    impl Drop for ClosureData {{
684        fn drop(&mut self) {{
685            (self.destructor)(self.data)
686        }}
687    }}
688    let this = ClosureData {{ data, destructor }};
689    let r: Box<dyn {trait_str}> = Box::new(move |{}| unsafe {{
690        _ = &this;
691        let data = this.data;
692"#,
693            inputs
694                .iter()
695                .enumerate()
696                .map(|(n, _)| format!("i{n}: *mut u8, "))
697                .join(" "),
698            inputs
699                .iter()
700                .enumerate()
701                .map(|(n, ty)| format!("i{n}: {ty}"))
702                .join(", "),
703        );
704        self.call_cpp_function("call(data, ", inputs.len());
705        wln!(
706            self,
707            r#"
708    }});
709    unsafe {{ std::ptr::write(o as *mut _, r) }}
710}}"#
711        );
712        mangled_name
713    }
714
715    pub fn add_tuple_constructor(&mut self, fields: &[RustType]) -> String {
716        let constructor = self.mangle_name(&fields.iter().join("&"));
717        w!(
718            self,
719            r#"
720#[allow(non_snake_case)]
721#[unsafe(no_mangle)]
722pub extern "C" fn {constructor}("#
723        );
724        for name in 0..fields.len() {
725            w!(self, "f_{name}: *mut u8, ");
726        }
727        w!(
728            self,
729            r#"o: *mut u8) {{ unsafe {{
730    ::std::ptr::write(o as *mut _, ("#
731        );
732        for (name, ty) in fields.iter().enumerate() {
733            w!(self, "::std::ptr::read(f_{name} as *mut {ty}), ");
734        }
735        wln!(self, ")) }} }}");
736        constructor
737    }
738
739    pub fn add_constructor<'a>(
740        &mut self,
741        rust_name: &str,
742        args: impl IntoIterator<Item = (&'a String, &'a RustType)> + Clone,
743    ) -> String {
744        let constructor = self.mangle_name(rust_name);
745        w!(
746            self,
747            r#"
748#[allow(non_snake_case)]
749#[unsafe(no_mangle)]
750pub extern "C" fn {constructor}("#
751        );
752        for (name, _) in args.clone() {
753            w!(self, "f_{name}: *mut u8, ");
754        }
755        w!(
756            self,
757            r#"o: *mut u8) {{ unsafe {{
758    ::std::ptr::write(o as *mut _, {rust_name} {{ "#
759        );
760        for (name, ty) in args {
761            w!(self, "{name}: ::std::ptr::read(f_{name} as *mut {ty}), ");
762        }
763        wln!(self, "}}) }} }}");
764        constructor
765    }
766
767    pub(crate) fn add_match_check(&mut self, rust_name: &str) -> String {
768        let match_check = self.mangle_name(&format!("{rust_name}_check"));
769        w!(
770            self,
771            r#"
772#[allow(non_snake_case)]
773#[unsafe(no_mangle)]
774pub extern "C" fn {match_check}(i: *mut u8, o: *mut u8) {{ unsafe {{
775    *o = matches!(&*(i as *mut &_), {rust_name} {{ .. }}) as u8;
776}} }}"#
777        );
778        match_check
779    }
780
781    pub(crate) fn add_discriminant(
782        &mut self,
783        rust_name: &str,
784        variants: &[ZngurVariant],
785    ) -> Option<String> {
786        if variants.is_empty() {
787            return None;
788        }
789
790        let name = self.mangle_name(&format!("{rust_name}::match"));
791        w!(
792            self,
793            r#"
794#[allow(non_snake_case)]
795#[unsafe(no_mangle)]
796pub extern "C" fn {name}(i: *mut u8) -> u32 {{ unsafe {{
797    match &*(i as *mut {rust_name} as *const _) {{"#
798        );
799        for (n, variant) in variants.iter().enumerate() {
800            let name = &variant.name;
801            w!(
802                self,
803                r#"
804        {rust_name}::{name} {{ .. }} => {n},
805        "#
806            );
807        }
808        w!(
809            self,
810            r#"
811    }}
812}} }}
813            "#
814        );
815        Some(name)
816    }
817
818    pub(crate) fn add_field_assertions(
819        &mut self,
820        field: &ZngurField,
821        owner: &RustType,
822    ) -> Option<String> {
823        let ZngurField { name, ty, offset } = field;
824        wln!(self, r#"__zngur_assert_has_field!({owner}, {ty}, {name});"#);
825        if let Some(offset) = offset {
826            wln!(
827                self,
828                r#"__zngur_assert_field_offset!({owner}, {offset}, {name});"#
829            );
830            None
831        } else {
832            let mn = self.mangle_name(&format!("{}_field_{}_offset", &owner, &name));
833            wln!(
834                self,
835                r#"
836#[allow(non_snake_case)]
837#[unsafe(no_mangle)]
838pub static {mn}: usize = ::std::mem::offset_of!({owner}, {name});
839                "#
840            );
841            Some(mn)
842        }
843    }
844
845    pub(crate) fn add_variant_field_calculations(
846        &mut self,
847        field: &ZngurField,
848        owner: &RustType,
849        variant: &str,
850    ) -> String {
851        let ZngurField { name, .. } = field;
852        let mn = self.mangle_name(&format!("{owner}_{variant}_field_{name}_offset"));
853        // SAFETY: this function is only called from the variant class methods.
854        // The only way to obtain variant classes is to match, so it is impossible
855        // to obtain an instance of variant class set to a wrong variant,
856        // so this match will always pass.
857        wln!(
858            self,
859            r#"
860#[allow(non_snake_case)]
861#[unsafe(no_mangle)]
862pub extern "C" fn {mn}(i: *const u8) -> usize {{ unsafe {{
863    let base = &*(i as *const {owner});
864    match base {{
865        {owner}::{variant} {{ {name}: f, .. }} => {{
866            (f as *const _ as usize) - (base as *const _ as usize)
867        }}
868        _ => std::hint::unreachable_unchecked(),
869    }}
870}} }}
871            "#
872        );
873        mn
874    }
875
876    pub fn add_extern_cpp_impl(
877        &mut self,
878        owner: &RustType,
879        tr: Option<&RustTrait>,
880        methods: &[ZngurMethod],
881    ) -> Vec<String> {
882        let mut mangled_names = vec![];
883        w!(self, r#"unsafe extern "C" {{"#);
884        for method in methods {
885            let mn = self.mangle_name(&format!("{}_extern_method_{}", owner, method.name));
886            w!(
887                self,
888                r#"
889    fn {mn}("#
890            );
891            let input_offset = if method.receiver == ZngurMethodReceiver::Static {
892                0
893            } else {
894                1
895            };
896            for n in 0..method.inputs.len() + input_offset {
897                w!(self, "i{n}: *mut u8, ");
898            }
899            wln!(self, r#"o: *mut u8);"#);
900            mangled_names.push(mn);
901        }
902        w!(self, r#"}}"#);
903        match tr {
904            Some(tr) => {
905                let (tr, assocs) = tr.clone().take_assocs();
906                w!(self, r#"impl {tr} for {owner} {{"#);
907                for (name, ty) in assocs {
908                    w!(self, r#"type {name} = {ty};"#);
909                }
910            }
911            None => w!(self, r#"impl {owner} {{"#),
912        }
913        for (mn, method) in mangled_names.iter().zip(methods) {
914            if tr.is_none() {
915                w!(self, "pub ");
916            }
917            w!(
918                self,
919                r#"{}fn {}("#,
920                if method.is_safe { "" } else { "unsafe " },
921                method.name
922            );
923            match method.receiver {
924                ZngurMethodReceiver::Static => (),
925                ZngurMethodReceiver::Ref(Mutability::Mut) => w!(self, "&mut self, "),
926                ZngurMethodReceiver::Ref(Mutability::Not) => w!(self, "&self, "),
927                ZngurMethodReceiver::Move => w!(self, "self, "),
928            }
929            let input_offset = if method.receiver == ZngurMethodReceiver::Static {
930                0
931            } else {
932                1
933            };
934            for (ty, n) in method.inputs.iter().zip(input_offset..) {
935                w!(self, "i{n}: {ty}, ");
936            }
937            wln!(self, ") -> {} {{ unsafe {{", method.output);
938            if method.receiver != ZngurMethodReceiver::Static {
939                wln!(self, "let i0 = self;");
940            }
941            self.call_cpp_function(&format!("{mn}("), method.inputs.len() + input_offset);
942            wln!(self, "}} }}");
943        }
944        w!(self, r#"}}"#);
945        mangled_names
946    }
947
948    pub fn add_extern_cpp_function(
949        &mut self,
950        rust_name: &str,
951        inputs: &[RustType],
952        output: &RustType,
953        is_safe: bool,
954    ) -> String {
955        let mangled_name = self.mangle_name(rust_name);
956        w!(
957            self,
958            r#"
959unsafe extern "C" {{ fn {mangled_name}("#
960        );
961        for (n, _) in inputs.iter().enumerate() {
962            w!(self, "i{n}: *mut u8, ");
963        }
964        wln!(self, r#"o: *mut u8); }}"#);
965        w!(
966            self,
967            r#"
968#[allow(non_snake_case)]
969pub {}fn {rust_name}("#,
970            if is_safe { "" } else { "unsafe " }
971        );
972        for (n, ty) in inputs.iter().enumerate() {
973            w!(self, "i{n}: {ty}, ");
974        }
975        wln!(self, ") -> {output} {{ unsafe {{");
976        self.call_cpp_function(&format!("{mangled_name}("), inputs.len());
977        wln!(self, "}} }}");
978        mangled_name
979    }
980
981    pub fn add_cpp_value_bridge(&mut self, ty: &RustType) -> String {
982        let type_name = ty.to_string().split("::").last().unwrap().to_string();
983        let mangled_name = self.mangle_name(&format!("{ty}_cpp_value"));
984        w!(
985            self,
986            r#"
987#[allow(non_snake_case)]
988#[unsafe(no_mangle)]
989pub extern "C" fn {mangled_name}(d: *mut u8) -> *mut cpp::{type_name} {{
990    d as *mut cpp::{type_name}
991}}"#
992        );
993        mangled_name
994    }
995
996    pub fn add_function(
997        &mut self,
998        cxx_name: &str,
999        rust_name: &str,
1000        inputs: &[RustType],
1001        output: &RustType,
1002        use_path: Option<Vec<String>>,
1003        deref: Option<Mutability>,
1004        namespace: &str,
1005        crate_name: &str,
1006    ) -> CppFnSig {
1007        let mut mangled_name =
1008            self.mangle_name(&format!("{cxx_name}={rust_name}")) + "_" + &hash_of_sig(&inputs);
1009        if deref.is_some() {
1010            mangled_name += "_deref";
1011        }
1012        w!(
1013            self,
1014            r#"
1015#[allow(non_snake_case)]
1016#[unsafe(no_mangle)]
1017#[allow(unused_parens)]
1018pub extern "C" fn {mangled_name}("#
1019        );
1020        for n in 0..inputs.len() {
1021            w!(self, "i{n}: *mut u8, ");
1022        }
1023        let (modified_output, is_impl_trait) = if let RustType::Impl(tr, bounds) = output {
1024            (
1025                RustType::Boxed(Box::new(RustType::Dyn(tr.clone(), bounds.clone()))),
1026                true,
1027            )
1028        } else {
1029            (output.clone(), false)
1030        };
1031        wln!(self, "o: *mut u8) {{ unsafe {{");
1032        self.wrap_in_catch_unwind(|this| {
1033            if let Some(use_path) = use_path {
1034                if use_path.first().is_some_and(|x| x == "crate") {
1035                    wln!(this, "    use {};", use_path.iter().join("::"));
1036                } else {
1037                    wln!(this, "    use ::{};", use_path.iter().join("::"));
1038                }
1039            }
1040
1041            w!(
1042                this,
1043                "    ::std::ptr::write(o as *mut {modified_output}, {impl_trait} {rust_name}(",
1044                impl_trait = if is_impl_trait { "Box::new( " } else { "" },
1045            );
1046            match deref {
1047                Some(Mutability::Mut) => w!(this, "::std::ops::DerefMut::deref_mut"),
1048                Some(Mutability::Not) => w!(this, "::std::ops::Deref::deref"),
1049                None => {}
1050            }
1051            for (n, ty) in inputs.iter().enumerate() {
1052                w!(this, "(::std::ptr::read(i{n} as *mut {ty})), ");
1053            }
1054            if is_impl_trait {
1055                wln!(this, ")));");
1056            } else {
1057                wln!(this, "));");
1058            }
1059        });
1060        wln!(self, " }} }}");
1061        CppFnSig {
1062            rust_link_name: mangled_name,
1063            inputs: inputs
1064                .iter()
1065                .map(|ty| ty.into_cpp(namespace, crate_name))
1066                .collect(),
1067            output: modified_output.into_cpp(namespace, crate_name),
1068        }
1069    }
1070
1071    pub(crate) fn add_wellknown_trait(
1072        &mut self,
1073        ty: &RustType,
1074        wellknown_trait: ZngurWellknownTrait,
1075        is_unsized: bool,
1076    ) -> ZngurWellknownTraitData {
1077        match wellknown_trait {
1078            ZngurWellknownTrait::Unsized => ZngurWellknownTraitData::Unsized,
1079            ZngurWellknownTrait::Copy => ZngurWellknownTraitData::Copy,
1080            ZngurWellknownTrait::Drop => {
1081                let drop_in_place = self.mangle_name(&format!("{ty}=drop_in_place"));
1082                wln!(
1083                    self,
1084                    r#"
1085#[allow(non_snake_case)]
1086#[unsafe(no_mangle)]
1087pub extern "C" fn {drop_in_place}(v: *mut u8) {{ unsafe {{
1088    ::std::ptr::drop_in_place(v as *mut {ty});
1089}} }}"#
1090                );
1091                ZngurWellknownTraitData::Drop { drop_in_place }
1092            }
1093            ZngurWellknownTrait::Debug => {
1094                let pretty_print = self.mangle_name(&format!("{ty}=debug_pretty"));
1095                let debug_print = self.mangle_name(&format!("{ty}=debug_print"));
1096                let dbg_ty = if !is_unsized {
1097                    format!("{ty}")
1098                } else {
1099                    format!("&{ty}")
1100                };
1101                wln!(
1102                    self,
1103                    r#"
1104#[allow(non_snake_case)]
1105#[unsafe(no_mangle)]
1106pub extern "C" fn {pretty_print}(v: *mut u8) {{
1107    eprintln!("{{:#?}}", unsafe {{ &*(v as *mut {dbg_ty}) }});
1108}}"#
1109                );
1110                wln!(
1111                    self,
1112                    r#"
1113#[allow(non_snake_case)]
1114#[unsafe(no_mangle)]
1115pub extern "C" fn {debug_print}(v: *mut u8) {{
1116    eprintln!("{{:?}}", unsafe {{ &*(v as *mut {dbg_ty}) }});
1117}}"#
1118                );
1119                ZngurWellknownTraitData::Debug {
1120                    pretty_print,
1121                    debug_print,
1122                }
1123            }
1124        }
1125    }
1126
1127    fn wrap_in_catch_unwind(&mut self, f: impl FnOnce(&mut RustFile)) {
1128        if !self.panic_to_exception {
1129            f(self);
1130        } else {
1131            wln!(
1132                self,
1133                r#"unsafe extern "C" {{
1134                fn __zngur_mark_panicked();   
1135            }}
1136            let e = ::std::panic::catch_unwind(|| {{"#
1137            );
1138            f(self);
1139            wln!(self, "}});");
1140            wln!(self, "if let Err(_) = e {{ __zngur_mark_panicked(); }}");
1141        }
1142    }
1143
1144    pub(crate) fn add_layout_policy_shim(
1145        &mut self,
1146        ty: &RustType,
1147        layout: LayoutPolicy,
1148    ) -> CppLayoutPolicy {
1149        match layout {
1150            LayoutPolicy::StackAllocated { size, align } => {
1151                CppLayoutPolicy::StackAllocated { size, align }
1152            }
1153            LayoutPolicy::Conservative { size, align } => {
1154                CppLayoutPolicy::StackAllocated { size, align }
1155            }
1156            LayoutPolicy::HeapAllocated => {
1157                let size_fn = self.mangle_name(&format!("{ty}_size_fn"));
1158                let alloc_fn = self.mangle_name(&format!("{ty}_alloc_fn"));
1159                let free_fn = self.mangle_name(&format!("{ty}_free_fn"));
1160                wln!(
1161                    self,
1162                    r#"
1163                #[allow(non_snake_case)]
1164                #[unsafe(no_mangle)]
1165                pub fn {size_fn}() -> usize {{
1166                    ::std::mem::size_of::<{ty}>()
1167                }}
1168        
1169                #[allow(non_snake_case)]
1170                #[unsafe(no_mangle)]
1171                pub fn {alloc_fn}() -> *mut u8 {{
1172                    unsafe {{ ::std::alloc::alloc(::std::alloc::Layout::new::<{ty}>()) }}
1173                }}
1174
1175                #[allow(non_snake_case)]
1176                #[unsafe(no_mangle)]
1177                pub fn {free_fn}(p: *mut u8) {{
1178                    unsafe {{ ::std::alloc::dealloc(p, ::std::alloc::Layout::new::<{ty}>()) }}
1179                }}
1180                "#
1181                );
1182                CppLayoutPolicy::HeapAllocated {
1183                    size_fn,
1184                    alloc_fn,
1185                    free_fn,
1186                }
1187            }
1188            LayoutPolicy::OnlyByRef => CppLayoutPolicy::OnlyByRef,
1189        }
1190    }
1191}