use std::collections::{BTreeMap, BTreeSet, VecDeque};
use std::vec::Vec;
use proc_macro2::TokenStream;
use quote::{format_ident, quote};
use syn::Ident;
use crate::emit::{
FnName, ResourceItemName, State, WitName, find_colliding_import_names, import_member_names,
kebab_to_cons, kebab_to_exports_name, kebab_to_flags_const, kebab_to_fn, kebab_to_getter,
kebab_to_imports_name, kebab_to_namespace, kebab_to_type, kebab_to_var, split_wit_name,
};
use crate::etypes::{
self, Component, Defined, ExternDecl, ExternDesc, Func, Handleable, ImportExport, Instance,
Param, TypeBound, Tyvar, Value,
};
#[derive(Clone, Copy, Debug)]
pub(crate) enum EmitPositivity {
Same,
Opposite,
}
impl EmitPositivity {
fn invert(self) -> Self {
match self {
EmitPositivity::Same => EmitPositivity::Opposite,
EmitPositivity::Opposite => EmitPositivity::Same,
}
}
}
fn emit_tvis(s: &mut State, ep: EmitPositivity, tvs: Vec<u32>) -> TokenStream {
let tvs = tvs
.iter()
.map(|tv| emit_var_ref_noff(s, *tv, false))
.collect::<Vec<_>>();
let p = s.positivity_param.clone().unwrap_or(quote! { P });
match ep {
EmitPositivity::Same => quote! { <#p #(,#tvs)*> },
EmitPositivity::Opposite => {
quote! { <<#p as ::hyperlight_common::component::Positivity>::NegativeOfThis #(,#tvs)*> }
}
}
}
pub(crate) fn trait_ref(
s: &mut State,
ep: EmitPositivity,
absolute: bool,
path: &[Ident],
) -> TokenStream {
let rp = if absolute {
s.root_path()
} else {
TokenStream::new()
};
let t = s.resolve_trait_immut(absolute, path);
let tvis = emit_tvis(s, ep, t.tv_idxs());
quote! { #rp #(#path)::* #tvis }
}
fn emit_resource_ref(s: &mut State, n: u32, path: Vec<ImportExport>) -> TokenStream {
if s.is_guest && s.is_impl {
let id = format_ident!("HostResource{}", n);
return quote! { #id };
}
let Some(resource) = path.last() else {
panic!("resource reference path must contain the resource type");
};
let rtrait = kebab_to_type(resource.name());
if path.len() == 1 {
let helper = s.cur_helper_mod.clone().unwrap();
let rtrait = kebab_to_type(path[0].name());
let trait_ref = trait_ref(
s,
EmitPositivity::Same,
false,
&[helper.clone(), rtrait.clone()],
);
let mut sv = quote! { Self };
if let Some(s) = &s.self_param_var {
sv = quote! { #s };
};
return quote! { <#sv as #trait_ref>::T };
};
let mut toks = quote! { Self };
if path[0].imported() {
if let Some(iv) = &s.import_param_var {
toks = quote! { #iv }
}
} else if let Some(s) = &s.self_param_var {
toks = quote! { #s }
}
let mut ep = EmitPositivity::Same;
for (i, p) in path[0..path.len() - 1].iter().enumerate() {
if p.imported() && (i != 0 || s.import_param_var.is_some()) {
ep = ep.invert();
}
let iwn = split_wit_name(p.name());
let export_name = if p.imported() {
import_member_names(&iwn, &s.colliding_import_names).0
} else {
kebab_to_type(iwn.name)
};
let namespace_suffix = if i == path.len() - 2 {
&[kebab_to_namespace(iwn.name), rtrait.clone()] as &[Ident]
} else {
&[kebab_to_type(iwn.name)] as &[Ident]
};
let trait_path = iwn
.namespace_idents()
.iter()
.chain(namespace_suffix.iter())
.cloned()
.collect::<Vec<_>>();
let trait_ref = trait_ref(s, ep, true, &trait_path);
toks = quote! { <#toks::#export_name as #trait_ref> };
}
quote! { #toks::T }
}
fn try_find_local_var_id(
s: &mut State,
n: u32,
) -> Option<TokenStream> {
if let Some((path, bound)) = s.is_noff_var_local(n) {
let var_is_helper = match bound {
TypeBound::Eq(_) => true,
TypeBound::SubResource => false,
};
if !var_is_helper {
if s.is_helper {
if path.len() == 1 && s.cur_trait == Some(kebab_to_type(path[0].name())) {
return Some(quote! { Self::T });
}
return None;
} else {
let mut path_strs = vec!["".to_string(); path.len()];
for (i, p) in path.iter().enumerate() {
path_strs[i] = p.name().to_string();
}
let path = path
.into_iter()
.enumerate()
.map(|(i, p)| match p {
ImportExport::Import(_) => ImportExport::Import(&path_strs[i]),
ImportExport::Export(_) => ImportExport::Export(&path_strs[i]),
})
.collect::<Vec<_>>();
return Some(emit_resource_ref(s, n, path));
}
}
tracing::debug!("path is {:?}\n", path);
let mut path = path.iter().rev();
let name = kebab_to_type(path.next().unwrap().name());
let owner = path.next();
if let Some(owner) = owner {
let wn = split_wit_name(owner.name());
let rp = s.root_path();
let tns = wn.namespace_path();
let helper = kebab_to_namespace(wn.name);
if tns.is_empty() {
Some(quote! { #rp #helper::#name })
} else {
Some(quote! { #rp #tns::#helper::#name })
}
} else {
let hp = s.helper_path();
Some(quote! { #hp #name })
}
} else {
None
}
}
pub fn emit_var_ref(s: &mut State, tv: &Tyvar) -> TokenStream {
let Tyvar::Bound(n) = tv else {
panic!("free tyvar in rust emit")
};
emit_var_ref_noff(s, n + s.var_offset as u32, false)
}
pub fn emit_var_ref_value(s: &mut State, tv: &Tyvar) -> TokenStream {
let Tyvar::Bound(n) = tv else {
panic!("free tyvar in rust emit")
};
emit_var_ref_noff(s, n + s.var_offset as u32, true)
}
pub fn emit_var_ref_noff(s: &mut State, n: u32, is_value: bool) -> TokenStream {
tracing::debug!("var_ref {:?} {:?}", &s.bound_vars[n as usize], s.origin);
let id = try_find_local_var_id(s, n);
let id = match id {
Some(id) => {
let vs = s.get_noff_var_refs(n);
let vs = vs
.iter()
.map(|n| emit_var_ref_noff(s, *n, false))
.collect::<Vec<_>>();
let vs_toks = if !vs.is_empty() {
if is_value {
quote! { ::<#(#vs),*> }
} else {
quote! { <#(#vs),*> }
}
} else {
TokenStream::new()
};
quote! { #id #vs_toks }
}
None => {
s.need_noff_var(n);
let id = s.noff_var_id(n);
quote! { #id }
}
};
quote! { #id }
}
pub fn numeric_rtype(vt: &Value) -> (Ident, u8) {
match vt {
Value::S(w) => (format_ident!("i{}", w.width()), w.width()),
Value::U(w) => (format_ident!("u{}", w.width()), w.width()),
Value::F(w) => (format_ident!("f{}", w.width()), w.width()),
_ => panic!("numeric_rtype: internal invariant violation"),
}
}
pub fn emit_value(s: &mut State, vt: &Value) -> TokenStream {
match vt {
Value::Bool => quote! { bool },
Value::S(_) | Value::U(_) | Value::F(_) => {
let (id, _) = numeric_rtype(vt);
quote! { #id }
}
Value::Char => quote! { char },
Value::String => quote! { alloc::string::String },
Value::List(vt) => {
let vt = emit_value(s, vt);
quote! { alloc::vec::Vec<#vt> }
}
Value::FixList(vt, size) => {
let vt = emit_value(s, vt);
let size = *size as usize;
quote! { [#vt; #size] }
}
Value::Record(_) => panic!("record not at top level of valtype"),
Value::Tuple(vts) => {
let vts = vts.iter().map(|vt| emit_value(s, vt)).collect::<Vec<_>>();
quote! { (#(#vts),*) }
}
Value::Flags(_) => panic!("flags not at top level of valtype"),
Value::Variant(_) => panic!("flags not at top level of valtype"),
Value::Enum(_) => panic!("enum not at top level of valtype"),
Value::Option(vt) => {
let vt = emit_value(s, vt);
quote! { ::core::option::Option<#vt> }
}
Value::Result(vt1, vt2) => {
let unit = Value::Tuple(Vec::new());
let vt1 = emit_value(s, vt1.as_ref().as_ref().unwrap_or(&unit));
let vt2 = emit_value(s, vt2.as_ref().as_ref().unwrap_or(&unit));
quote! { ::core::result::Result<#vt1, #vt2> }
}
Value::Own(ht) => match ht {
Handleable::Resource(_) => panic!("bare resource in type"),
Handleable::Var(tv) => {
if s.is_guest {
let wrap = if s.is_wasmtime_guest {
|toks| quote! { ::wasmtime::component::Resource<#toks> }
} else {
|toks| toks
};
if !s.is_impl {
wrap(emit_var_ref(s, tv))
} else {
let n = crate::hl::resolve_handleable_to_resource(s, ht);
tracing::debug!("resolved ht to r (4) {:?} {:?}", ht, n);
let id = format_ident!("HostResource{}", n);
wrap(quote! { #id })
}
} else {
emit_var_ref(s, tv)
}
}
},
Value::Borrow(ht) => match ht {
Handleable::Resource(_) => panic!("bare resource in type"),
Handleable::Var(tv) => {
if s.is_guest {
let wrap = if s.is_wasmtime_guest {
|toks| quote! { ::wasmtime::component::Resource<#toks> }
} else {
|toks| quote! { &#toks }
};
if !s.is_impl {
wrap(emit_var_ref(s, tv))
} else {
let n = crate::hl::resolve_handleable_to_resource(s, ht);
tracing::debug!("resolved ht to r (5) {:?} {:?}", ht, n);
let id = format_ident!("HostResource{}", n);
wrap(quote! { #id })
}
} else {
let vr = emit_var_ref(s, tv);
let p = s.positivity_param.clone().unwrap_or(quote! { P });
quote! { <#p as ::hyperlight_common::component::Positivity>::Borrow<'_, #vr> }
}
}
},
Value::Var(Some(tv), _) => emit_var_ref(s, tv),
Value::Var(None, _) => panic!("value type with recorded but unknown var"),
}
}
fn emit_value_toplevel(s: &mut State, v: Option<u32>, id: Ident, vt: &Value) -> TokenStream {
let is_wasmtime_guest = s.is_wasmtime_guest;
match vt {
Value::Record(rfs) => {
let (vs, toks) = gather_needed_vars(s, v, |s| {
let rfs = rfs
.iter()
.map(|rf| {
let orig_name = rf.name.name;
let id = kebab_to_var(orig_name);
let derives = if s.is_wasmtime_guest {
quote! { #[component(name = #orig_name)] }
} else {
TokenStream::new()
};
let ty = emit_value(s, &rf.ty);
quote! { #derives pub #id: #ty }
})
.collect::<Vec<_>>();
quote! { #(#rfs),* }
});
let vs = emit_type_defn_var_list(s, vs);
let derives = if s.is_wasmtime_guest {
quote! {
#[derive(::wasmtime::component::ComponentType)]
#[derive(::wasmtime::component::Lift)]
#[derive(::wasmtime::component::Lower)]
#[component(record)]
}
} else {
TokenStream::new()
};
quote! {
#derives
#[derive(Debug)]
pub struct #id #vs { #toks }
}
}
Value::Flags(ns) => {
if s.is_wasmtime_guest {
let flags = ns
.iter()
.map(|n| {
let orig_name = n.name;
let const_name = kebab_to_flags_const(orig_name);
quote! {
#[component(name = #orig_name)]
const #const_name;
}
})
.collect::<Vec<_>>();
quote! {
::wasmtime::component::flags! {
#id {
#(#flags)*
}
}
}
} else {
let (vs, toks) = gather_needed_vars(s, v, |_| {
let ns = ns
.iter()
.map(|n| {
let id = kebab_to_var(n.name);
quote! { pub #id: bool }
})
.collect::<Vec<_>>();
quote! { #(#ns),* }
});
let vs = emit_type_defn_var_list(s, vs);
quote! {
#[derive(Debug, Clone, PartialEq)]
pub struct #id #vs { #toks }
}
}
}
Value::Variant(vcs) => {
let (vs, toks) = gather_needed_vars(s, v, |s| {
let vcs = vcs
.iter()
.map(|vc| {
let orig_name = vc.name.name;
let id = kebab_to_cons(orig_name);
let derives = if s.is_wasmtime_guest {
quote! { #[component(name = #orig_name)] }
} else {
TokenStream::new()
};
match &vc.ty {
Some(ty) => {
let ty = emit_value(s, ty);
quote! { #derives #id(#ty) }
}
None => quote! { #derives #id },
}
})
.collect::<Vec<_>>();
quote! { #(#vcs),* }
});
let vs = emit_type_defn_var_list(s, vs);
let derives = if s.is_wasmtime_guest {
quote! {
#[derive(::wasmtime::component::ComponentType)]
#[derive(::wasmtime::component::Lift)]
#[derive(::wasmtime::component::Lower)]
#[component(variant)]
}
} else {
TokenStream::new()
};
quote! {
#derives
#[derive(Debug)]
pub enum #id #vs { #toks }
}
}
Value::Enum(ns) => {
let (vs, toks) = gather_needed_vars(s, v, |_| {
let ns = ns
.iter()
.map(|n| {
let orig_name = n.name;
let id = kebab_to_cons(orig_name);
let derives = if is_wasmtime_guest {
quote! { #[component(name = #orig_name)] }
} else {
TokenStream::new()
};
quote! { #derives #id }
})
.collect::<Vec<_>>();
quote! { #(#ns),* }
});
let vs = emit_type_defn_var_list(s, vs);
let derives = if s.is_wasmtime_guest {
quote! {
#[derive(::wasmtime::component::ComponentType)]
#[derive(::wasmtime::component::Lift)]
#[derive(::wasmtime::component::Lower)]
#[component(enum)]
#[repr(u8)] }
} else {
TokenStream::new()
};
quote! {
#derives
#[derive(Debug, Copy, Clone, PartialEq)]
pub enum #id #vs { #toks }
}
}
_ => emit_type_alias(s, v, id, |s| emit_value(s, vt)),
}
}
fn emit_defined(s: &mut State, v: Option<u32>, id: Ident, dt: &Defined) -> TokenStream {
match dt {
Defined::Instance(_) | Defined::Component(_) => TokenStream::new(),
Defined::Handleable(Handleable::Resource(_)) => panic!("bare resource in type"),
Defined::Handleable(Handleable::Var(tv)) => {
emit_type_alias(s, v, id, |s| emit_var_ref(s, tv))
}
Defined::Value(vt) => emit_value_toplevel(s, v, id, vt),
Defined::Func(ft) => emit_type_alias(s, v, id, |s| emit_func(s, ft)),
}
}
pub fn emit_func_param(s: &mut State, p: &Param) -> TokenStream {
let name = kebab_to_var(p.name.name);
let ty = emit_value(s, &p.ty);
quote! { #name: #ty }
}
pub fn emit_func_result(s: &mut State, r: &etypes::Result<'_>) -> TokenStream {
let inner = match r {
Some(vt) => emit_value(s, vt),
None => quote! { () },
};
let p = s.positivity_param.clone().unwrap_or(quote! { P });
quote! { <#p as ::hyperlight_common::component::Positivity>::CallResult<#inner> }
}
fn emit_func(s: &mut State, ft: &Func) -> TokenStream {
let params = ft
.params
.iter()
.map(|p| emit_func_param(s, p))
.collect::<Vec<_>>();
let result = emit_func_result(s, &ft.result);
quote! { fn(#(#params),*) -> #result }
}
fn gather_needed_vars<F: Fn(&mut State) -> TokenStream>(
s: &mut State,
v: Option<u32>,
f: F,
) -> (BTreeSet<u32>, TokenStream) {
let mut needs_vars = BTreeSet::new();
let mut sv = s.with_needs_vars(&mut needs_vars);
let toks = f(&mut sv);
if let Some(vn) = v {
sv.record_needs_vars(vn);
}
drop(sv);
(needs_vars, toks)
}
fn emit_type_defn_var_list(s: &mut State, vs: BTreeSet<u32>) -> TokenStream {
if vs.is_empty() {
TokenStream::new()
} else {
let vs = vs
.iter()
.map(|n| {
if s.is_guest {
let t = s.noff_var_id(*n);
quote! { #t: 'static }
} else {
let t = s.noff_var_id(*n);
quote! { #t }
}
})
.collect::<Vec<_>>();
quote! { <#(#vs),*> }
}
}
fn emit_type_alias<F: Fn(&mut State) -> TokenStream>(
s: &mut State,
v: Option<u32>,
id: Ident,
f: F,
) -> TokenStream {
let (vs, toks) = gather_needed_vars(s, v, f);
let vs = emit_type_defn_var_list(s, vs);
quote! { pub type #id #vs = #toks; }
}
fn emit_extern_decl<'a, 'b, 'c>(
origin_was_export: bool,
s: &'c mut State<'a, 'b>,
ed: &'c ExternDecl<'b>,
) -> TokenStream {
tracing::debug!(" emitting decl {:?}", ed.kebab_name);
match &ed.desc {
ExternDesc::CoreModule(_) => panic!("core module (im/ex)ports are not supported"),
ExternDesc::Func(ft) => {
let mut s = s.push_origin(origin_was_export, ed.kebab_name);
match kebab_to_fn(ed.kebab_name) {
FnName::Plain(n) => {
let params = ft
.params
.iter()
.map(|p| emit_func_param(&mut s, p))
.collect::<Vec<_>>();
let result = emit_func_result(&mut s, &ft.result);
quote! {
fn #n(&mut self, #(#params),*) -> #result;
}
}
FnName::Associated(r, n) => {
let mut s = s.helper();
s.cur_trait = Some(r.clone());
let mut needs_vars = BTreeSet::new();
let mut sv = s.with_needs_vars(&mut needs_vars);
let params = ft
.params
.iter()
.map(|p| emit_func_param(&mut sv, p))
.collect::<Vec<_>>();
match n {
ResourceItemName::Constructor => {
sv.cur_trait().items.extend(quote! {
fn new(&mut self, #(#params),*) -> Self::T;
});
}
ResourceItemName::Method(n) => {
let result = emit_func_result(&mut sv, &ft.result);
sv.cur_trait().items.extend(quote! {
fn #n(&mut self, #(#params),*) -> #result;
});
}
ResourceItemName::Static(n) => {
let result = emit_func_result(&mut sv, &ft.result);
sv.cur_trait().items.extend(quote! {
fn #n(&mut self, #(#params),*) -> #result;
});
}
}
for v in needs_vars {
let id = s.noff_var_id(v);
s.cur_trait().tvs.insert(id, (Some(v), TokenStream::new()));
}
quote! {}
}
}
}
ExternDesc::Type(t) => {
fn go_defined<'a, 'b, 'c>(
s: &'c mut State<'a, 'b>,
ed: &'c ExternDecl<'b>,
t: &'c Defined<'b>,
v: Option<u32>,
) -> TokenStream {
let id = kebab_to_type(ed.kebab_name);
let mut s = s.helper();
let t = emit_defined(&mut s, v, id, t);
s.cur_mod().items.extend(t);
TokenStream::new()
}
let edn: &'b str = ed.kebab_name;
let mut s: State<'_, 'b> = s.push_origin(origin_was_export, edn);
if let Some((n, bound)) = s.is_var_defn(t) {
match bound {
TypeBound::Eq(t) => {
let noff = s.var_offset as u32 + n;
s.var_offset += n as usize + 1;
go_defined(&mut s, ed, &t, Some(noff))
}
TypeBound::SubResource => {
let rn = kebab_to_type(ed.kebab_name);
s.add_helper_supertrait(rn.clone());
let mut s = s.helper();
s.cur_trait = Some(rn.clone());
s.cur_trait().items.extend(quote! {
type T: ::core::marker::Send;
});
quote! {}
}
}
} else {
go_defined(&mut s, ed, t, None)
}
}
ExternDesc::Instance(it) => {
let mut s = s.push_origin(origin_was_export, ed.kebab_name);
let wn = split_wit_name(ed.kebab_name);
emit_instance(&mut s, wn.clone(), it);
let (member_tn, member_getter) = if origin_was_export {
(kebab_to_type(wn.name), kebab_to_getter(wn.name))
} else {
import_member_names(&wn, &s.colliding_import_names)
};
let trait_path = wn
.namespace_idents()
.iter()
.chain(&[kebab_to_type(wn.name)])
.cloned()
.collect::<Vec<_>>();
let trait_ref = trait_ref(&mut s, EmitPositivity::Same, true, &trait_path);
quote! {
type #member_tn: #trait_ref;
fn #member_getter(&mut self) -> impl ::core::borrow::BorrowMut<Self::#member_tn>;
}
}
ExternDesc::Component(_) => {
panic!("nested components not yet supported in rust bindings");
}
}
}
fn emit_instance<'a, 'b, 'c>(s: &'c mut State<'a, 'b>, wn: WitName, it: &'c Instance<'b>) {
tracing::debug!("emitting instance {:?}", wn);
let mut s = s.with_cursor(wn.namespace_idents());
let name = kebab_to_type(wn.name);
s.cur_helper_mod = Some(kebab_to_namespace(wn.name));
s.cur_trait = Some(name.clone());
fn run_normally<'a, 'b, 'c>(
s: &'c mut State<'a, 'b>,
f: impl for<'d> FnOnce(&mut State<'d, 'b>),
) {
f(s)
}
fn run_for_var_effects_only<'a, 'b, 'c>(
s: &'c mut State<'a, 'b>,
f: impl for<'d> FnOnce(&mut State<'d, 'b>),
) {
s.for_var_effects_only(f)
}
let run = if s.cur_trait().items.is_empty() {
run_normally
} else {
run_for_var_effects_only
};
run(&mut s, &mut |s: &mut State<'_, 'b>| {
let mut needs_vars = BTreeSet::new();
let mut sv = s.with_needs_vars(&mut needs_vars);
let exports = it
.exports
.iter()
.map(|ed| emit_extern_decl(true, &mut sv, ed))
.collect::<Vec<_>>();
let mut stvs = BTreeMap::new();
let _ = sv.cur_trait(); let t = sv.cur_trait_immut();
for ti in t.supertraits.keys() {
let t = sv.resolve_trait_immut(false, ti);
stvs.insert(ti.clone(), t.tv_idxs());
}
sv.origin.push(ImportExport::Export("self"));
let mut stis = BTreeMap::new();
for (id, tvs) in stvs.into_iter() {
stis.insert(id, emit_tvis(&mut sv, EmitPositivity::Same, tvs));
}
for (id, ts) in stis.into_iter() {
sv.cur_trait().supertraits.get_mut(&id).unwrap().extend(ts);
}
drop(sv);
tracing::debug!("after exports, ncur_needs_vars is {:?}", needs_vars);
for v in needs_vars {
let id = s.noff_var_id(v);
s.cur_trait().tvs.insert(id, (Some(v), TokenStream::new()));
}
s.cur_trait().items.extend(quote! { #(#exports)* });
});
}
fn emit_component<'a, 'b, 'c>(s: &'c mut State<'a, 'b>, wn: WitName, ct: &'c Component<'b>) {
let mut s = s.with_cursor(wn.namespace_idents());
let base_name = kebab_to_type(wn.name);
s.cur_helper_mod = Some(kebab_to_namespace(wn.name));
let import_name = kebab_to_imports_name(wn.name);
*s.bound_vars = ct
.uvars
.iter()
.rev()
.map(Clone::clone)
.collect::<VecDeque<_>>();
s.cur_trait = Some(import_name.clone());
s.colliding_import_names = find_colliding_import_names(&ct.imports);
let imports = ct
.imports
.iter()
.map(|ed| emit_extern_decl(false, &mut s, ed))
.collect::<Vec<TokenStream>>();
s.cur_trait().items.extend(quote! { #(#imports)* });
s.adjust_vars(ct.instance.evars.len() as u32);
s.import_param_var = Some(format_ident!("I"));
let export_name = kebab_to_exports_name(wn.name);
*s.bound_vars = ct
.instance
.evars
.iter()
.rev()
.chain(ct.uvars.iter().rev())
.map(Clone::clone)
.collect::<VecDeque<_>>();
s.cur_trait = Some(export_name.clone());
let exports = ct
.instance
.unqualified
.exports
.iter()
.map(|ed| emit_extern_decl(true, &mut s, ed))
.collect::<Vec<_>>();
s.cur_trait().tvs.insert(
format_ident!("I"),
(
None,
quote! { #import_name<P::NegativeOfThis> + ::core::marker::Send },
),
);
s.cur_trait().items.extend(quote! { #(#exports)* });
s.cur_helper_mod = None;
s.cur_trait = None;
s.cur_mod().items.extend(quote! {
pub trait #base_name<P: ::hyperlight_common::component::Positivity> {
type Exports<I: #import_name<P::NegativeOfThis> + ::core::marker::Send>: #export_name<P, I>;
fn instantiate<I: #import_name<P::NegativeOfThis> + ::core::marker::Send + 'static>(self, imports: I) -> <P as ::hyperlight_common::component::Positivity>::CallResult<Self::Exports<I>>;
}
});
}
pub fn emit_toplevel<'a, 'b, 'c>(s: &'c mut State<'a, 'b>, n: &str, ct: &'c Component<'b>) {
let wn = split_wit_name(n);
emit_component(s, wn, ct);
}