Skip to main content

hyperlight_component_util/
rtypes.rs

1// SPDX-License-Identifier: Apache-2.0
2// Copyright 2025 The Hyperlight Authors.
3
4//! The Rust representation of a component type (etype)
5
6use std::collections::{BTreeMap, BTreeSet, VecDeque};
7use std::vec::Vec;
8
9use proc_macro2::TokenStream;
10use quote::{format_ident, quote};
11use syn::Ident;
12
13use crate::emit::{
14    FnName, ResourceItemName, State, WitName, find_colliding_import_names, import_member_names,
15    kebab_to_cons, kebab_to_exports_name, kebab_to_flags_const, kebab_to_fn, kebab_to_getter,
16    kebab_to_imports_name, kebab_to_namespace, kebab_to_type, kebab_to_var, split_wit_name,
17};
18use crate::etypes::{
19    self, Component, Defined, ExternDecl, ExternDesc, Func, Handleable, ImportExport, Instance,
20    Param, TypeBound, Tyvar, Value,
21};
22
23#[derive(Clone, Copy, Debug)]
24pub(crate) enum EmitPositivity {
25    Same,
26    Opposite,
27}
28impl EmitPositivity {
29    fn invert(self) -> Self {
30        match self {
31            EmitPositivity::Same => EmitPositivity::Opposite,
32            EmitPositivity::Opposite => EmitPositivity::Same,
33        }
34    }
35}
36
37/// When referring to an instance or resource trait, emit a token
38/// stream that instantiates any types it is parametrized by with our
39/// own best understanding of how to name the relevant type variables
40fn emit_tvis(s: &mut State, ep: EmitPositivity, tvs: Vec<u32>) -> TokenStream {
41    let tvs = tvs
42        .iter()
43        .map(|tv| emit_var_ref_noff(s, *tv, false))
44        .collect::<Vec<_>>();
45    let p = s.positivity_param.clone().unwrap_or(quote! { P });
46    match ep {
47        EmitPositivity::Same => quote! { <#p #(,#tvs)*> },
48        EmitPositivity::Opposite => {
49            quote! { <<#p as ::hyperlight_common::component::Positivity>::NegativeOfThis #(,#tvs)*> }
50        }
51    }
52}
53
54/// Construct a token stream referencing a trait at a given trait path
55pub(crate) fn trait_ref(
56    s: &mut State,
57    ep: EmitPositivity,
58    absolute: bool,
59    path: &[Ident],
60) -> TokenStream {
61    let rp = if absolute {
62        s.root_path()
63    } else {
64        TokenStream::new()
65    };
66    let t = s.resolve_trait_immut(absolute, path);
67    let tvis = emit_tvis(s, ep, t.tv_idxs());
68    quote! { #rp #(#path)::* #tvis }
69}
70
71/// Emit a token stream that references the type of a particular resource
72///
73/// - `n`: the absolute index (i.e. ignoring [`State::var_offset`]) of
74///   the component tyvar being referenced
75/// - `path`: the origin path between the module where we are and the
76///   module where the resource is defined.  The existence of this
77///   path implies that the var is "locally defined".
78fn emit_resource_ref(s: &mut State, n: u32, path: Vec<ImportExport>) -> TokenStream {
79    // todo: when the guest codegen is split into generic and wasm,
80    // this can go away, since an appropriate impl for the imports
81    // trait will be there
82    if s.is_guest && s.is_impl {
83        // Morally, this should check that the var is imported, but
84        // that information is gone by now (in the common prefix of
85        // the path that was chopped off), and we won't support
86        // resources exported from the guest until this whole special
87        // case is gone, so ignore it.
88        let id = format_ident!("HostResource{}", n);
89        return quote! { #id };
90    }
91    let Some(resource) = path.last() else {
92        panic!("resource reference path must contain the resource type");
93    };
94    let rtrait = kebab_to_type(resource.name());
95
96    // Deal specially with being in the local instance, where there is
97    // no instance type & so it is not easy to resolve the
98    // path-from-the-root to the resource type trait in question
99    if path.len() == 1 {
100        let helper = s.cur_helper_mod.clone().unwrap();
101        let rtrait = kebab_to_type(path[0].name());
102        let trait_ref = trait_ref(
103            s,
104            EmitPositivity::Same,
105            false,
106            &[helper.clone(), rtrait.clone()],
107        );
108        let mut sv = quote! { Self };
109        if let Some(s) = &s.self_param_var {
110            sv = quote! { #s };
111        };
112        return quote! { <#sv as #trait_ref>::T };
113    };
114
115    // Generally speaking, the structure that we expect to see in
116    // `path` ends in an instance that exports the resource type,
117    // followed by the resource type itself.
118
119    let mut toks = quote! { Self };
120    if path[0].imported() {
121        if let Some(iv) = &s.import_param_var {
122            toks = quote! { #iv }
123        }
124    } else if let Some(s) = &s.self_param_var {
125        toks = quote! { #s }
126    }
127    // todo: this will need a bit of adjustment to work well with
128    // plainname externs, which may require keeping track of the last
129    // interfacename we saw
130    let mut ep = EmitPositivity::Same;
131    for (i, p) in path[0..path.len() - 1].iter().enumerate() {
132        // Don't update ep if the import is the first item on the path
133        // and we don't have a var pointing at a different imports
134        // trait instance, since that means we are in an `Imports`
135        // trait, and our `P` has already been negative'd.
136        if p.imported() && (i != 0 || s.import_param_var.is_some()) {
137            ep = ep.invert();
138        }
139        let iwn = split_wit_name(p.name());
140        let export_name = if p.imported() {
141            import_member_names(&iwn, &s.colliding_import_names).0
142        } else {
143            kebab_to_type(iwn.name)
144        };
145
146        // The final item in the path will be referring to the
147        // relevant resource trait, while the others will be referring
148        // instance traits on the way there. So, we need to treat them
149        // separately.
150        let namespace_suffix = if i == path.len() - 2 {
151            &[kebab_to_namespace(iwn.name), rtrait.clone()] as &[Ident]
152        } else {
153            &[kebab_to_type(iwn.name)] as &[Ident]
154        };
155        let trait_path = iwn
156            .namespace_idents()
157            .iter()
158            .chain(namespace_suffix.iter())
159            .cloned()
160            .collect::<Vec<_>>();
161        let trait_ref = trait_ref(s, ep, true, &trait_path);
162
163        toks = quote! { <#toks::#export_name as #trait_ref> };
164    }
165    quote! { #toks::T }
166}
167
168/// Try to find a way to refer to the given type variable from the
169/// current module/trait. If this fails, the type must be coming from
170/// a sibling package, so we will have to emit a parametrization that
171/// the root (or at least someone higher up the tree) can instantiate.
172/// - `n`: the absolute index (i.e. ignoring [`State::var_offset`]) of
173///   the component tyvar being referenced
174fn try_find_local_var_id(
175    s: &mut State,
176    // this should be an absolute var number (no noff)
177    n: u32,
178) -> Option<TokenStream> {
179    if let Some((path, bound)) = s.is_noff_var_local(n) {
180        let var_is_helper = match bound {
181            TypeBound::Eq(_) => true,
182            TypeBound::SubResource => false,
183        };
184        if !var_is_helper {
185            // it is a resource type
186            if s.is_helper {
187                // but we're in that resource type, so that's ok
188                if path.len() == 1 && s.cur_trait == Some(kebab_to_type(path[0].name())) {
189                    return Some(quote! { Self::T });
190                }
191                // otherwise, there is no way to reference that from here
192                return None;
193            } else {
194                let mut path_strs = vec!["".to_string(); path.len()];
195                for (i, p) in path.iter().enumerate() {
196                    path_strs[i] = p.name().to_string();
197                }
198                let path = path
199                    .into_iter()
200                    .enumerate()
201                    .map(|(i, p)| match p {
202                        ImportExport::Import(_) => ImportExport::Import(&path_strs[i]),
203                        ImportExport::Export(_) => ImportExport::Export(&path_strs[i]),
204                    })
205                    .collect::<Vec<_>>();
206                return Some(emit_resource_ref(s, n, path));
207            }
208        }
209        tracing::debug!("path is {:?}\n", path);
210        let mut path = path.iter().rev();
211        let name = kebab_to_type(path.next().unwrap().name());
212        let owner = path.next();
213        if let Some(owner) = owner {
214            // if we have an instance type, use it
215            let wn = split_wit_name(owner.name());
216            let rp = s.root_path();
217            let tns = wn.namespace_path();
218            let helper = kebab_to_namespace(wn.name);
219            if tns.is_empty() {
220                Some(quote! { #rp #helper::#name })
221            } else {
222                Some(quote! { #rp #tns::#helper::#name })
223            }
224        } else {
225            let hp = s.helper_path();
226            Some(quote! { #hp #name })
227        }
228    } else {
229        None
230    }
231}
232
233/// Emit a token stream that references the given type variable in a
234/// type context, either directly if it is locally defined or by
235/// adding a parameter to the current type/trait/etc if necessary.
236/// - `tv`: the variable to reference
237///
238/// Precondition: `tv` must be a [`Tyvar::Bound`] tyvar
239pub fn emit_var_ref(s: &mut State, tv: &Tyvar) -> TokenStream {
240    let Tyvar::Bound(n) = tv else {
241        panic!("free tyvar in rust emit")
242    };
243    emit_var_ref_noff(s, n + s.var_offset as u32, false)
244}
245/// Emit a token stream that references the given type variable in a
246/// value context (e.g. a constructor), either directly if it is
247/// locally defined or by adding a parameter to the current
248/// type/trait/etc if necessary.
249/// - `tv`: the variable to reference
250///
251/// Precondition: `tv` must be a [`Tyvar::Bound`] tyvar
252pub fn emit_var_ref_value(s: &mut State, tv: &Tyvar) -> TokenStream {
253    let Tyvar::Bound(n) = tv else {
254        panic!("free tyvar in rust emit")
255    };
256    emit_var_ref_noff(s, n + s.var_offset as u32, true)
257}
258/// Emit a token stream that references the given bound type variable,
259/// either directly if it is locally defined or by adding a parameter
260/// to the current type/trait/etc if necessary.
261/// - `n`: the absolute index (i.e. ignoring [`State::var_offset`]) of
262///   the bound variable being referenced
263/// - `is_value`: whether this is a value (e.g. constructor) or type context.
264pub fn emit_var_ref_noff(s: &mut State, n: u32, is_value: bool) -> TokenStream {
265    tracing::debug!("var_ref {:?} {:?}", &s.bound_vars[n as usize], s.origin);
266    // if the variable was defined locally, try to reference it directly
267    let id = try_find_local_var_id(s, n);
268    let id = match id {
269        Some(id) => {
270            // if we are referencing the local one, we need to give it
271            // the variables it wants
272            let vs = s.get_noff_var_refs(n);
273            let vs = vs
274                .iter()
275                .map(|n| emit_var_ref_noff(s, *n, false))
276                .collect::<Vec<_>>();
277            let vs_toks = if !vs.is_empty() {
278                if is_value {
279                    quote! { ::<#(#vs),*> }
280                } else {
281                    quote! { <#(#vs),*> }
282                }
283            } else {
284                TokenStream::new()
285            };
286
287            quote! { #id #vs_toks }
288        }
289        None => {
290            // otherwise, record that whatever type is referencing it needs to
291            // have it in scope
292            s.need_noff_var(n);
293            let id = s.noff_var_id(n);
294            quote! { #id }
295        }
296    };
297    quote! { #id }
298}
299
300/// Format the name of the rust type corresponding to a component
301/// numeric type.
302///
303/// Precondition: `vt` is a numeric type (`S`, `U`, `F`)
304pub fn numeric_rtype(vt: &Value) -> (Ident, u8) {
305    match vt {
306        Value::S(w) => (format_ident!("i{}", w.width()), w.width()),
307        Value::U(w) => (format_ident!("u{}", w.width()), w.width()),
308        Value::F(w) => (format_ident!("f{}", w.width()), w.width()),
309        _ => panic!("numeric_rtype: internal invariant violation"),
310    }
311}
312
313/// Emit a Rust type corresponding to a given value type. The
314/// resultant token stream will parse as a Rust type.
315///
316/// Precondition: `vt` is an inline-able value type.
317pub fn emit_value(s: &mut State, vt: &Value) -> TokenStream {
318    match vt {
319        Value::Bool => quote! { bool },
320        Value::S(_) | Value::U(_) | Value::F(_) => {
321            let (id, _) = numeric_rtype(vt);
322            quote! { #id }
323        }
324        Value::Char => quote! { char },
325        Value::String => quote! { alloc::string::String },
326        Value::List(vt) => {
327            let vt = emit_value(s, vt);
328            quote! { alloc::vec::Vec<#vt> }
329        }
330        Value::FixList(vt, size) => {
331            let vt = emit_value(s, vt);
332            let size = *size as usize;
333            quote! { [#vt; #size] }
334        }
335        Value::Record(_) => panic!("record not at top level of valtype"),
336        Value::Tuple(vts) => {
337            let vts = vts.iter().map(|vt| emit_value(s, vt)).collect::<Vec<_>>();
338            quote! { (#(#vts),*) }
339        }
340        Value::Flags(_) => panic!("flags not at top level of valtype"),
341        Value::Variant(_) => panic!("flags not at top level of valtype"),
342        Value::Enum(_) => panic!("enum not at top level of valtype"),
343        Value::Option(vt) => {
344            let vt = emit_value(s, vt);
345            quote! { ::core::option::Option<#vt> }
346        }
347        Value::Result(vt1, vt2) => {
348            let unit = Value::Tuple(Vec::new());
349            let vt1 = emit_value(s, vt1.as_ref().as_ref().unwrap_or(&unit));
350            let vt2 = emit_value(s, vt2.as_ref().as_ref().unwrap_or(&unit));
351            quote! { ::core::result::Result<#vt1, #vt2> }
352        }
353        Value::Own(ht) => match ht {
354            Handleable::Resource(_) => panic!("bare resource in type"),
355            Handleable::Var(tv) => {
356                if s.is_guest {
357                    let wrap = if s.is_wasmtime_guest {
358                        |toks| quote! { ::wasmtime::component::Resource<#toks> }
359                    } else {
360                        |toks| toks
361                    };
362                    if !s.is_impl {
363                        wrap(emit_var_ref(s, tv))
364                    } else {
365                        let n = crate::hl::resolve_handleable_to_resource(s, ht);
366                        tracing::debug!("resolved ht to r (4) {:?} {:?}", ht, n);
367                        let id = format_ident!("HostResource{}", n);
368                        wrap(quote! { #id })
369                    }
370                } else {
371                    emit_var_ref(s, tv)
372                }
373            }
374        },
375        Value::Borrow(ht) => match ht {
376            Handleable::Resource(_) => panic!("bare resource in type"),
377            Handleable::Var(tv) => {
378                if s.is_guest {
379                    let wrap = if s.is_wasmtime_guest {
380                        |toks| quote! { ::wasmtime::component::Resource<#toks> }
381                    } else {
382                        |toks| quote! { &#toks }
383                    };
384                    if !s.is_impl {
385                        wrap(emit_var_ref(s, tv))
386                    } else {
387                        let n = crate::hl::resolve_handleable_to_resource(s, ht);
388                        tracing::debug!("resolved ht to r (5) {:?} {:?}", ht, n);
389                        let id = format_ident!("HostResource{}", n);
390                        wrap(quote! { #id })
391                    }
392                } else {
393                    let vr = emit_var_ref(s, tv);
394                    let p = s.positivity_param.clone().unwrap_or(quote! { P });
395                    quote! { <#p as ::hyperlight_common::component::Positivity>::Borrow<'_, #vr> }
396                }
397            }
398        },
399        Value::Var(Some(tv), _) => emit_var_ref(s, tv),
400        Value::Var(None, _) => panic!("value type with recorded but unknown var"),
401    }
402}
403
404/// Emit a Rust type corresponding to a given toplevel value type. The
405/// resultant token stream will parse as a Rust type declaration that
406/// defines a type named `id`.
407fn emit_value_toplevel(s: &mut State, v: Option<u32>, id: Ident, vt: &Value) -> TokenStream {
408    let is_wasmtime_guest = s.is_wasmtime_guest;
409    match vt {
410        Value::Record(rfs) => {
411            let (vs, toks) = gather_needed_vars(s, v, |s| {
412                let rfs = rfs
413                    .iter()
414                    .map(|rf| {
415                        let orig_name = rf.name.name;
416                        let id = kebab_to_var(orig_name);
417                        let derives = if s.is_wasmtime_guest {
418                            quote! { #[component(name = #orig_name)] }
419                        } else {
420                            TokenStream::new()
421                        };
422                        let ty = emit_value(s, &rf.ty);
423                        quote! { #derives pub #id: #ty }
424                    })
425                    .collect::<Vec<_>>();
426                quote! { #(#rfs),* }
427            });
428            let vs = emit_type_defn_var_list(s, vs);
429            let derives = if s.is_wasmtime_guest {
430                quote! {
431                    #[derive(::wasmtime::component::ComponentType)]
432                    #[derive(::wasmtime::component::Lift)]
433                    #[derive(::wasmtime::component::Lower)]
434                    #[component(record)]
435                }
436            } else {
437                TokenStream::new()
438            };
439            quote! {
440                #derives
441                #[derive(Debug)]
442                pub struct #id #vs { #toks }
443            }
444        }
445        Value::Flags(ns) => {
446            if s.is_wasmtime_guest {
447                let flags = ns
448                    .iter()
449                    .map(|n| {
450                        let orig_name = n.name;
451                        let const_name = kebab_to_flags_const(orig_name);
452                        quote! {
453                            #[component(name = #orig_name)]
454                            const #const_name;
455                        }
456                    })
457                    .collect::<Vec<_>>();
458                quote! {
459                    ::wasmtime::component::flags! {
460                        #id {
461                            #(#flags)*
462                        }
463                    }
464                }
465            } else {
466                let (vs, toks) = gather_needed_vars(s, v, |_| {
467                    let ns = ns
468                        .iter()
469                        .map(|n| {
470                            let id = kebab_to_var(n.name);
471                            quote! { pub #id: bool }
472                        })
473                        .collect::<Vec<_>>();
474                    quote! { #(#ns),* }
475                });
476                let vs = emit_type_defn_var_list(s, vs);
477                quote! {
478                    #[derive(Debug, Clone, PartialEq)]
479                    pub struct #id #vs { #toks }
480                }
481            }
482        }
483        Value::Variant(vcs) => {
484            let (vs, toks) = gather_needed_vars(s, v, |s| {
485                let vcs = vcs
486                    .iter()
487                    .map(|vc| {
488                        let orig_name = vc.name.name;
489                        let id = kebab_to_cons(orig_name);
490                        let derives = if s.is_wasmtime_guest {
491                            quote! { #[component(name = #orig_name)] }
492                        } else {
493                            TokenStream::new()
494                        };
495                        match &vc.ty {
496                            Some(ty) => {
497                                let ty = emit_value(s, ty);
498                                quote! { #derives #id(#ty) }
499                            }
500                            None => quote! { #derives #id },
501                        }
502                    })
503                    .collect::<Vec<_>>();
504                quote! { #(#vcs),* }
505            });
506            let vs = emit_type_defn_var_list(s, vs);
507            let derives = if s.is_wasmtime_guest {
508                quote! {
509                    #[derive(::wasmtime::component::ComponentType)]
510                    #[derive(::wasmtime::component::Lift)]
511                    #[derive(::wasmtime::component::Lower)]
512                    #[component(variant)]
513                }
514            } else {
515                TokenStream::new()
516            };
517            quote! {
518                #derives
519                #[derive(Debug)]
520                pub enum #id #vs { #toks }
521            }
522        }
523        Value::Enum(ns) => {
524            let (vs, toks) = gather_needed_vars(s, v, |_| {
525                let ns = ns
526                    .iter()
527                    .map(|n| {
528                        let orig_name = n.name;
529                        let id = kebab_to_cons(orig_name);
530                        let derives = if is_wasmtime_guest {
531                            quote! { #[component(name = #orig_name)] }
532                        } else {
533                            TokenStream::new()
534                        };
535                        quote! { #derives #id }
536                    })
537                    .collect::<Vec<_>>();
538                quote! { #(#ns),* }
539            });
540            let vs = emit_type_defn_var_list(s, vs);
541            let derives = if s.is_wasmtime_guest {
542                quote! {
543                    #[derive(::wasmtime::component::ComponentType)]
544                    #[derive(::wasmtime::component::Lift)]
545                    #[derive(::wasmtime::component::Lower)]
546                    #[component(enum)]
547                    #[repr(u8)] // todo: should this always be u8?
548                }
549            } else {
550                TokenStream::new()
551            };
552            quote! {
553                #derives
554                #[derive(Debug, Copy, Clone, PartialEq)]
555                pub enum #id #vs { #toks }
556            }
557        }
558        _ => emit_type_alias(s, v, id, |s| emit_value(s, vt)),
559    }
560}
561
562/// Emit a Rust type corresponding to a defined type. The token stream
563/// will parse as a Rust type declaration that defines a type named `id`.
564///
565/// Precondition: `dt` is not an instance or component, which we
566/// cannot deal with as first-class at the moment, or a bare resource
567/// type.
568fn emit_defined(s: &mut State, v: Option<u32>, id: Ident, dt: &Defined) -> TokenStream {
569    match dt {
570        // the lack of trait aliases makes emitting a name for an
571        // instance/component difficult in rust
572        Defined::Instance(_) | Defined::Component(_) => TokenStream::new(),
573        // toplevel vars should have been handled elsewhere
574        Defined::Handleable(Handleable::Resource(_)) => panic!("bare resource in type"),
575        Defined::Handleable(Handleable::Var(tv)) => {
576            emit_type_alias(s, v, id, |s| emit_var_ref(s, tv))
577        }
578        Defined::Value(vt) => emit_value_toplevel(s, v, id, vt),
579        Defined::Func(ft) => emit_type_alias(s, v, id, |s| emit_func(s, ft)),
580    }
581}
582
583/// Emit a Rust argument declaration, suitable for placing in the
584/// argument list of a function, for a given component function type
585/// parameter.
586pub fn emit_func_param(s: &mut State, p: &Param) -> TokenStream {
587    let name = kebab_to_var(p.name.name);
588    let ty = emit_value(s, &p.ty);
589    quote! { #name: #ty }
590}
591
592/// Emit a Rust version of a component function return type.
593///
594/// Precondition: the result type must only be a named result if there
595/// are no names in it (i.e. a unit type)
596pub fn emit_func_result(s: &mut State, r: &etypes::Result<'_>) -> TokenStream {
597    let inner = match r {
598        Some(vt) => emit_value(s, vt),
599        None => quote! { () },
600    };
601    let p = s.positivity_param.clone().unwrap_or(quote! { P });
602    quote! { <#p as ::hyperlight_common::component::Positivity>::CallResult<#inner> }
603}
604
605/// Emit a Rust typeversion of a component function type. This is only
606/// used for defining certain type aliases of functions, and so it
607/// truly is a Rust type-level function type, not a value-level
608/// declaration.
609fn emit_func(s: &mut State, ft: &Func) -> TokenStream {
610    let params = ft
611        .params
612        .iter()
613        .map(|p| emit_func_param(s, p))
614        .collect::<Vec<_>>();
615    let result = emit_func_result(s, &ft.result);
616    quote! { fn(#(#params),*) -> #result }
617}
618
619/// Gather the vars that are referenced when running `f`. If `v` is
620/// [`Some(vn)`], also record this as the set of vars needed by the
621/// bound tyvar with absolute index `vn`.
622fn gather_needed_vars<F: Fn(&mut State) -> TokenStream>(
623    s: &mut State,
624    v: Option<u32>,
625    f: F,
626) -> (BTreeSet<u32>, TokenStream) {
627    let mut needs_vars = BTreeSet::new();
628    let mut sv = s.with_needs_vars(&mut needs_vars);
629    let toks = f(&mut sv);
630    if let Some(vn) = v {
631        sv.record_needs_vars(vn);
632    }
633    drop(sv);
634    (needs_vars, toks)
635}
636/// Emit a Rust type parameter list that can be affixed to a type
637/// definition, given a set `vs` of the component-level bound tyvars
638/// that the type references but are not locally-defined.
639fn emit_type_defn_var_list(s: &mut State, vs: BTreeSet<u32>) -> TokenStream {
640    if vs.is_empty() {
641        TokenStream::new()
642    } else {
643        let vs = vs
644            .iter()
645            .map(|n| {
646                if s.is_guest {
647                    let t = s.noff_var_id(*n);
648                    quote! { #t: 'static }
649                } else {
650                    let t = s.noff_var_id(*n);
651                    quote! { #t }
652                }
653            })
654            .collect::<Vec<_>>();
655        quote! { <#(#vs),*> }
656    }
657}
658/// Emit a type alias declaration, allowing one to name an anonymous
659/// Rust type without creating a new nominal type.
660///
661/// - `v`: If [`Some(vn)`], the component-level bound tyvar absolute
662///   index that this declaration corresponds to
663/// - `id`: The name of the alias to produce
664/// - `f`: A function which produces a token stream that parses as a
665///   Rust type, to use as the body of the alias
666fn emit_type_alias<F: Fn(&mut State) -> TokenStream>(
667    s: &mut State,
668    v: Option<u32>,
669    id: Ident,
670    f: F,
671) -> TokenStream {
672    let (vs, toks) = gather_needed_vars(s, v, f);
673    let vs = emit_type_defn_var_list(s, vs);
674    quote! { pub type #id #vs = #toks; }
675}
676
677/// Emit (via returning) a Rust trait item corresponding to this
678/// extern decl
679fn emit_extern_decl<'a, 'b, 'c>(
680    origin_was_export: bool,
681    s: &'c mut State<'a, 'b>,
682    ed: &'c ExternDecl<'b>,
683) -> TokenStream {
684    tracing::debug!("  emitting decl {:?}", ed.kebab_name);
685    match &ed.desc {
686        ExternDesc::CoreModule(_) => panic!("core module (im/ex)ports are not supported"),
687        ExternDesc::Func(ft) => {
688            let mut s = s.push_origin(origin_was_export, ed.kebab_name);
689            match kebab_to_fn(ed.kebab_name) {
690                FnName::Plain(n) => {
691                    let params = ft
692                        .params
693                        .iter()
694                        .map(|p| emit_func_param(&mut s, p))
695                        .collect::<Vec<_>>();
696                    let result = emit_func_result(&mut s, &ft.result);
697                    quote! {
698                        fn #n(&mut self, #(#params),*) -> #result;
699                    }
700                }
701                FnName::Associated(r, n) => {
702                    let mut s = s.helper();
703                    s.cur_trait = Some(r.clone());
704                    let mut needs_vars = BTreeSet::new();
705                    let mut sv = s.with_needs_vars(&mut needs_vars);
706                    let params = ft
707                        .params
708                        .iter()
709                        .map(|p| emit_func_param(&mut sv, p))
710                        .collect::<Vec<_>>();
711                    match n {
712                        ResourceItemName::Constructor => {
713                            sv.cur_trait().items.extend(quote! {
714                                fn new(&mut self, #(#params),*) -> Self::T;
715                            });
716                        }
717                        ResourceItemName::Method(n) => {
718                            let result = emit_func_result(&mut sv, &ft.result);
719                            sv.cur_trait().items.extend(quote! {
720                                fn #n(&mut self, #(#params),*) -> #result;
721                            });
722                        }
723                        ResourceItemName::Static(n) => {
724                            let result = emit_func_result(&mut sv, &ft.result);
725                            sv.cur_trait().items.extend(quote! {
726                                fn #n(&mut self, #(#params),*) -> #result;
727                            });
728                        }
729                    }
730                    for v in needs_vars {
731                        let id = s.noff_var_id(v);
732                        s.cur_trait().tvs.insert(id, (Some(v), TokenStream::new()));
733                    }
734                    quote! {}
735                }
736            }
737        }
738        ExternDesc::Type(t) => {
739            fn go_defined<'a, 'b, 'c>(
740                s: &'c mut State<'a, 'b>,
741                ed: &'c ExternDecl<'b>,
742                t: &'c Defined<'b>,
743                v: Option<u32>,
744            ) -> TokenStream {
745                let id = kebab_to_type(ed.kebab_name);
746                let mut s = s.helper();
747
748                let t = emit_defined(&mut s, v, id, t);
749                s.cur_mod().items.extend(t);
750                TokenStream::new()
751            }
752            let edn: &'b str = ed.kebab_name;
753            let mut s: State<'_, 'b> = s.push_origin(origin_was_export, edn);
754            if let Some((n, bound)) = s.is_var_defn(t) {
755                match bound {
756                    TypeBound::Eq(t) => {
757                        // ensure that when go_defined() looks up vars
758                        // that might occur in the type, they resolve
759                        // properly
760                        let noff = s.var_offset as u32 + n;
761                        s.var_offset += n as usize + 1;
762                        go_defined(&mut s, ed, &t, Some(noff))
763                    }
764                    TypeBound::SubResource => {
765                        let rn = kebab_to_type(ed.kebab_name);
766                        s.add_helper_supertrait(rn.clone());
767                        let mut s = s.helper();
768                        s.cur_trait = Some(rn.clone());
769                        s.cur_trait().items.extend(quote! {
770                            type T: ::core::marker::Send;
771                        });
772                        quote! {}
773                    }
774                }
775            } else {
776                go_defined(&mut s, ed, t, None)
777            }
778        }
779        ExternDesc::Instance(it) => {
780            let mut s = s.push_origin(origin_was_export, ed.kebab_name);
781            let wn = split_wit_name(ed.kebab_name);
782            emit_instance(&mut s, wn.clone(), it);
783
784            let (member_tn, member_getter) = if origin_was_export {
785                (kebab_to_type(wn.name), kebab_to_getter(wn.name))
786            } else {
787                import_member_names(&wn, &s.colliding_import_names)
788            };
789            let trait_path = wn
790                .namespace_idents()
791                .iter()
792                .chain(&[kebab_to_type(wn.name)])
793                .cloned()
794                .collect::<Vec<_>>();
795            let trait_ref = trait_ref(&mut s, EmitPositivity::Same, true, &trait_path);
796            quote! {
797                type #member_tn: #trait_ref;
798                fn #member_getter(&mut self) -> impl ::core::borrow::BorrowMut<Self::#member_tn>;
799            }
800        }
801        ExternDesc::Component(_) => {
802            panic!("nested components not yet supported in rust bindings");
803        }
804    }
805}
806
807/// Emit (via mutating `s`) a Rust trait declaration corresponding to
808/// this instance type
809fn emit_instance<'a, 'b, 'c>(s: &'c mut State<'a, 'b>, wn: WitName, it: &'c Instance<'b>) {
810    tracing::debug!("emitting instance {:?}", wn);
811    let mut s = s.with_cursor(wn.namespace_idents());
812
813    let name = kebab_to_type(wn.name);
814
815    s.cur_helper_mod = Some(kebab_to_namespace(wn.name));
816    s.cur_trait = Some(name.clone());
817
818    // Temporary hack: if some items have already been generated for
819    // this wit:package/instance, implying that we have visited it
820    // before, we use [`State::for_var_effects_only`] to avoid adding
821    // duplicates of everything. We still bother running it, instead
822    // of bailing out entirely, to, as the name implies, get the
823    // effects on the variable tracking---otherwise the "second copy"
824    // of some Eq-bounded type variables will not properly acquire
825    // dependency tracking information.
826    //
827    // Since we don't really have strong semantic guarantees that the
828    // exact same contents will be in each occurrence of a
829    // wit:package/instance (and indeed they may well be stripped down
830    // to the essentials in each occurrence), this is NOT sound, and
831    // will need to be revisited. The really proper approach here is
832    // probably to properly spec some unification/principle type at
833    // the component level and preemptively run it on everything with
834    // the same extern name.
835    fn run_normally<'a, 'b, 'c>(
836        s: &'c mut State<'a, 'b>,
837        f: impl for<'d> FnOnce(&mut State<'d, 'b>),
838    ) {
839        f(s)
840    }
841    fn run_for_var_effects_only<'a, 'b, 'c>(
842        s: &'c mut State<'a, 'b>,
843        f: impl for<'d> FnOnce(&mut State<'d, 'b>),
844    ) {
845        s.for_var_effects_only(f)
846    }
847    let run = if s.cur_trait().items.is_empty() {
848        run_normally
849    } else {
850        run_for_var_effects_only
851    };
852    run(&mut s, &mut |s: &mut State<'_, 'b>| {
853        let mut needs_vars = BTreeSet::new();
854        let mut sv = s.with_needs_vars(&mut needs_vars);
855
856        let exports = it
857            .exports
858            .iter()
859            .map(|ed| emit_extern_decl(true, &mut sv, ed))
860            .collect::<Vec<_>>();
861
862        // instantiations for the supertraits
863
864        let mut stvs = BTreeMap::new();
865        let _ = sv.cur_trait(); // make sure it exists
866        let t = sv.cur_trait_immut();
867        for (ti, _) in t.supertraits.iter() {
868            let t = sv.resolve_trait_immut(false, ti);
869            stvs.insert(ti.clone(), t.tv_idxs());
870        }
871        // hack to make the local-definedness check work properly, since
872        // it usually should ignore the last origin component
873        sv.origin.push(ImportExport::Export("self"));
874        let mut stis = BTreeMap::new();
875        for (id, tvs) in stvs.into_iter() {
876            stis.insert(id, emit_tvis(&mut sv, EmitPositivity::Same, tvs));
877        }
878        for (id, ts) in stis.into_iter() {
879            sv.cur_trait().supertraits.get_mut(&id).unwrap().extend(ts);
880        }
881
882        drop(sv);
883        tracing::debug!("after exports, ncur_needs_vars is {:?}", needs_vars);
884        for v in needs_vars {
885            let id = s.noff_var_id(v);
886            s.cur_trait().tvs.insert(id, (Some(v), TokenStream::new()));
887        }
888
889        s.cur_trait().items.extend(quote! { #(#exports)* });
890    });
891}
892
893/// Emit (via mutating `s`) a set of Rust trait declarations
894/// corresponding to this component. This includes an `Imports` and an
895/// `Exports` trait, as well as a main trait with an `instantiate()`
896/// function that maps from an implementer of the imports to an
897/// implementor of the exports
898fn emit_component<'a, 'b, 'c>(s: &'c mut State<'a, 'b>, wn: WitName, ct: &'c Component<'b>) {
899    let mut s = s.with_cursor(wn.namespace_idents());
900
901    let base_name = kebab_to_type(wn.name);
902
903    s.cur_helper_mod = Some(kebab_to_namespace(wn.name));
904
905    let import_name = kebab_to_imports_name(wn.name);
906    *s.bound_vars = ct
907        .uvars
908        .iter()
909        .rev()
910        .map(Clone::clone)
911        .collect::<VecDeque<_>>();
912    s.cur_trait = Some(import_name.clone());
913    s.colliding_import_names = find_colliding_import_names(&ct.imports);
914    let imports = ct
915        .imports
916        .iter()
917        .map(|ed| emit_extern_decl(false, &mut s, ed))
918        .collect::<Vec<TokenStream>>();
919    s.cur_trait().items.extend(quote! { #(#imports)* });
920
921    s.adjust_vars(ct.instance.evars.len() as u32);
922    s.import_param_var = Some(format_ident!("I"));
923
924    let export_name = kebab_to_exports_name(wn.name);
925    *s.bound_vars = ct
926        .instance
927        .evars
928        .iter()
929        .rev()
930        .chain(ct.uvars.iter().rev())
931        .map(Clone::clone)
932        .collect::<VecDeque<_>>();
933    s.cur_trait = Some(export_name.clone());
934    let exports = ct
935        .instance
936        .unqualified
937        .exports
938        .iter()
939        .map(|ed| emit_extern_decl(true, &mut s, ed))
940        .collect::<Vec<_>>();
941    s.cur_trait().tvs.insert(
942        format_ident!("I"),
943        (
944            None,
945            quote! { #import_name<P::NegativeOfThis> + ::core::marker::Send },
946        ),
947    );
948    s.cur_trait().items.extend(quote! { #(#exports)* });
949
950    s.cur_helper_mod = None;
951    s.cur_trait = None;
952
953    s.cur_mod().items.extend(quote! {
954        pub trait #base_name<P: ::hyperlight_common::component::Positivity> {
955            type Exports<I: #import_name<P::NegativeOfThis> + ::core::marker::Send>: #export_name<P, I>;
956            // todo: can/should this 'static bound be avoided?
957            // it is important right now because this is closed over in host functions
958            fn instantiate<I: #import_name<P::NegativeOfThis> + ::core::marker::Send + 'static>(self, imports: I) -> <P as ::hyperlight_common::component::Positivity>::CallResult<Self::Exports<I>>;
959        }
960    });
961}
962
963/// See [`emit_component`]
964pub fn emit_toplevel<'a, 'b, 'c>(s: &'c mut State<'a, 'b>, n: &str, ct: &'c Component<'b>) {
965    let wn = split_wit_name(n);
966    emit_component(s, wn, ct);
967}