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