hyperlight_component_util/
hl.rs

1/*
2Copyright 2025 The Hyperlight Authors.
3
4Licensed under the Apache License, Version 2.0 (the "License");
5you may not use this file except in compliance with the License.
6You may obtain a copy of the License at
7
8    http://www.apache.org/licenses/LICENSE-2.0
9
10Unless required by applicable law or agreed to in writing, software
11distributed under the License is distributed on an "AS IS" BASIS,
12WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13See the License for the specific language governing permissions and
14limitations under the License.
15 */
16
17use itertools::Itertools;
18use proc_macro2::{Ident, TokenStream};
19use quote::{format_ident, quote};
20
21use crate::emit::{ResolvedBoundVar, State, kebab_to_cons, kebab_to_var};
22use crate::etypes::{self, Defined, Handleable, Tyvar, Value};
23use crate::rtypes;
24
25/// Construct a string that can be used "on the wire" to identify a
26/// given function between the guest/host.  This should be replaced
27/// with an integer index so that we can dispatch less dynamically in
28/// the future.
29pub fn emit_fn_hl_name(s: &State, kebab: &str) -> String {
30    s.mod_cursor
31        .iter()
32        .map(|x| x.to_string())
33        .chain(std::iter::once(kebab.to_string()))
34        .join("::")
35}
36
37/// Emit code to unmarshal a value into a toplevel type (i.e. types
38/// that cannot be represented inline in a valtype).
39/// - `id`: an ident of a slice that the code will unmarshal from; also
40///   used as the beginning of any other identifiers that this code
41///   declares (if only we had hygiene in stable rust...)
42/// - `tv`: the tyvar that we followed to get to this type
43/// - `vt`: the value type that we are unmarshalling
44///
45/// The token stream produced will be an expression which typechecks
46/// as a tuple whose first component is the Rust type (as defined by
47/// the [`crate::rtypes`] module) of the given value type and whose
48/// second component is an integer. The second component represents
49/// the number of bytes consumed from the `id` slice while
50/// unmarshalling.
51pub fn emit_hl_unmarshal_toplevel_value(
52    s: &mut State,
53    id: Ident,
54    tv: Tyvar,
55    vt: &Value,
56) -> TokenStream {
57    let tname = rtypes::emit_var_ref_value(s, &tv);
58    let mut s = s.clone();
59    let Tyvar::Bound(n) = tv else {
60        panic!("impossible tyvar")
61    };
62    s.var_offset += n as usize + 1;
63    let s = &mut s;
64    match vt {
65        Value::Record(rfs) => {
66            let cursor = format_ident!("{}_cursor", id);
67            let inid = format_ident!("{}_field", id);
68            let (decls, uses) = rfs
69                .iter()
70                .map(|rf| {
71                    let field_name = kebab_to_var(rf.name.name);
72                    let field_name_var = format_ident!("{}_field_{}", id, field_name);
73                    let vtun = emit_hl_unmarshal_value(s, inid.clone(), &rf.ty);
74                    (
75                        quote! {
76                            let #inid = &#id[#cursor..];
77                            let (#field_name_var, b) = { #vtun };
78                            #cursor += b;
79                        },
80                        quote! {
81                            #field_name: #field_name_var,
82                        },
83                    )
84                })
85                .unzip::<_, _, Vec<_>, Vec<_>>();
86            quote! {
87                let mut #cursor = 0;
88                #(#decls)*
89                (#tname { #(#uses)* }, #cursor)
90            }
91        }
92        Value::Flags(ns) => {
93            let bytes = usize::div_ceil(ns.len(), 8);
94            let fields = ns.iter().enumerate().map(|(i, n)| {
95                let byte_offset = i / 8;
96                let bit_offset = i % 8;
97                let fieldid = kebab_to_var(n.name);
98                quote! {
99                    #fieldid: (#id[#byte_offset] >> #bit_offset) & 0x1 == 1,
100                }
101            });
102            quote! {
103                (#tname { #(#fields)* }, #bytes)
104            }
105        }
106        Value::Variant(vcs) => {
107            let inid = format_ident!("{}_body", id);
108            let vcs = vcs.iter().enumerate().map(|(i, vc)| {
109                let case_name = kebab_to_cons(vc.name.name);
110                let i = i as u32;
111                let case_name_var = format_ident!("{}_case_{}", id, case_name);
112                match &vc.ty {
113                    Some(ty) => {
114                        let vtun = emit_hl_unmarshal_value(s, inid.clone(), ty);
115                        quote! {
116                            #i => {
117                                let (#case_name_var, b) = { #vtun };
118                                (#tname::#case_name(#case_name_var), b + 4)
119                            }
120                        }
121                    }
122                    None => quote! {
123                        #i => (#tname::#case_name, 4)
124                    },
125                }
126            });
127            quote! {
128                let n = u32::from_ne_bytes(#id[0..4].try_into().unwrap());
129                let #inid = &#id[4..];
130                match n {
131                    #(#vcs,)*
132                    _ => panic!("invalid value for variant"),
133                }
134            }
135        }
136        Value::Enum(ns) => {
137            let vcs = ns.iter().enumerate().map(|(i, n)| {
138                let case_name = kebab_to_cons(n.name);
139                let i = i as u32;
140                quote! { #i => ( #tname::#case_name, 4) }
141            });
142            quote! {
143                let n = u32::from_ne_bytes(#id[0..4].try_into().unwrap());
144                match n {
145                    #(#vcs,)*
146                    _ => panic!("invalid value for enum"),
147                }
148            }
149        }
150        _ => emit_hl_unmarshal_value(s, id, vt),
151    }
152}
153
154/// Find the resource index that the given Handleable refers to.
155///
156/// Precondition: this type variable does refer to a resource type
157pub fn resolve_handleable_to_resource(s: &mut State, ht: &Handleable) -> u32 {
158    match ht {
159        Handleable::Var(Tyvar::Bound(vi)) => {
160            let ResolvedBoundVar::Resource { rtidx } = s.resolve_bound_var(*vi) else {
161                panic!("impossible: resource var is not resource");
162            };
163            rtidx
164        }
165        _ => panic!("impossible handleable in type"),
166    }
167}
168
169/// Emit code to unmarshal a value into an inline-able value type
170/// - `id`: an ident of a slice that the code will unmarshal from; also
171///   used as the beginning of any other identifiers that this code
172///   declares (if only we had hygiene in stable rust...)
173/// - `vt`: the value type that we are unmarshalling
174///
175/// The token stream produced will be an expression which typechecks
176/// as a tuple whose first component is the Rust type (as defined by
177/// the [`crate::rtypes`] module) of the given value type and whose
178/// second component is an integer. The second component represents
179/// the number of bytes consumed from the `id` slice while
180/// unmarshalling.
181pub fn emit_hl_unmarshal_value(s: &mut State, id: Ident, vt: &Value) -> TokenStream {
182    match vt {
183        Value::Bool => quote! { (#id[0] != 0, 1) },
184        Value::S(_) | Value::U(_) | Value::F(_) => {
185            let (tid, width) = rtypes::numeric_rtype(vt);
186            let blen = width as usize / 8;
187            quote! {
188                (#tid::from_ne_bytes(#id[0..#blen].try_into().unwrap()), #blen)
189            }
190        }
191        Value::Char => quote! {
192            (unsafe { char::from_u32_unchecked(u32::from_ne_bytes(
193                #id[0..4].try_into().unwrap())) }, 4)
194        },
195        Value::String => quote! {
196            let n = u32::from_ne_bytes(#id[0..4].try_into().unwrap()) as usize;
197            let s = ::alloc::string::ToString::to_string(::core::str::from_utf8(&#id[4..4 + n]).unwrap()); // todo: better error handling
198            (s, n + 4)
199        },
200        Value::List(vt) => {
201            let retid = format_ident!("{}_list", id);
202            let inid = format_ident!("{}_elem", id);
203            let vtun = emit_hl_unmarshal_value(s, inid.clone(), vt);
204            quote! {
205                let n = u32::from_ne_bytes(#id[0..4].try_into().unwrap()) as usize;
206                let mut #retid = alloc::vec::Vec::new();
207                let mut cursor = 4;
208                for i in 0..n {
209                    let #inid = &#id[cursor..];
210                    let (x, b) = { #vtun };
211                    cursor += b;
212                    #retid.push(x);
213                }
214                (#retid, cursor)
215            }
216        }
217        Value::FixList(vt, _) => {
218            let inid = format_ident!("{}_elem", id);
219            let vtun = emit_hl_unmarshal_value(s, inid.clone(), vt);
220            quote! {
221                let mut cursor = 0;
222                let arr = ::core::array::from_fn(|_i| {
223                    let #inid = &#id[cursor..];
224                    let (x, b) = { #vtun };
225                    cursor += b;
226                    x
227                });
228                (arr, cursor)
229            }
230        }
231        Value::Record(_) => panic!("record not at top level of valtype"),
232        Value::Tuple(vts) => {
233            let inid = format_ident!("{}_elem", id);
234            let len = format_ident!("{}_len", id);
235            let (ns, vtuns) = vts
236                .iter()
237                .enumerate()
238                .map(|(i, vt)| {
239                    let vtun = emit_hl_unmarshal_value(s, inid.clone(), vt);
240                    let retid = format_ident!("{}_elem{}", id, i);
241                    (
242                        retid.clone(),
243                        quote! {
244                            let (#retid, b) = { #vtun };
245                            #len += b;
246                            let #inid = &#inid[b..];
247                        },
248                    )
249                })
250                .unzip::<_, _, Vec<_>, Vec<_>>();
251            quote! {
252                let #inid = &#id[0..];
253                let mut #len = 0;
254                #(#vtuns)*
255                ((#(#ns),*), #len)
256            }
257        }
258        Value::Flags(_) => panic!("flags not at top level of valtype"),
259        Value::Variant(_) => panic!("variant not at top level of valtype"),
260        Value::Enum(_) => panic!("enum not at top level of valtype"),
261        Value::Option(vt) => {
262            let inid = format_ident!("{}_body", id);
263            let vtun = emit_hl_unmarshal_value(s, inid.clone(), vt);
264            quote! {
265                let n = u8::from_ne_bytes(#id[0..1].try_into().unwrap());
266                if n != 0 {
267                    let #inid = &#id[1..];
268                    let (x, b) = { #vtun };
269                    (::core::option::Option::Some(x), b + 1)
270                } else {
271                    (::core::option::Option::None, 1)
272                }
273            }
274        }
275        Value::Result(vt1, vt2) => {
276            let inid = format_ident!("{}_body", id);
277            let vtun1 = if let Some(ref vt1) = **vt1 {
278                emit_hl_unmarshal_value(s, inid.clone(), vt1)
279            } else {
280                quote! { ((), 0) }
281            };
282            let vtun2 = if let Some(ref vt2) = **vt2 {
283                emit_hl_unmarshal_value(s, inid.clone(), vt2)
284            } else {
285                quote! { ((), 0) }
286            };
287            quote! {
288                let i = u8::from_ne_bytes(#id[0..1].try_into().unwrap());
289                let #inid = &#id[1..];
290                if i == 0 {
291                    let (x, b) = { #vtun1 };
292                    (::core::result::Result::Ok(x), b + 1)
293                } else {
294                    let (x, b)= { #vtun2 };
295                    (::core::result::Result::Err(x), b +1)
296                }
297            }
298        }
299        Value::Own(ht) => {
300            let vi = resolve_handleable_to_resource(s, ht);
301            log::debug!("resolved ht to r (1) {:?} {:?}", ht, vi);
302            if s.is_guest {
303                let rid = format_ident!("HostResource{}", vi);
304                if s.is_wasmtime_guest {
305                    quote! {
306                        let i = u32::from_ne_bytes(#id[0..4].try_into().unwrap());
307                        (::wasmtime::component::Resource::<#rid>::new_own(i), 4)
308                    }
309                } else {
310                    quote! {
311                        let i = u32::from_ne_bytes(#id[0..4].try_into().unwrap());
312                        (#rid { rep: i }, 4)
313                    }
314                }
315            } else {
316                let rid = format_ident!("resource{}", vi);
317                quote! {
318                    let i = u32::from_ne_bytes(#id[0..4].try_into().unwrap());
319                    let Some(v) = rts.#rid[i as usize].take() else {
320                        // todo: better error handling
321                        panic!("");
322                    };
323                    (v, 4)
324                }
325            }
326        }
327        Value::Borrow(ht) => {
328            let vi = resolve_handleable_to_resource(s, ht);
329            log::debug!("resolved ht to r (2) {:?} {:?}", ht, vi);
330            if s.is_guest {
331                let rid = format_ident!("HostResource{}", vi);
332                if s.is_wasmtime_guest {
333                    quote! {
334                        let i = u32::from_ne_bytes(#id[0..4].try_into().unwrap());
335                        (::wasmtime::component::Resource::<#rid>::new_borrow(i), 4)
336                    }
337                } else {
338                    // TODO: When we add the Drop impl (#810), we need
339                    // to make sure it does not get called here
340                    //
341                    // If we tried to actually return a reference
342                    // here, rustc would get mad about the temporary
343                    // constructed here not living long enough, so
344                    // instead we return the temporary and construct
345                    // the reference elsewhere. It might be a bit more
346                    // principled to have a separate
347                    // HostResourceXXBorrow struct that implements
348                    // AsRef<HostResourceXX> or something in the
349                    // future...
350                    quote! {
351                        let i = u32::from_ne_bytes(#id[0..4].try_into().unwrap());
352
353                        (#rid { rep: i }, 4)
354                    }
355                }
356            } else {
357                let rid = format_ident!("resource{}", vi);
358                quote! {
359                    let i = u32::from_ne_bytes(#id[0..4].try_into().unwrap());
360                    let Some(v) = rts.#rid[i as usize].borrow() else {
361                        // todo: better error handling
362                        panic!("");
363                    };
364                    (v, 4)
365                }
366            }
367        }
368        Value::Var(tv, _) => {
369            let Some(Tyvar::Bound(n)) = tv else {
370                panic!("impossible tyvar")
371            };
372            let ResolvedBoundVar::Definite {
373                final_bound_var: n,
374                ty: Defined::Value(vt),
375            } = s.resolve_bound_var(*n)
376            else {
377                panic!("unresolvable tyvar (2)");
378            };
379            let vt = vt.clone();
380            emit_hl_unmarshal_toplevel_value(s, id, Tyvar::Bound(n), &vt)
381        }
382    }
383}
384
385/// Emit code to marshal a value from a toplevel type (i.e. types that
386/// cannot be represented inline in a valtype).
387/// - `id`: an ident of a Rust value of the Rust type (as defined by
388///   the [`crate::rtypes`] module) of the given value type that is
389///   being marshaled from
390/// - `tv`: the tyvar that we followed to get to this type
391/// - `vt`: the value type that we are marshaling
392///
393/// The token stream produced will be an expression which typechecks
394/// as `Vec<u8`>`.
395pub fn emit_hl_marshal_toplevel_value(
396    s: &mut State,
397    id: Ident,
398    tv: Tyvar,
399    vt: &Value,
400) -> TokenStream {
401    let tname = rtypes::emit_var_ref_value(s, &tv);
402    let mut s = s.clone();
403    let Tyvar::Bound(n) = tv else {
404        panic!("impossible tyvar")
405    };
406    s.var_offset += n as usize + 1;
407    let s = &mut s;
408    match vt {
409        Value::Record(rfs) => {
410            let retid = format_ident!("{}_record", id);
411            let fields = rfs
412                .iter()
413                .map(|rf| {
414                    let field_name = kebab_to_var(rf.name.name);
415                    let fieldid = format_ident!("{}_field_{}", id, field_name);
416                    let vtun = emit_hl_marshal_value(s, fieldid.clone(), &rf.ty);
417                    quote! {
418                        let #fieldid = #id.#field_name;
419                        #retid.extend({ #vtun });
420                    }
421                })
422                .collect::<Vec<_>>();
423            quote! {
424                let mut #retid = alloc::vec::Vec::new();
425                #(#fields)*
426                #retid
427            }
428        }
429        Value::Flags(ns) => {
430            let bytes = usize::div_ceil(ns.len(), 8);
431            let fields = ns
432                .iter()
433                .enumerate()
434                .map(|(i, n)| {
435                    let byte_offset = i / 8;
436                    let bit_offset = i % 8;
437                    let fieldid = kebab_to_var(n.name);
438                    quote! {
439                        bytes[#byte_offset] |= (if #id.#fieldid { 1 } else { 0 }) << #bit_offset;
440                    }
441                })
442                .collect::<Vec<_>>();
443            quote! {
444                let mut bytes = [0; #bytes];
445                #(#fields)*
446                alloc::vec::Vec::from(bytes)
447            }
448        }
449        Value::Variant(vcs) => {
450            let retid = format_ident!("{}_ret", id);
451            let bodyid = format_ident!("{}_body", id);
452            let vcs = vcs
453                .iter()
454                .enumerate()
455                .map(|(i, vc)| {
456                    let i = i as u32;
457                    let case_name = kebab_to_cons(vc.name.name);
458                    match &vc.ty {
459                        Some(ty) => {
460                            let vtun = emit_hl_marshal_value(s, bodyid.clone(), ty);
461                            quote! {
462                               #tname::#case_name(#bodyid) => {
463                                    #retid.extend(u32::to_ne_bytes(#i));
464                                    #retid.extend({ #vtun })
465                                }
466                            }
467                        }
468                        None => {
469                            quote! {
470                                #tname::#case_name => {
471                                    #retid.extend(u32::to_ne_bytes(#i));
472                                }
473                            }
474                        }
475                    }
476                })
477                .collect::<Vec<_>>();
478            quote! {
479                let mut #retid = alloc::vec::Vec::new();
480                match #id {
481                    #(#vcs)*
482                }
483                #retid
484            }
485        }
486        Value::Enum(ns) => {
487            let vcs = ns.iter().enumerate().map(|(i, n)| {
488                let case_name = kebab_to_cons(n.name);
489                let i = i as u32;
490                quote! { #tname::#case_name => #i }
491            });
492            quote! {
493                alloc::vec::Vec::from(u32::to_ne_bytes(match #id {
494                    #(#vcs,)*
495                }))
496            }
497        }
498        _ => emit_hl_marshal_value(s, id, vt),
499    }
500}
501
502/// Emit code to marshal a value from an inline-able value type
503/// - `id`: an ident of a Rust value of the Rust type (as defined by
504///   the [`crate::rtypes`] module) of the given value type that is
505///   being marshaled from
506/// - `vt`: the value type that we are marshaling
507///
508/// The token stream produced will be an expression which typechecks
509/// as `Vec<u8>`.
510pub fn emit_hl_marshal_value(s: &mut State, id: Ident, vt: &Value) -> TokenStream {
511    match vt {
512        Value::Bool => quote! {
513            alloc::vec![if #id { 1u8 } else { 0u8 }]
514        },
515        Value::S(_) | Value::U(_) | Value::F(_) => {
516            let (tid, _) = rtypes::numeric_rtype(vt);
517            quote! { alloc::vec::Vec::from(#tid::to_ne_bytes(#id)) }
518        }
519        Value::Char => quote! {
520            alloc::vec::Vec::from((#id as u32).to_ne_bytes())
521        },
522        Value::String => {
523            let retid = format_ident!("{}_string", id);
524            let bytesid = format_ident!("{}_bytes", id);
525            quote! {
526                let mut #retid = alloc::vec::Vec::new();
527                let #bytesid = #id.into_bytes();
528                #retid.extend(alloc::vec::Vec::from(u32::to_ne_bytes(#bytesid.len() as u32)));
529                #retid.extend(#bytesid);
530                #retid
531            }
532        }
533        Value::List(vt) => {
534            let retid = format_ident!("{}_list", id);
535            let inid = format_ident!("{}_elem", id);
536            let vtun = emit_hl_marshal_value(s, inid.clone(), vt);
537            quote! {
538                let mut #retid = alloc::vec::Vec::new();
539                let n = #id.len();
540                #retid.extend(alloc::vec::Vec::from(u32::to_ne_bytes(n as u32)));
541                for #inid in #id {
542                    #retid.extend({ #vtun })
543                }
544                #retid
545            }
546        }
547        Value::FixList(vt, _size) => {
548            let retid = format_ident!("{}_fixlist", id);
549            let inid = format_ident!("{}_elem", id);
550            let vtun = emit_hl_marshal_value(s, inid.clone(), vt);
551            quote! {
552                let mut #retid = alloc::vec::Vec::new();
553                for #inid in #id {
554                    #retid.extend({ #vtun })
555                }
556                #retid
557            }
558        }
559        Value::Record(_) => panic!("record not at top level of valtype"),
560        Value::Tuple(vts) => {
561            let retid = format_ident!("{}_tuple", id);
562            let inid = format_ident!("{}_elem", id);
563            let vtuns = vts.iter().enumerate().map(|(i, vt)| {
564                let i = syn::Index::from(i);
565                let vtun = emit_hl_marshal_value(s, inid.clone(), vt);
566                quote! {
567                    let #inid = #id.#i;
568                    #retid.extend({ #vtun });
569                }
570            });
571            quote! {
572                let mut #retid = alloc::vec::Vec::new();
573                #(#vtuns)*
574                #retid
575            }
576        }
577        Value::Flags(_) => panic!("flags not at top level of valtype"),
578        Value::Variant(_) => panic!("flags not at top level of valtype"),
579        Value::Enum(_) => panic!("flags not at top level of valtype"),
580        Value::Option(vt) => {
581            let bodyid = format_ident!("{}_body", id);
582            let retid = format_ident!("{}_ret", id);
583            let vtun = emit_hl_marshal_value(s, bodyid.clone(), vt);
584            quote! {
585                match #id {
586                    ::core::option::Option::Some(#bodyid) => {
587                        let mut #retid = alloc::vec::Vec::from(u8::to_ne_bytes(1));
588                        #retid.extend({ #vtun });
589                        #retid
590                    },
591                    ::core::option::Option::None => alloc::vec::Vec::from(u8::to_ne_bytes(0))
592                }
593            }
594        }
595        Value::Result(vt1, vt2) => {
596            let bodyid = format_ident!("{}_body", id);
597            let retid = format_ident!("{}_ret", id);
598            let vtun1 = if let Some(ref vt1) = **vt1 {
599                let vtun = emit_hl_marshal_value(s, bodyid.clone(), vt1);
600                quote! { #retid.extend({ #vtun }); }
601            } else {
602                quote! {}
603            };
604            let vtun2 = if let Some(ref vt2) = **vt2 {
605                let vtun = emit_hl_marshal_value(s, bodyid.clone(), vt2);
606                quote! { #retid.extend({ #vtun }); }
607            } else {
608                quote! {}
609            };
610            quote! {
611                match #id {
612                    ::core::result::Result::Ok(#bodyid) => {
613                        let mut #retid = alloc::vec::Vec::from(u8::to_ne_bytes(0));
614                        #vtun1
615                        #retid
616                    },
617                    ::core::result::Result::Err(#bodyid) => {
618                        let mut #retid = alloc::vec::Vec::from(u8::to_ne_bytes(1));
619                        #vtun2
620                        #retid
621                    },
622                }
623            }
624        }
625        Value::Own(ht) => {
626            let vi = resolve_handleable_to_resource(s, ht);
627            log::debug!("resolved ht to r (3) {:?} {:?}", ht, vi);
628            if s.is_guest {
629                let call = if s.is_wasmtime_guest {
630                    quote! { () }
631                } else {
632                    quote! {}
633                };
634                quote! {
635                    alloc::vec::Vec::from(u32::to_ne_bytes(#id.rep #call))
636                }
637            } else {
638                let rid = format_ident!("resource{}", vi);
639                quote! {
640                    let i = rts.#rid.len();
641                    rts.#rid.push_back(::hyperlight_common::resource::ResourceEntry::give(#id));
642                    alloc::vec::Vec::from(u32::to_ne_bytes(i as u32))
643                }
644            }
645        }
646        Value::Borrow(ht) => {
647            let vi = resolve_handleable_to_resource(s, ht);
648            log::debug!("resolved ht to r (6) {:?} {:?}", ht, vi);
649            if s.is_guest {
650                let call = if s.is_wasmtime_guest {
651                    quote! { () }
652                } else {
653                    quote! {}
654                };
655                quote! {
656                    alloc::vec::Vec::from(u32::to_ne_bytes(#id.rep #call))
657                }
658            } else {
659                let rid = format_ident!("resource{}", vi);
660                quote! {
661                    let i = rts.#rid.len();
662                    let (lrg, re) = ::hyperlight_common::resource::ResourceEntry::lend(#id);
663                    to_cleanup.push(Box::new(lrg));
664                    rts.#rid.push_back(re);
665                    alloc::vec::Vec::from(u32::to_ne_bytes(i as u32))
666                }
667            }
668        }
669        Value::Var(tv, _) => {
670            let Some(Tyvar::Bound(n)) = tv else {
671                panic!("impossible tyvar")
672            };
673            let ResolvedBoundVar::Definite {
674                final_bound_var: n,
675                ty: Defined::Value(vt),
676            } = s.resolve_bound_var(*n)
677            else {
678                panic!("unresolvable tyvar (2)");
679            };
680            let vt = vt.clone();
681            emit_hl_marshal_toplevel_value(s, id, Tyvar::Bound(n), &vt)
682        }
683    }
684}
685
686/// Emit code to unmarshal a parameter with value type `pt` from a
687/// slice named by `id`. The resultant token stream will be an
688/// expression which typechecks at the Rust type (as defined by the
689/// [`crate::rtypes`] module) of the given value type.
690pub fn emit_hl_unmarshal_param(s: &mut State, id: Ident, pt: &Value) -> TokenStream {
691    let toks = emit_hl_unmarshal_value(s, id, pt);
692    // Slight hack to avoid rust complaints about deserialised
693    // resource borrow lifetimes.
694    fn is_borrow(vt: &Value) -> bool {
695        match vt {
696            Value::Borrow(_) => true,
697            Value::Var(_, vt) => is_borrow(vt),
698            _ => false,
699        }
700    }
701    if s.is_guest && !s.is_wasmtime_guest && is_borrow(pt) {
702        quote! { &({ #toks }.0) }
703    } else {
704        quote! { { #toks }.0 }
705    }
706}
707
708/// Emit code to unmarshal the result of a function with result type
709/// `rt` from a slice named by `id`. The resultant token stream
710/// will be an expression which typechecks at the Rust type (as
711/// defined by the [`crate::rtypes`] module) of the unnamed type of
712/// the result, or unit if named results are used.
713///
714/// Precondition: the result type must only be a named result if there
715/// are no names in it (i.e. a unit type)
716pub fn emit_hl_unmarshal_result(s: &mut State, id: Ident, rt: &etypes::Result<'_>) -> TokenStream {
717    match rt {
718        Some(vt) => {
719            let toks = emit_hl_unmarshal_value(s, id, vt);
720            quote! { { #toks }.0 }
721        }
722        None => quote! { () },
723    }
724}
725
726/// Emit code to marshal a parameter with value type `pt` from a
727/// Rust value named by `id`. The resultant token stream will be an
728/// expression which typechecks as `Vec<u8>`.
729pub fn emit_hl_marshal_param(s: &mut State, id: Ident, pt: &Value) -> TokenStream {
730    let toks = emit_hl_marshal_value(s, id, pt);
731    quote! { { #toks } }
732}
733
734/// Emit code to marshal the result of a function with result type
735/// `rt` from a Rust value named by `id`. The resultant token stream
736/// will be an expression that which typechecks as `Vec<u8>`.
737///
738/// Precondition: the result type must only be a named result if there
739/// are no names in it (a unit type)
740pub fn emit_hl_marshal_result(s: &mut State, id: Ident, rt: &etypes::Result) -> TokenStream {
741    match rt {
742        None => quote! { ::alloc::vec::Vec::new() },
743        Some(vt) => {
744            let toks = emit_hl_marshal_value(s, id, vt);
745            quote! { { #toks } }
746        }
747    }
748}