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