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)]
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)]
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                    #[component(enum)]
475                    #[repr(u8)] // todo: should this always be u8?
476                }
477            } else {
478                TokenStream::new()
479            };
480            quote! {
481                #derives
482                #[derive(Debug, Copy, Clone, PartialEq)]
483                pub enum #id #vs { #toks }
484            }
485        }
486        _ => emit_type_alias(s, v, id, |s| emit_value(s, vt)),
487    }
488}
489
490/// Emit a Rust type corresponding to a defined type. The token stream
491/// will parse as a Rust type declaration that defines a type named `id`.
492///
493/// Precondition: `dt` is not an instance or component, which we
494/// cannot deal with as first-class at the moment, or a bare resource
495/// type.
496fn emit_defined(s: &mut State, v: Option<u32>, id: Ident, dt: &Defined) -> TokenStream {
497    match dt {
498        // the lack of trait aliases makes emitting a name for an
499        // instance/component difficult in rust
500        Defined::Instance(_) | Defined::Component(_) => TokenStream::new(),
501        // toplevel vars should have been handled elsewhere
502        Defined::Handleable(Handleable::Resource(_)) => panic!("bare resource in type"),
503        Defined::Handleable(Handleable::Var(tv)) => {
504            emit_type_alias(s, v, id, |s| emit_var_ref(s, tv))
505        }
506        Defined::Value(vt) => emit_value_toplevel(s, v, id, vt),
507        Defined::Func(ft) => emit_type_alias(s, v, id, |s| emit_func(s, ft)),
508    }
509}
510
511/// Emit a Rust argument declaration, suitable for placing in the
512/// argument list of a function, for a given component function type
513/// parameter.
514pub fn emit_func_param(s: &mut State, p: &Param) -> TokenStream {
515    let name = kebab_to_var(p.name.name);
516    let ty = emit_value(s, &p.ty);
517    quote! { #name: #ty }
518}
519
520/// Emit a Rust version of a component function return type.
521///
522/// Precondition: the result type must only be a named result if there
523/// are no names in it (i.e. a unit type)
524pub fn emit_func_result(s: &mut State, r: &Result) -> TokenStream {
525    match r {
526        Result::Unnamed(vt) => emit_value(s, vt),
527        Result::Named(rs) if rs.is_empty() => quote! { () },
528        _ => panic!("multiple named function results are not currently supported"),
529    }
530}
531
532/// Emit a Rust typeversion of a component function type. This is only
533/// used for defining certain type aliases of functions, and so it
534/// truly is a Rust type-level function type, not a value-level
535/// declaration.
536fn emit_func(s: &mut State, ft: &Func) -> TokenStream {
537    let params = ft
538        .params
539        .iter()
540        .map(|p| emit_func_param(s, p))
541        .collect::<Vec<_>>();
542    let result = emit_func_result(s, &ft.result);
543    quote! { fn(#(#params),*) -> #result }
544}
545
546/// Gather the vars that are referenced when running `f`. If `v` is
547/// [`Some(vn)`], also record this as the set of vars needed by the
548/// bound tyvar with absolute index `vn`.
549fn gather_needed_vars<F: Fn(&mut State) -> TokenStream>(
550    s: &mut State,
551    v: Option<u32>,
552    f: F,
553) -> (BTreeSet<u32>, TokenStream) {
554    let mut needs_vars = BTreeSet::new();
555    let mut sv = s.with_needs_vars(&mut needs_vars);
556    let toks = f(&mut sv);
557    if let Some(vn) = v {
558        sv.record_needs_vars(vn);
559    }
560    drop(sv);
561    (needs_vars, toks)
562}
563/// Emit a Rust type parameter list that can be affixed to a type
564/// definition, given a set `vs` of the component-level bound tyvars
565/// that the type references but are not locally-defined.
566fn emit_type_defn_var_list(s: &mut State, vs: BTreeSet<u32>) -> TokenStream {
567    if vs.is_empty() {
568        TokenStream::new()
569    } else {
570        let vs = vs
571            .iter()
572            .map(|n| {
573                if s.is_guest {
574                    let t = s.noff_var_id(*n);
575                    quote! { #t: 'static }
576                } else {
577                    let t = s.noff_var_id(*n);
578                    quote! { #t }
579                }
580            })
581            .collect::<Vec<_>>();
582        quote! { <#(#vs),*> }
583    }
584}
585/// Emit a type alias declaration, allowing one to name an anonymous
586/// Rust type without creating a new nominal type.
587///
588/// - `v`: If [`Some(vn)`], the component-level bound tyvar absolute
589///   index that this declaration corresponds to
590/// - `id`: The name of the alias to produce
591/// - `f`: A function which produces a token stream that parses as a
592///   Rust type, to use as the body of the alias
593fn emit_type_alias<F: Fn(&mut State) -> TokenStream>(
594    s: &mut State,
595    v: Option<u32>,
596    id: Ident,
597    f: F,
598) -> TokenStream {
599    let (vs, toks) = gather_needed_vars(s, v, f);
600    let vs = emit_type_defn_var_list(s, vs);
601    quote! { pub type #id #vs = #toks; }
602}
603
604/// Emit (via returning) a Rust trait item corresponding to this
605/// extern decl
606fn emit_extern_decl<'a, 'b, 'c>(
607    is_export: bool,
608    s: &'c mut State<'a, 'b>,
609    ed: &'c ExternDecl<'b>,
610) -> TokenStream {
611    log::debug!("  emitting decl {:?}", ed.kebab_name);
612    match &ed.desc {
613        ExternDesc::CoreModule(_) => panic!("core module (im/ex)ports are not supported"),
614        ExternDesc::Func(ft) => {
615            let mut s = s.push_origin(is_export, ed.kebab_name);
616            match kebab_to_fn(ed.kebab_name) {
617                FnName::Plain(n) => {
618                    let params = ft
619                        .params
620                        .iter()
621                        .map(|p| emit_func_param(&mut s, p))
622                        .collect::<Vec<_>>();
623                    let result = emit_func_result(&mut s, &ft.result);
624                    quote! {
625                        fn #n(&mut self, #(#params),*) -> #result;
626                    }
627                }
628                FnName::Associated(r, n) => {
629                    let mut s = s.helper();
630                    s.cur_trait = Some(r.clone());
631                    let mut needs_vars = BTreeSet::new();
632                    let mut sv = s.with_needs_vars(&mut needs_vars);
633                    let params = ft
634                        .params
635                        .iter()
636                        .map(|p| emit_func_param(&mut sv, p))
637                        .collect::<Vec<_>>();
638                    match n {
639                        ResourceItemName::Constructor => {
640                            sv.cur_trait().items.extend(quote! {
641                                fn new(&mut self, #(#params),*) -> Self::T;
642                            });
643                        }
644                        ResourceItemName::Method(n) => {
645                            let result = emit_func_result(&mut sv, &ft.result);
646                            sv.cur_trait().items.extend(quote! {
647                                fn #n(&mut self, #(#params),*) -> #result;
648                            });
649                        }
650                        ResourceItemName::Static(n) => {
651                            let result = emit_func_result(&mut sv, &ft.result);
652                            sv.cur_trait().items.extend(quote! {
653                                fn #n(&mut self, #(#params),*) -> #result;
654                            });
655                        }
656                    }
657                    for v in needs_vars {
658                        let id = s.noff_var_id(v);
659                        s.cur_trait().tvs.insert(id, (Some(v), TokenStream::new()));
660                    }
661                    quote! {}
662                }
663            }
664        }
665        ExternDesc::Type(t) => {
666            fn go_defined<'a, 'b, 'c>(
667                s: &'c mut State<'a, 'b>,
668                ed: &'c ExternDecl<'b>,
669                t: &'c Defined<'b>,
670                v: Option<u32>,
671            ) -> TokenStream {
672                let id = kebab_to_type(ed.kebab_name);
673                let mut s = s.helper();
674
675                let t = emit_defined(&mut s, v, id, t);
676                s.cur_mod().items.extend(t);
677                TokenStream::new()
678            }
679            let edn: &'b str = ed.kebab_name;
680            let mut s: State<'_, 'b> = s.push_origin(is_export, edn);
681            if let Some((n, bound)) = s.is_var_defn(t) {
682                match bound {
683                    TypeBound::Eq(t) => {
684                        // ensure that when go_defined() looks up vars
685                        // that might occur in the type, they resolve
686                        // properly
687                        let noff = s.var_offset as u32 + n;
688                        s.var_offset += n as usize + 1;
689                        go_defined(&mut s, ed, &t, Some(noff))
690                    }
691                    TypeBound::SubResource => {
692                        let rn = kebab_to_type(ed.kebab_name);
693                        s.add_helper_supertrait(rn.clone());
694                        let mut s = s.helper();
695                        s.cur_trait = Some(rn.clone());
696                        s.cur_trait().items.extend(quote! {
697                            type T: ::core::marker::Send;
698                        });
699                        quote! {}
700                    }
701                }
702            } else {
703                go_defined(&mut s, ed, t, None)
704            }
705        }
706        ExternDesc::Instance(it) => {
707            let mut s = s.push_origin(is_export, ed.kebab_name);
708            let wn = split_wit_name(ed.kebab_name);
709            emit_instance(&mut s, wn.clone(), it);
710
711            let nsids = wn.namespace_idents();
712            let repr = s.r#trait(&nsids, kebab_to_type(wn.name));
713            let vs = if !repr.tvs.is_empty() {
714                let vs = repr.tvs.clone();
715                let tvs = vs
716                    .iter()
717                    .map(|(_, (tv, _))| emit_var_ref(&mut s, &Tyvar::Bound(tv.unwrap())));
718                quote! { <#(#tvs),*> }
719            } else {
720                TokenStream::new()
721            };
722
723            let getter = kebab_to_getter(wn.name);
724            let rp = s.root_path();
725            let tns = wn.namespace_path();
726            let tn = kebab_to_type(wn.name);
727            quote! {
728                type #tn: #rp #tns::#tn #vs;
729                fn #getter(&mut self) -> impl ::core::borrow::BorrowMut<Self::#tn>;
730            }
731        }
732        ExternDesc::Component(_) => {
733            panic!("nested components not yet supported in rust bindings");
734        }
735    }
736}
737
738/// Emit (via mutating `s`) a Rust trait declaration corresponding to
739/// this instance type
740fn emit_instance<'a, 'b, 'c>(s: &'c mut State<'a, 'b>, wn: WitName, it: &'c Instance<'b>) {
741    log::debug!("emitting instance {:?}", wn);
742    let mut s = s.with_cursor(wn.namespace_idents());
743
744    let name = kebab_to_type(wn.name);
745
746    s.cur_helper_mod = Some(kebab_to_namespace(wn.name));
747    s.cur_trait = Some(name.clone());
748    if !s.cur_trait().items.is_empty() {
749        // Temporary hack: we have visited this wit:package/instance
750        // before, so bail out instead of adding duplicates of
751        // everything. Since we don't really have strong semantic
752        // guarantees that the exact same contents will be in each
753        // occurrence of a wit:package/instance (and indeed they may
754        // well be stripped down to the essentials in each
755        // occurrence), this is NOT sound, and will need to be
756        // revisited.  The correct approach here is to change
757        // emit_extern_decl to create function/resource items in a
758        // Trait that can be merged properly, instead of directly
759        // emitting tokens.
760        return;
761    }
762
763    let mut needs_vars = BTreeSet::new();
764    let mut sv = s.with_needs_vars(&mut needs_vars);
765
766    let exports = it
767        .exports
768        .iter()
769        .map(|ed| emit_extern_decl(true, &mut sv, ed))
770        .collect::<Vec<_>>();
771
772    // instantiations for the supertraits
773
774    let mut stvs = BTreeMap::new();
775    let _ = sv.cur_trait(); // make sure it exists
776    let t = sv.cur_trait_immut();
777    for (ti, _) in t.supertraits.iter() {
778        let t = sv.resolve_trait_immut(false, ti);
779        stvs.insert(ti.clone(), t.tv_idxs());
780    }
781    // hack to make the local-definedness check work properly, since
782    // it usually should ignore the last origin component
783    sv.origin.push(ImportExport::Export("self"));
784    let mut stis = BTreeMap::new();
785    for (id, tvs) in stvs.into_iter() {
786        stis.insert(id, emit_tvis(&mut sv, tvs));
787    }
788    for (id, ts) in stis.into_iter() {
789        sv.cur_trait().supertraits.get_mut(&id).unwrap().extend(ts);
790    }
791
792    drop(sv);
793    log::debug!("after exports, ncur_needs_vars is {:?}", needs_vars);
794    for v in needs_vars {
795        let id = s.noff_var_id(v);
796        s.cur_trait().tvs.insert(id, (Some(v), TokenStream::new()));
797    }
798
799    s.cur_trait().items.extend(quote! { #(#exports)* });
800}
801
802/// Emit (via mutating `s`) a set of Rust trait declarations
803/// corresponding to this component. This includes an `Imports` and an
804/// `Exports` trait, as well as a main trait with an `instantiate()`
805/// function that maps from an implementer of the imports to an
806/// implementor of the exports
807fn emit_component<'a, 'b, 'c>(s: &'c mut State<'a, 'b>, wn: WitName, ct: &'c Component<'b>) {
808    let mut s = s.with_cursor(wn.namespace_idents());
809
810    let base_name = kebab_to_type(wn.name);
811
812    s.cur_helper_mod = Some(kebab_to_namespace(wn.name));
813
814    let import_name = kebab_to_imports_name(wn.name);
815    *s.bound_vars = ct
816        .uvars
817        .iter()
818        .rev()
819        .map(Clone::clone)
820        .collect::<VecDeque<_>>();
821    s.cur_trait = Some(import_name.clone());
822    let imports = ct
823        .imports
824        .iter()
825        .map(|ed| emit_extern_decl(false, &mut s, ed))
826        .collect::<Vec<TokenStream>>();
827    s.cur_trait().items.extend(quote! { #(#imports)* });
828
829    s.adjust_vars(ct.instance.evars.len() as u32);
830
831    s.import_param_var = Some(format_ident!("I"));
832
833    let export_name = kebab_to_exports_name(wn.name);
834    *s.bound_vars = ct
835        .instance
836        .evars
837        .iter()
838        .rev()
839        .chain(ct.uvars.iter().rev())
840        .map(Clone::clone)
841        .collect::<VecDeque<_>>();
842    s.cur_trait = Some(export_name.clone());
843    let exports = ct
844        .instance
845        .unqualified
846        .exports
847        .iter()
848        .map(|ed| emit_extern_decl(true, &mut s, ed))
849        .collect::<Vec<_>>();
850    s.cur_trait().tvs.insert(
851        format_ident!("I"),
852        (None, quote! { #import_name + ::core::marker::Send }),
853    );
854    s.cur_trait().items.extend(quote! { #(#exports)* });
855
856    s.cur_helper_mod = None;
857    s.cur_trait = None;
858
859    s.cur_mod().items.extend(quote! {
860        pub trait #base_name {
861            type Exports<I: #import_name + ::core::marker::Send>: #export_name<I>;
862            // todo: can/should this 'static bound be avoided?
863            // it is important right now because this is closed over in host functions
864            fn instantiate<I: #import_name + ::core::marker::Send + 'static>(self, imports: I) -> Self::Exports<I>;
865        }
866    });
867}
868
869/// See [`emit_component`]
870pub fn emit_toplevel<'a, 'b, 'c>(s: &'c mut State<'a, 'b>, n: &str, ct: &'c Component<'b>) {
871    let wn = split_wit_name(n);
872    emit_component(s, wn, ct);
873}