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    self, Component, Defined, ExternDecl, ExternDesc, Func, Handleable, ImportExport, Instance,
33    Param, 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::FixList(vt, size) => {
285            let vt = emit_value(s, vt);
286            let size = *size as usize;
287            quote! { [#vt; #size] }
288        }
289        Value::Record(_) => panic!("record not at top level of valtype"),
290        Value::Tuple(vts) => {
291            let vts = vts.iter().map(|vt| emit_value(s, vt)).collect::<Vec<_>>();
292            quote! { (#(#vts),*) }
293        }
294        Value::Flags(_) => panic!("flags not at top level of valtype"),
295        Value::Variant(_) => panic!("flags not at top level of valtype"),
296        Value::Enum(_) => panic!("enum not at top level of valtype"),
297        Value::Option(vt) => {
298            let vt = emit_value(s, vt);
299            quote! { ::core::option::Option<#vt> }
300        }
301        Value::Result(vt1, vt2) => {
302            let unit = Value::Tuple(Vec::new());
303            let vt1 = emit_value(s, vt1.as_ref().as_ref().unwrap_or(&unit));
304            let vt2 = emit_value(s, vt2.as_ref().as_ref().unwrap_or(&unit));
305            quote! { ::core::result::Result<#vt1, #vt2> }
306        }
307        Value::Own(ht) => match ht {
308            Handleable::Resource(_) => panic!("bare resource in type"),
309            Handleable::Var(tv) => {
310                if s.is_guest {
311                    let wrap = if s.is_wasmtime_guest {
312                        |toks| quote! { ::wasmtime::component::Resource<#toks> }
313                    } else {
314                        |toks| toks
315                    };
316                    if !s.is_impl {
317                        wrap(emit_var_ref(s, tv))
318                    } else {
319                        let n = crate::hl::resolve_handleable_to_resource(s, ht);
320                        log::debug!("resolved ht to r (4) {:?} {:?}", ht, n);
321                        let id = format_ident!("HostResource{}", n);
322                        wrap(quote! { #id })
323                    }
324                } else {
325                    emit_var_ref(s, tv)
326                }
327            }
328        },
329        Value::Borrow(ht) => match ht {
330            Handleable::Resource(_) => panic!("bare resource in type"),
331            Handleable::Var(tv) => {
332                if s.is_guest {
333                    let wrap = if s.is_wasmtime_guest {
334                        |toks| quote! { ::wasmtime::component::Resource<#toks> }
335                    } else {
336                        |toks| quote! { &#toks }
337                    };
338                    if !s.is_impl {
339                        wrap(emit_var_ref(s, tv))
340                    } else {
341                        let n = crate::hl::resolve_handleable_to_resource(s, ht);
342                        log::debug!("resolved ht to r (5) {:?} {:?}", ht, n);
343                        let id = format_ident!("HostResource{}", n);
344                        wrap(quote! { #id })
345                    }
346                } else {
347                    let vr = emit_var_ref(s, tv);
348                    quote! { ::hyperlight_common::resource::BorrowedResourceGuard<#vr> }
349                }
350            }
351        },
352        Value::Var(Some(tv), _) => emit_var_ref(s, tv),
353        Value::Var(None, _) => panic!("value type with recorded but unknown var"),
354    }
355}
356
357/// Emit a Rust type corresponding to a given toplevel value type. The
358/// resultant token stream will parse as a Rust type declaration that
359/// defines a type named `id`.
360fn emit_value_toplevel(s: &mut State, v: Option<u32>, id: Ident, vt: &Value) -> TokenStream {
361    let is_wasmtime_guest = s.is_wasmtime_guest;
362    match vt {
363        Value::Record(rfs) => {
364            let (vs, toks) = gather_needed_vars(s, v, |s| {
365                let rfs = rfs
366                    .iter()
367                    .map(|rf| {
368                        let orig_name = rf.name.name;
369                        let id = kebab_to_var(orig_name);
370                        let derives = if s.is_wasmtime_guest {
371                            quote! { #[component(name = #orig_name)] }
372                        } else {
373                            TokenStream::new()
374                        };
375                        let ty = emit_value(s, &rf.ty);
376                        quote! { #derives pub #id: #ty }
377                    })
378                    .collect::<Vec<_>>();
379                quote! { #(#rfs),* }
380            });
381            let vs = emit_type_defn_var_list(s, vs);
382            let derives = if s.is_wasmtime_guest {
383                quote! {
384                    #[derive(::wasmtime::component::ComponentType)]
385                    #[derive(::wasmtime::component::Lift)]
386                    #[derive(::wasmtime::component::Lower)]
387                    #[component(record)]
388                }
389            } else {
390                TokenStream::new()
391            };
392            quote! {
393                #derives
394                #[derive(Debug)]
395                pub struct #id #vs { #toks }
396            }
397        }
398        Value::Flags(ns) => {
399            let (vs, toks) = gather_needed_vars(s, v, |_| {
400                let ns = ns
401                    .iter()
402                    .map(|n| {
403                        let orig_name = n.name;
404                        let id = kebab_to_var(orig_name);
405                        quote! { pub #id: bool }
406                    })
407                    .collect::<Vec<_>>();
408                quote! { #(#ns),* }
409            });
410            let vs = emit_type_defn_var_list(s, vs);
411            quote! {
412                #[derive(Debug, Clone, PartialEq)]
413                pub struct #id #vs { #toks }
414            }
415        }
416        Value::Variant(vcs) => {
417            let (vs, toks) = gather_needed_vars(s, v, |s| {
418                let vcs = vcs
419                    .iter()
420                    .map(|vc| {
421                        let orig_name = vc.name.name;
422                        let id = kebab_to_cons(orig_name);
423                        let derives = if s.is_wasmtime_guest {
424                            quote! { #[component(name = #orig_name)] }
425                        } else {
426                            TokenStream::new()
427                        };
428                        match &vc.ty {
429                            Some(ty) => {
430                                let ty = emit_value(s, ty);
431                                quote! { #derives #id(#ty) }
432                            }
433                            None => quote! { #derives #id },
434                        }
435                    })
436                    .collect::<Vec<_>>();
437                quote! { #(#vcs),* }
438            });
439            let vs = emit_type_defn_var_list(s, vs);
440            let derives = if s.is_wasmtime_guest {
441                quote! {
442                    #[derive(::wasmtime::component::ComponentType)]
443                    #[derive(::wasmtime::component::Lift)]
444                    #[derive(::wasmtime::component::Lower)]
445                    #[component(variant)]
446                }
447            } else {
448                TokenStream::new()
449            };
450            quote! {
451                #derives
452                #[derive(Debug)]
453                pub enum #id #vs { #toks }
454            }
455        }
456        Value::Enum(ns) => {
457            let (vs, toks) = gather_needed_vars(s, v, |_| {
458                let ns = ns
459                    .iter()
460                    .map(|n| {
461                        let orig_name = n.name;
462                        let id = kebab_to_cons(orig_name);
463                        let derives = if is_wasmtime_guest {
464                            quote! { #[component(name = #orig_name)] }
465                        } else {
466                            TokenStream::new()
467                        };
468                        quote! { #derives #id }
469                    })
470                    .collect::<Vec<_>>();
471                quote! { #(#ns),* }
472            });
473            let vs = emit_type_defn_var_list(s, vs);
474            let derives = if s.is_wasmtime_guest {
475                quote! {
476                    #[derive(::wasmtime::component::ComponentType)]
477                    #[derive(::wasmtime::component::Lift)]
478                    #[derive(::wasmtime::component::Lower)]
479                    #[component(enum)]
480                    #[repr(u8)] // todo: should this always be u8?
481                }
482            } else {
483                TokenStream::new()
484            };
485            quote! {
486                #derives
487                #[derive(Debug, Copy, Clone, PartialEq)]
488                pub enum #id #vs { #toks }
489            }
490        }
491        _ => emit_type_alias(s, v, id, |s| emit_value(s, vt)),
492    }
493}
494
495/// Emit a Rust type corresponding to a defined type. The token stream
496/// will parse as a Rust type declaration that defines a type named `id`.
497///
498/// Precondition: `dt` is not an instance or component, which we
499/// cannot deal with as first-class at the moment, or a bare resource
500/// type.
501fn emit_defined(s: &mut State, v: Option<u32>, id: Ident, dt: &Defined) -> TokenStream {
502    match dt {
503        // the lack of trait aliases makes emitting a name for an
504        // instance/component difficult in rust
505        Defined::Instance(_) | Defined::Component(_) => TokenStream::new(),
506        // toplevel vars should have been handled elsewhere
507        Defined::Handleable(Handleable::Resource(_)) => panic!("bare resource in type"),
508        Defined::Handleable(Handleable::Var(tv)) => {
509            emit_type_alias(s, v, id, |s| emit_var_ref(s, tv))
510        }
511        Defined::Value(vt) => emit_value_toplevel(s, v, id, vt),
512        Defined::Func(ft) => emit_type_alias(s, v, id, |s| emit_func(s, ft)),
513    }
514}
515
516/// Emit a Rust argument declaration, suitable for placing in the
517/// argument list of a function, for a given component function type
518/// parameter.
519pub fn emit_func_param(s: &mut State, p: &Param) -> TokenStream {
520    let name = kebab_to_var(p.name.name);
521    let ty = emit_value(s, &p.ty);
522    quote! { #name: #ty }
523}
524
525/// Emit a Rust version of a component function return type.
526///
527/// Precondition: the result type must only be a named result if there
528/// are no names in it (i.e. a unit type)
529pub fn emit_func_result(s: &mut State, r: &etypes::Result<'_>) -> TokenStream {
530    match r {
531        Some(vt) => emit_value(s, vt),
532        None => quote! { () },
533    }
534}
535
536/// Emit a Rust typeversion of a component function type. This is only
537/// used for defining certain type aliases of functions, and so it
538/// truly is a Rust type-level function type, not a value-level
539/// declaration.
540fn emit_func(s: &mut State, ft: &Func) -> TokenStream {
541    let params = ft
542        .params
543        .iter()
544        .map(|p| emit_func_param(s, p))
545        .collect::<Vec<_>>();
546    let result = emit_func_result(s, &ft.result);
547    quote! { fn(#(#params),*) -> #result }
548}
549
550/// Gather the vars that are referenced when running `f`. If `v` is
551/// [`Some(vn)`], also record this as the set of vars needed by the
552/// bound tyvar with absolute index `vn`.
553fn gather_needed_vars<F: Fn(&mut State) -> TokenStream>(
554    s: &mut State,
555    v: Option<u32>,
556    f: F,
557) -> (BTreeSet<u32>, TokenStream) {
558    let mut needs_vars = BTreeSet::new();
559    let mut sv = s.with_needs_vars(&mut needs_vars);
560    let toks = f(&mut sv);
561    if let Some(vn) = v {
562        sv.record_needs_vars(vn);
563    }
564    drop(sv);
565    (needs_vars, toks)
566}
567/// Emit a Rust type parameter list that can be affixed to a type
568/// definition, given a set `vs` of the component-level bound tyvars
569/// that the type references but are not locally-defined.
570fn emit_type_defn_var_list(s: &mut State, vs: BTreeSet<u32>) -> TokenStream {
571    if vs.is_empty() {
572        TokenStream::new()
573    } else {
574        let vs = vs
575            .iter()
576            .map(|n| {
577                if s.is_guest {
578                    let t = s.noff_var_id(*n);
579                    quote! { #t: 'static }
580                } else {
581                    let t = s.noff_var_id(*n);
582                    quote! { #t }
583                }
584            })
585            .collect::<Vec<_>>();
586        quote! { <#(#vs),*> }
587    }
588}
589/// Emit a type alias declaration, allowing one to name an anonymous
590/// Rust type without creating a new nominal type.
591///
592/// - `v`: If [`Some(vn)`], the component-level bound tyvar absolute
593///   index that this declaration corresponds to
594/// - `id`: The name of the alias to produce
595/// - `f`: A function which produces a token stream that parses as a
596///   Rust type, to use as the body of the alias
597fn emit_type_alias<F: Fn(&mut State) -> TokenStream>(
598    s: &mut State,
599    v: Option<u32>,
600    id: Ident,
601    f: F,
602) -> TokenStream {
603    let (vs, toks) = gather_needed_vars(s, v, f);
604    let vs = emit_type_defn_var_list(s, vs);
605    quote! { pub type #id #vs = #toks; }
606}
607
608/// Emit (via returning) a Rust trait item corresponding to this
609/// extern decl
610fn emit_extern_decl<'a, 'b, 'c>(
611    is_export: bool,
612    s: &'c mut State<'a, 'b>,
613    ed: &'c ExternDecl<'b>,
614) -> TokenStream {
615    log::debug!("  emitting decl {:?}", ed.kebab_name);
616    match &ed.desc {
617        ExternDesc::CoreModule(_) => panic!("core module (im/ex)ports are not supported"),
618        ExternDesc::Func(ft) => {
619            let mut s = s.push_origin(is_export, ed.kebab_name);
620            match kebab_to_fn(ed.kebab_name) {
621                FnName::Plain(n) => {
622                    let params = ft
623                        .params
624                        .iter()
625                        .map(|p| emit_func_param(&mut s, p))
626                        .collect::<Vec<_>>();
627                    let result = emit_func_result(&mut s, &ft.result);
628                    quote! {
629                        fn #n(&mut self, #(#params),*) -> #result;
630                    }
631                }
632                FnName::Associated(r, n) => {
633                    let mut s = s.helper();
634                    s.cur_trait = Some(r.clone());
635                    let mut needs_vars = BTreeSet::new();
636                    let mut sv = s.with_needs_vars(&mut needs_vars);
637                    let params = ft
638                        .params
639                        .iter()
640                        .map(|p| emit_func_param(&mut sv, p))
641                        .collect::<Vec<_>>();
642                    match n {
643                        ResourceItemName::Constructor => {
644                            sv.cur_trait().items.extend(quote! {
645                                fn new(&mut self, #(#params),*) -> Self::T;
646                            });
647                        }
648                        ResourceItemName::Method(n) => {
649                            let result = emit_func_result(&mut sv, &ft.result);
650                            sv.cur_trait().items.extend(quote! {
651                                fn #n(&mut self, #(#params),*) -> #result;
652                            });
653                        }
654                        ResourceItemName::Static(n) => {
655                            let result = emit_func_result(&mut sv, &ft.result);
656                            sv.cur_trait().items.extend(quote! {
657                                fn #n(&mut self, #(#params),*) -> #result;
658                            });
659                        }
660                    }
661                    for v in needs_vars {
662                        let id = s.noff_var_id(v);
663                        s.cur_trait().tvs.insert(id, (Some(v), TokenStream::new()));
664                    }
665                    quote! {}
666                }
667            }
668        }
669        ExternDesc::Type(t) => {
670            fn go_defined<'a, 'b, 'c>(
671                s: &'c mut State<'a, 'b>,
672                ed: &'c ExternDecl<'b>,
673                t: &'c Defined<'b>,
674                v: Option<u32>,
675            ) -> TokenStream {
676                let id = kebab_to_type(ed.kebab_name);
677                let mut s = s.helper();
678
679                let t = emit_defined(&mut s, v, id, t);
680                s.cur_mod().items.extend(t);
681                TokenStream::new()
682            }
683            let edn: &'b str = ed.kebab_name;
684            let mut s: State<'_, 'b> = s.push_origin(is_export, edn);
685            if let Some((n, bound)) = s.is_var_defn(t) {
686                match bound {
687                    TypeBound::Eq(t) => {
688                        // ensure that when go_defined() looks up vars
689                        // that might occur in the type, they resolve
690                        // properly
691                        let noff = s.var_offset as u32 + n;
692                        s.var_offset += n as usize + 1;
693                        go_defined(&mut s, ed, &t, Some(noff))
694                    }
695                    TypeBound::SubResource => {
696                        let rn = kebab_to_type(ed.kebab_name);
697                        s.add_helper_supertrait(rn.clone());
698                        let mut s = s.helper();
699                        s.cur_trait = Some(rn.clone());
700                        s.cur_trait().items.extend(quote! {
701                            type T: ::core::marker::Send;
702                        });
703                        quote! {}
704                    }
705                }
706            } else {
707                go_defined(&mut s, ed, t, None)
708            }
709        }
710        ExternDesc::Instance(it) => {
711            let mut s = s.push_origin(is_export, ed.kebab_name);
712            let wn = split_wit_name(ed.kebab_name);
713            emit_instance(&mut s, wn.clone(), it);
714
715            let nsids = wn.namespace_idents();
716            let repr = s.r#trait(&nsids, kebab_to_type(wn.name));
717            let vs = if !repr.tvs.is_empty() {
718                let vs = repr.tvs.clone();
719                let tvs = vs
720                    .iter()
721                    .map(|(_, (tv, _))| emit_var_ref(&mut s, &Tyvar::Bound(tv.unwrap())));
722                quote! { <#(#tvs),*> }
723            } else {
724                TokenStream::new()
725            };
726
727            let getter = kebab_to_getter(wn.name);
728            let rp = s.root_path();
729            let tns = wn.namespace_path();
730            let tn = kebab_to_type(wn.name);
731            quote! {
732                type #tn: #rp #tns::#tn #vs;
733                fn #getter(&mut self) -> impl ::core::borrow::BorrowMut<Self::#tn>;
734            }
735        }
736        ExternDesc::Component(_) => {
737            panic!("nested components not yet supported in rust bindings");
738        }
739    }
740}
741
742/// Emit (via mutating `s`) a Rust trait declaration corresponding to
743/// this instance type
744fn emit_instance<'a, 'b, 'c>(s: &'c mut State<'a, 'b>, wn: WitName, it: &'c Instance<'b>) {
745    log::debug!("emitting instance {:?}", wn);
746    let mut s = s.with_cursor(wn.namespace_idents());
747
748    let name = kebab_to_type(wn.name);
749
750    s.cur_helper_mod = Some(kebab_to_namespace(wn.name));
751    s.cur_trait = Some(name.clone());
752    if !s.cur_trait().items.is_empty() {
753        // Temporary hack: we have visited this wit:package/instance
754        // before, so bail out instead of adding duplicates of
755        // everything. Since we don't really have strong semantic
756        // guarantees that the exact same contents will be in each
757        // occurrence of a wit:package/instance (and indeed they may
758        // well be stripped down to the essentials in each
759        // occurrence), this is NOT sound, and will need to be
760        // revisited.  The correct approach here is to change
761        // emit_extern_decl to create function/resource items in a
762        // Trait that can be merged properly, instead of directly
763        // emitting tokens.
764        return;
765    }
766
767    let mut needs_vars = BTreeSet::new();
768    let mut sv = s.with_needs_vars(&mut needs_vars);
769
770    let exports = it
771        .exports
772        .iter()
773        .map(|ed| emit_extern_decl(true, &mut sv, ed))
774        .collect::<Vec<_>>();
775
776    // instantiations for the supertraits
777
778    let mut stvs = BTreeMap::new();
779    let _ = sv.cur_trait(); // make sure it exists
780    let t = sv.cur_trait_immut();
781    for (ti, _) in t.supertraits.iter() {
782        let t = sv.resolve_trait_immut(false, ti);
783        stvs.insert(ti.clone(), t.tv_idxs());
784    }
785    // hack to make the local-definedness check work properly, since
786    // it usually should ignore the last origin component
787    sv.origin.push(ImportExport::Export("self"));
788    let mut stis = BTreeMap::new();
789    for (id, tvs) in stvs.into_iter() {
790        stis.insert(id, emit_tvis(&mut sv, tvs));
791    }
792    for (id, ts) in stis.into_iter() {
793        sv.cur_trait().supertraits.get_mut(&id).unwrap().extend(ts);
794    }
795
796    drop(sv);
797    log::debug!("after exports, ncur_needs_vars is {:?}", needs_vars);
798    for v in needs_vars {
799        let id = s.noff_var_id(v);
800        s.cur_trait().tvs.insert(id, (Some(v), TokenStream::new()));
801    }
802
803    s.cur_trait().items.extend(quote! { #(#exports)* });
804}
805
806/// Emit (via mutating `s`) a set of Rust trait declarations
807/// corresponding to this component. This includes an `Imports` and an
808/// `Exports` trait, as well as a main trait with an `instantiate()`
809/// function that maps from an implementer of the imports to an
810/// implementor of the exports
811fn emit_component<'a, 'b, 'c>(s: &'c mut State<'a, 'b>, wn: WitName, ct: &'c Component<'b>) {
812    let mut s = s.with_cursor(wn.namespace_idents());
813
814    let base_name = kebab_to_type(wn.name);
815
816    s.cur_helper_mod = Some(kebab_to_namespace(wn.name));
817
818    let import_name = kebab_to_imports_name(wn.name);
819    *s.bound_vars = ct
820        .uvars
821        .iter()
822        .rev()
823        .map(Clone::clone)
824        .collect::<VecDeque<_>>();
825    s.cur_trait = Some(import_name.clone());
826    let imports = ct
827        .imports
828        .iter()
829        .map(|ed| emit_extern_decl(false, &mut s, ed))
830        .collect::<Vec<TokenStream>>();
831    s.cur_trait().items.extend(quote! { #(#imports)* });
832
833    s.adjust_vars(ct.instance.evars.len() as u32);
834
835    s.import_param_var = Some(format_ident!("I"));
836
837    let export_name = kebab_to_exports_name(wn.name);
838    *s.bound_vars = ct
839        .instance
840        .evars
841        .iter()
842        .rev()
843        .chain(ct.uvars.iter().rev())
844        .map(Clone::clone)
845        .collect::<VecDeque<_>>();
846    s.cur_trait = Some(export_name.clone());
847    let exports = ct
848        .instance
849        .unqualified
850        .exports
851        .iter()
852        .map(|ed| emit_extern_decl(true, &mut s, ed))
853        .collect::<Vec<_>>();
854    s.cur_trait().tvs.insert(
855        format_ident!("I"),
856        (None, quote! { #import_name + ::core::marker::Send }),
857    );
858    s.cur_trait().items.extend(quote! { #(#exports)* });
859
860    s.cur_helper_mod = None;
861    s.cur_trait = None;
862
863    s.cur_mod().items.extend(quote! {
864        pub trait #base_name {
865            type Exports<I: #import_name + ::core::marker::Send>: #export_name<I>;
866            // todo: can/should this 'static bound be avoided?
867            // it is important right now because this is closed over in host functions
868            fn instantiate<I: #import_name + ::core::marker::Send + 'static>(self, imports: I) -> Self::Exports<I>;
869        }
870    });
871}
872
873/// See [`emit_component`]
874pub fn emit_toplevel<'a, 'b, 'c>(s: &'c mut State<'a, 'b>, n: &str, ct: &'c Component<'b>) {
875    let wn = split_wit_name(n);
876    emit_component(s, wn, ct);
877}