1use std::collections::{BTreeMap, BTreeSet, VecDeque};
7use std::vec::Vec;
8
9use proc_macro2::TokenStream;
10use quote::{format_ident, quote};
11use syn::Ident;
12
13use crate::emit::{
14 FnName, ResourceItemName, State, WitName, find_colliding_import_names, import_member_names,
15 kebab_to_cons, kebab_to_exports_name, kebab_to_flags_const, kebab_to_fn, kebab_to_getter,
16 kebab_to_imports_name, kebab_to_namespace, kebab_to_type, kebab_to_var, split_wit_name,
17};
18use crate::etypes::{
19 self, Component, Defined, ExternDecl, ExternDesc, Func, Handleable, ImportExport, Instance,
20 Param, TypeBound, Tyvar, Value,
21};
22
23#[derive(Clone, Copy, Debug)]
24pub(crate) enum EmitPositivity {
25 Same,
26 Opposite,
27}
28impl EmitPositivity {
29 fn invert(self) -> Self {
30 match self {
31 EmitPositivity::Same => EmitPositivity::Opposite,
32 EmitPositivity::Opposite => EmitPositivity::Same,
33 }
34 }
35}
36
37fn emit_tvis(s: &mut State, ep: EmitPositivity, tvs: Vec<u32>) -> TokenStream {
41 let tvs = tvs
42 .iter()
43 .map(|tv| emit_var_ref_noff(s, *tv, false))
44 .collect::<Vec<_>>();
45 let p = s.positivity_param.clone().unwrap_or(quote! { P });
46 match ep {
47 EmitPositivity::Same => quote! { <#p #(,#tvs)*> },
48 EmitPositivity::Opposite => {
49 quote! { <<#p as ::hyperlight_common::component::Positivity>::NegativeOfThis #(,#tvs)*> }
50 }
51 }
52}
53
54pub(crate) fn trait_ref(
56 s: &mut State,
57 ep: EmitPositivity,
58 absolute: bool,
59 path: &[Ident],
60) -> TokenStream {
61 let rp = if absolute {
62 s.root_path()
63 } else {
64 TokenStream::new()
65 };
66 let t = s.resolve_trait_immut(absolute, path);
67 let tvis = emit_tvis(s, ep, t.tv_idxs());
68 quote! { #rp #(#path)::* #tvis }
69}
70
71fn emit_resource_ref(s: &mut State, n: u32, path: Vec<ImportExport>) -> TokenStream {
79 if s.is_guest && s.is_impl {
83 let id = format_ident!("HostResource{}", n);
89 return quote! { #id };
90 }
91 let Some(resource) = path.last() else {
92 panic!("resource reference path must contain the resource type");
93 };
94 let rtrait = kebab_to_type(resource.name());
95
96 if path.len() == 1 {
100 let helper = s.cur_helper_mod.clone().unwrap();
101 let rtrait = kebab_to_type(path[0].name());
102 let trait_ref = trait_ref(
103 s,
104 EmitPositivity::Same,
105 false,
106 &[helper.clone(), rtrait.clone()],
107 );
108 let mut sv = quote! { Self };
109 if let Some(s) = &s.self_param_var {
110 sv = quote! { #s };
111 };
112 return quote! { <#sv as #trait_ref>::T };
113 };
114
115 let mut toks = quote! { Self };
120 if path[0].imported() {
121 if let Some(iv) = &s.import_param_var {
122 toks = quote! { #iv }
123 }
124 } else if let Some(s) = &s.self_param_var {
125 toks = quote! { #s }
126 }
127 let mut ep = EmitPositivity::Same;
131 for (i, p) in path[0..path.len() - 1].iter().enumerate() {
132 if p.imported() && (i != 0 || s.import_param_var.is_some()) {
137 ep = ep.invert();
138 }
139 let iwn = split_wit_name(p.name());
140 let export_name = if p.imported() {
141 import_member_names(&iwn, &s.colliding_import_names).0
142 } else {
143 kebab_to_type(iwn.name)
144 };
145
146 let namespace_suffix = if i == path.len() - 2 {
151 &[kebab_to_namespace(iwn.name), rtrait.clone()] as &[Ident]
152 } else {
153 &[kebab_to_type(iwn.name)] as &[Ident]
154 };
155 let trait_path = iwn
156 .namespace_idents()
157 .iter()
158 .chain(namespace_suffix.iter())
159 .cloned()
160 .collect::<Vec<_>>();
161 let trait_ref = trait_ref(s, ep, true, &trait_path);
162
163 toks = quote! { <#toks::#export_name as #trait_ref> };
164 }
165 quote! { #toks::T }
166}
167
168fn try_find_local_var_id(
175 s: &mut State,
176 n: u32,
178) -> Option<TokenStream> {
179 if let Some((path, bound)) = s.is_noff_var_local(n) {
180 let var_is_helper = match bound {
181 TypeBound::Eq(_) => true,
182 TypeBound::SubResource => false,
183 };
184 if !var_is_helper {
185 if s.is_helper {
187 if path.len() == 1 && s.cur_trait == Some(kebab_to_type(path[0].name())) {
189 return Some(quote! { Self::T });
190 }
191 return None;
193 } else {
194 let mut path_strs = vec!["".to_string(); path.len()];
195 for (i, p) in path.iter().enumerate() {
196 path_strs[i] = p.name().to_string();
197 }
198 let path = path
199 .into_iter()
200 .enumerate()
201 .map(|(i, p)| match p {
202 ImportExport::Import(_) => ImportExport::Import(&path_strs[i]),
203 ImportExport::Export(_) => ImportExport::Export(&path_strs[i]),
204 })
205 .collect::<Vec<_>>();
206 return Some(emit_resource_ref(s, n, path));
207 }
208 }
209 tracing::debug!("path is {:?}\n", path);
210 let mut path = path.iter().rev();
211 let name = kebab_to_type(path.next().unwrap().name());
212 let owner = path.next();
213 if let Some(owner) = owner {
214 let wn = split_wit_name(owner.name());
216 let rp = s.root_path();
217 let tns = wn.namespace_path();
218 let helper = kebab_to_namespace(wn.name);
219 if tns.is_empty() {
220 Some(quote! { #rp #helper::#name })
221 } else {
222 Some(quote! { #rp #tns::#helper::#name })
223 }
224 } else {
225 let hp = s.helper_path();
226 Some(quote! { #hp #name })
227 }
228 } else {
229 None
230 }
231}
232
233pub fn emit_var_ref(s: &mut State, tv: &Tyvar) -> TokenStream {
240 let Tyvar::Bound(n) = tv else {
241 panic!("free tyvar in rust emit")
242 };
243 emit_var_ref_noff(s, n + s.var_offset as u32, false)
244}
245pub fn emit_var_ref_value(s: &mut State, tv: &Tyvar) -> TokenStream {
253 let Tyvar::Bound(n) = tv else {
254 panic!("free tyvar in rust emit")
255 };
256 emit_var_ref_noff(s, n + s.var_offset as u32, true)
257}
258pub fn emit_var_ref_noff(s: &mut State, n: u32, is_value: bool) -> TokenStream {
265 tracing::debug!("var_ref {:?} {:?}", &s.bound_vars[n as usize], s.origin);
266 let id = try_find_local_var_id(s, n);
268 let id = match id {
269 Some(id) => {
270 let vs = s.get_noff_var_refs(n);
273 let vs = vs
274 .iter()
275 .map(|n| emit_var_ref_noff(s, *n, false))
276 .collect::<Vec<_>>();
277 let vs_toks = if !vs.is_empty() {
278 if is_value {
279 quote! { ::<#(#vs),*> }
280 } else {
281 quote! { <#(#vs),*> }
282 }
283 } else {
284 TokenStream::new()
285 };
286
287 quote! { #id #vs_toks }
288 }
289 None => {
290 s.need_noff_var(n);
293 let id = s.noff_var_id(n);
294 quote! { #id }
295 }
296 };
297 quote! { #id }
298}
299
300pub fn numeric_rtype(vt: &Value) -> (Ident, u8) {
305 match vt {
306 Value::S(w) => (format_ident!("i{}", w.width()), w.width()),
307 Value::U(w) => (format_ident!("u{}", w.width()), w.width()),
308 Value::F(w) => (format_ident!("f{}", w.width()), w.width()),
309 _ => panic!("numeric_rtype: internal invariant violation"),
310 }
311}
312
313pub fn emit_value(s: &mut State, vt: &Value) -> TokenStream {
318 match vt {
319 Value::Bool => quote! { bool },
320 Value::S(_) | Value::U(_) | Value::F(_) => {
321 let (id, _) = numeric_rtype(vt);
322 quote! { #id }
323 }
324 Value::Char => quote! { char },
325 Value::String => quote! { alloc::string::String },
326 Value::List(vt) => {
327 let vt = emit_value(s, vt);
328 quote! { alloc::vec::Vec<#vt> }
329 }
330 Value::FixList(vt, size) => {
331 let vt = emit_value(s, vt);
332 let size = *size as usize;
333 quote! { [#vt; #size] }
334 }
335 Value::Record(_) => panic!("record not at top level of valtype"),
336 Value::Tuple(vts) => {
337 let vts = vts.iter().map(|vt| emit_value(s, vt)).collect::<Vec<_>>();
338 quote! { (#(#vts),*) }
339 }
340 Value::Flags(_) => panic!("flags not at top level of valtype"),
341 Value::Variant(_) => panic!("flags not at top level of valtype"),
342 Value::Enum(_) => panic!("enum not at top level of valtype"),
343 Value::Option(vt) => {
344 let vt = emit_value(s, vt);
345 quote! { ::core::option::Option<#vt> }
346 }
347 Value::Result(vt1, vt2) => {
348 let unit = Value::Tuple(Vec::new());
349 let vt1 = emit_value(s, vt1.as_ref().as_ref().unwrap_or(&unit));
350 let vt2 = emit_value(s, vt2.as_ref().as_ref().unwrap_or(&unit));
351 quote! { ::core::result::Result<#vt1, #vt2> }
352 }
353 Value::Own(ht) => match ht {
354 Handleable::Resource(_) => panic!("bare resource in type"),
355 Handleable::Var(tv) => {
356 if s.is_guest {
357 let wrap = if s.is_wasmtime_guest {
358 |toks| quote! { ::wasmtime::component::Resource<#toks> }
359 } else {
360 |toks| toks
361 };
362 if !s.is_impl {
363 wrap(emit_var_ref(s, tv))
364 } else {
365 let n = crate::hl::resolve_handleable_to_resource(s, ht);
366 tracing::debug!("resolved ht to r (4) {:?} {:?}", ht, n);
367 let id = format_ident!("HostResource{}", n);
368 wrap(quote! { #id })
369 }
370 } else {
371 emit_var_ref(s, tv)
372 }
373 }
374 },
375 Value::Borrow(ht) => match ht {
376 Handleable::Resource(_) => panic!("bare resource in type"),
377 Handleable::Var(tv) => {
378 if s.is_guest {
379 let wrap = if s.is_wasmtime_guest {
380 |toks| quote! { ::wasmtime::component::Resource<#toks> }
381 } else {
382 |toks| quote! { &#toks }
383 };
384 if !s.is_impl {
385 wrap(emit_var_ref(s, tv))
386 } else {
387 let n = crate::hl::resolve_handleable_to_resource(s, ht);
388 tracing::debug!("resolved ht to r (5) {:?} {:?}", ht, n);
389 let id = format_ident!("HostResource{}", n);
390 wrap(quote! { #id })
391 }
392 } else {
393 let vr = emit_var_ref(s, tv);
394 let p = s.positivity_param.clone().unwrap_or(quote! { P });
395 quote! { <#p as ::hyperlight_common::component::Positivity>::Borrow<'_, #vr> }
396 }
397 }
398 },
399 Value::Var(Some(tv), _) => emit_var_ref(s, tv),
400 Value::Var(None, _) => panic!("value type with recorded but unknown var"),
401 }
402}
403
404fn emit_value_toplevel(s: &mut State, v: Option<u32>, id: Ident, vt: &Value) -> TokenStream {
408 let is_wasmtime_guest = s.is_wasmtime_guest;
409 match vt {
410 Value::Record(rfs) => {
411 let (vs, toks) = gather_needed_vars(s, v, |s| {
412 let rfs = rfs
413 .iter()
414 .map(|rf| {
415 let orig_name = rf.name.name;
416 let id = kebab_to_var(orig_name);
417 let derives = if s.is_wasmtime_guest {
418 quote! { #[component(name = #orig_name)] }
419 } else {
420 TokenStream::new()
421 };
422 let ty = emit_value(s, &rf.ty);
423 quote! { #derives pub #id: #ty }
424 })
425 .collect::<Vec<_>>();
426 quote! { #(#rfs),* }
427 });
428 let vs = emit_type_defn_var_list(s, vs);
429 let derives = if s.is_wasmtime_guest {
430 quote! {
431 #[derive(::wasmtime::component::ComponentType)]
432 #[derive(::wasmtime::component::Lift)]
433 #[derive(::wasmtime::component::Lower)]
434 #[component(record)]
435 }
436 } else {
437 TokenStream::new()
438 };
439 quote! {
440 #derives
441 #[derive(Debug)]
442 pub struct #id #vs { #toks }
443 }
444 }
445 Value::Flags(ns) => {
446 if s.is_wasmtime_guest {
447 let flags = ns
448 .iter()
449 .map(|n| {
450 let orig_name = n.name;
451 let const_name = kebab_to_flags_const(orig_name);
452 quote! {
453 #[component(name = #orig_name)]
454 const #const_name;
455 }
456 })
457 .collect::<Vec<_>>();
458 quote! {
459 ::wasmtime::component::flags! {
460 #id {
461 #(#flags)*
462 }
463 }
464 }
465 } else {
466 let (vs, toks) = gather_needed_vars(s, v, |_| {
467 let ns = ns
468 .iter()
469 .map(|n| {
470 let id = kebab_to_var(n.name);
471 quote! { pub #id: bool }
472 })
473 .collect::<Vec<_>>();
474 quote! { #(#ns),* }
475 });
476 let vs = emit_type_defn_var_list(s, vs);
477 quote! {
478 #[derive(Debug, Clone, PartialEq)]
479 pub struct #id #vs { #toks }
480 }
481 }
482 }
483 Value::Variant(vcs) => {
484 let (vs, toks) = gather_needed_vars(s, v, |s| {
485 let vcs = vcs
486 .iter()
487 .map(|vc| {
488 let orig_name = vc.name.name;
489 let id = kebab_to_cons(orig_name);
490 let derives = if s.is_wasmtime_guest {
491 quote! { #[component(name = #orig_name)] }
492 } else {
493 TokenStream::new()
494 };
495 match &vc.ty {
496 Some(ty) => {
497 let ty = emit_value(s, ty);
498 quote! { #derives #id(#ty) }
499 }
500 None => quote! { #derives #id },
501 }
502 })
503 .collect::<Vec<_>>();
504 quote! { #(#vcs),* }
505 });
506 let vs = emit_type_defn_var_list(s, vs);
507 let derives = if s.is_wasmtime_guest {
508 quote! {
509 #[derive(::wasmtime::component::ComponentType)]
510 #[derive(::wasmtime::component::Lift)]
511 #[derive(::wasmtime::component::Lower)]
512 #[component(variant)]
513 }
514 } else {
515 TokenStream::new()
516 };
517 quote! {
518 #derives
519 #[derive(Debug)]
520 pub enum #id #vs { #toks }
521 }
522 }
523 Value::Enum(ns) => {
524 let (vs, toks) = gather_needed_vars(s, v, |_| {
525 let ns = ns
526 .iter()
527 .map(|n| {
528 let orig_name = n.name;
529 let id = kebab_to_cons(orig_name);
530 let derives = if is_wasmtime_guest {
531 quote! { #[component(name = #orig_name)] }
532 } else {
533 TokenStream::new()
534 };
535 quote! { #derives #id }
536 })
537 .collect::<Vec<_>>();
538 quote! { #(#ns),* }
539 });
540 let vs = emit_type_defn_var_list(s, vs);
541 let derives = if s.is_wasmtime_guest {
542 quote! {
543 #[derive(::wasmtime::component::ComponentType)]
544 #[derive(::wasmtime::component::Lift)]
545 #[derive(::wasmtime::component::Lower)]
546 #[component(enum)]
547 #[repr(u8)] }
549 } else {
550 TokenStream::new()
551 };
552 quote! {
553 #derives
554 #[derive(Debug, Copy, Clone, PartialEq)]
555 pub enum #id #vs { #toks }
556 }
557 }
558 _ => emit_type_alias(s, v, id, |s| emit_value(s, vt)),
559 }
560}
561
562fn emit_defined(s: &mut State, v: Option<u32>, id: Ident, dt: &Defined) -> TokenStream {
569 match dt {
570 Defined::Instance(_) | Defined::Component(_) => TokenStream::new(),
573 Defined::Handleable(Handleable::Resource(_)) => panic!("bare resource in type"),
575 Defined::Handleable(Handleable::Var(tv)) => {
576 emit_type_alias(s, v, id, |s| emit_var_ref(s, tv))
577 }
578 Defined::Value(vt) => emit_value_toplevel(s, v, id, vt),
579 Defined::Func(ft) => emit_type_alias(s, v, id, |s| emit_func(s, ft)),
580 }
581}
582
583pub fn emit_func_param(s: &mut State, p: &Param) -> TokenStream {
587 let name = kebab_to_var(p.name.name);
588 let ty = emit_value(s, &p.ty);
589 quote! { #name: #ty }
590}
591
592pub fn emit_func_result(s: &mut State, r: &etypes::Result<'_>) -> TokenStream {
597 let inner = match r {
598 Some(vt) => emit_value(s, vt),
599 None => quote! { () },
600 };
601 let p = s.positivity_param.clone().unwrap_or(quote! { P });
602 quote! { <#p as ::hyperlight_common::component::Positivity>::CallResult<#inner> }
603}
604
605fn emit_func(s: &mut State, ft: &Func) -> TokenStream {
610 let params = ft
611 .params
612 .iter()
613 .map(|p| emit_func_param(s, p))
614 .collect::<Vec<_>>();
615 let result = emit_func_result(s, &ft.result);
616 quote! { fn(#(#params),*) -> #result }
617}
618
619fn gather_needed_vars<F: Fn(&mut State) -> TokenStream>(
623 s: &mut State,
624 v: Option<u32>,
625 f: F,
626) -> (BTreeSet<u32>, TokenStream) {
627 let mut needs_vars = BTreeSet::new();
628 let mut sv = s.with_needs_vars(&mut needs_vars);
629 let toks = f(&mut sv);
630 if let Some(vn) = v {
631 sv.record_needs_vars(vn);
632 }
633 drop(sv);
634 (needs_vars, toks)
635}
636fn emit_type_defn_var_list(s: &mut State, vs: BTreeSet<u32>) -> TokenStream {
640 if vs.is_empty() {
641 TokenStream::new()
642 } else {
643 let vs = vs
644 .iter()
645 .map(|n| {
646 if s.is_guest {
647 let t = s.noff_var_id(*n);
648 quote! { #t: 'static }
649 } else {
650 let t = s.noff_var_id(*n);
651 quote! { #t }
652 }
653 })
654 .collect::<Vec<_>>();
655 quote! { <#(#vs),*> }
656 }
657}
658fn emit_type_alias<F: Fn(&mut State) -> TokenStream>(
667 s: &mut State,
668 v: Option<u32>,
669 id: Ident,
670 f: F,
671) -> TokenStream {
672 let (vs, toks) = gather_needed_vars(s, v, f);
673 let vs = emit_type_defn_var_list(s, vs);
674 quote! { pub type #id #vs = #toks; }
675}
676
677fn emit_extern_decl<'a, 'b, 'c>(
680 origin_was_export: bool,
681 s: &'c mut State<'a, 'b>,
682 ed: &'c ExternDecl<'b>,
683) -> TokenStream {
684 tracing::debug!(" emitting decl {:?}", ed.kebab_name);
685 match &ed.desc {
686 ExternDesc::CoreModule(_) => panic!("core module (im/ex)ports are not supported"),
687 ExternDesc::Func(ft) => {
688 let mut s = s.push_origin(origin_was_export, ed.kebab_name);
689 match kebab_to_fn(ed.kebab_name) {
690 FnName::Plain(n) => {
691 let params = ft
692 .params
693 .iter()
694 .map(|p| emit_func_param(&mut s, p))
695 .collect::<Vec<_>>();
696 let result = emit_func_result(&mut s, &ft.result);
697 quote! {
698 fn #n(&mut self, #(#params),*) -> #result;
699 }
700 }
701 FnName::Associated(r, n) => {
702 let mut s = s.helper();
703 s.cur_trait = Some(r.clone());
704 let mut needs_vars = BTreeSet::new();
705 let mut sv = s.with_needs_vars(&mut needs_vars);
706 let params = ft
707 .params
708 .iter()
709 .map(|p| emit_func_param(&mut sv, p))
710 .collect::<Vec<_>>();
711 match n {
712 ResourceItemName::Constructor => {
713 sv.cur_trait().items.extend(quote! {
714 fn new(&mut self, #(#params),*) -> Self::T;
715 });
716 }
717 ResourceItemName::Method(n) => {
718 let result = emit_func_result(&mut sv, &ft.result);
719 sv.cur_trait().items.extend(quote! {
720 fn #n(&mut self, #(#params),*) -> #result;
721 });
722 }
723 ResourceItemName::Static(n) => {
724 let result = emit_func_result(&mut sv, &ft.result);
725 sv.cur_trait().items.extend(quote! {
726 fn #n(&mut self, #(#params),*) -> #result;
727 });
728 }
729 }
730 for v in needs_vars {
731 let id = s.noff_var_id(v);
732 s.cur_trait().tvs.insert(id, (Some(v), TokenStream::new()));
733 }
734 quote! {}
735 }
736 }
737 }
738 ExternDesc::Type(t) => {
739 fn go_defined<'a, 'b, 'c>(
740 s: &'c mut State<'a, 'b>,
741 ed: &'c ExternDecl<'b>,
742 t: &'c Defined<'b>,
743 v: Option<u32>,
744 ) -> TokenStream {
745 let id = kebab_to_type(ed.kebab_name);
746 let mut s = s.helper();
747
748 let t = emit_defined(&mut s, v, id, t);
749 s.cur_mod().items.extend(t);
750 TokenStream::new()
751 }
752 let edn: &'b str = ed.kebab_name;
753 let mut s: State<'_, 'b> = s.push_origin(origin_was_export, edn);
754 if let Some((n, bound)) = s.is_var_defn(t) {
755 match bound {
756 TypeBound::Eq(t) => {
757 let noff = s.var_offset as u32 + n;
761 s.var_offset += n as usize + 1;
762 go_defined(&mut s, ed, &t, Some(noff))
763 }
764 TypeBound::SubResource => {
765 let rn = kebab_to_type(ed.kebab_name);
766 s.add_helper_supertrait(rn.clone());
767 let mut s = s.helper();
768 s.cur_trait = Some(rn.clone());
769 s.cur_trait().items.extend(quote! {
770 type T: ::core::marker::Send;
771 });
772 quote! {}
773 }
774 }
775 } else {
776 go_defined(&mut s, ed, t, None)
777 }
778 }
779 ExternDesc::Instance(it) => {
780 let mut s = s.push_origin(origin_was_export, ed.kebab_name);
781 let wn = split_wit_name(ed.kebab_name);
782 emit_instance(&mut s, wn.clone(), it);
783
784 let (member_tn, member_getter) = if origin_was_export {
785 (kebab_to_type(wn.name), kebab_to_getter(wn.name))
786 } else {
787 import_member_names(&wn, &s.colliding_import_names)
788 };
789 let trait_path = wn
790 .namespace_idents()
791 .iter()
792 .chain(&[kebab_to_type(wn.name)])
793 .cloned()
794 .collect::<Vec<_>>();
795 let trait_ref = trait_ref(&mut s, EmitPositivity::Same, true, &trait_path);
796 quote! {
797 type #member_tn: #trait_ref;
798 fn #member_getter(&mut self) -> impl ::core::borrow::BorrowMut<Self::#member_tn>;
799 }
800 }
801 ExternDesc::Component(_) => {
802 panic!("nested components not yet supported in rust bindings");
803 }
804 }
805}
806
807fn emit_instance<'a, 'b, 'c>(s: &'c mut State<'a, 'b>, wn: WitName, it: &'c Instance<'b>) {
810 tracing::debug!("emitting instance {:?}", wn);
811 let mut s = s.with_cursor(wn.namespace_idents());
812
813 let name = kebab_to_type(wn.name);
814
815 s.cur_helper_mod = Some(kebab_to_namespace(wn.name));
816 s.cur_trait = Some(name.clone());
817
818 fn run_normally<'a, 'b, 'c>(
836 s: &'c mut State<'a, 'b>,
837 f: impl for<'d> FnOnce(&mut State<'d, 'b>),
838 ) {
839 f(s)
840 }
841 fn run_for_var_effects_only<'a, 'b, 'c>(
842 s: &'c mut State<'a, 'b>,
843 f: impl for<'d> FnOnce(&mut State<'d, 'b>),
844 ) {
845 s.for_var_effects_only(f)
846 }
847 let run = if s.cur_trait().items.is_empty() {
848 run_normally
849 } else {
850 run_for_var_effects_only
851 };
852 run(&mut s, &mut |s: &mut State<'_, 'b>| {
853 let mut needs_vars = BTreeSet::new();
854 let mut sv = s.with_needs_vars(&mut needs_vars);
855
856 let exports = it
857 .exports
858 .iter()
859 .map(|ed| emit_extern_decl(true, &mut sv, ed))
860 .collect::<Vec<_>>();
861
862 let mut stvs = BTreeMap::new();
865 let _ = sv.cur_trait(); let t = sv.cur_trait_immut();
867 for (ti, _) in t.supertraits.iter() {
868 let t = sv.resolve_trait_immut(false, ti);
869 stvs.insert(ti.clone(), t.tv_idxs());
870 }
871 sv.origin.push(ImportExport::Export("self"));
874 let mut stis = BTreeMap::new();
875 for (id, tvs) in stvs.into_iter() {
876 stis.insert(id, emit_tvis(&mut sv, EmitPositivity::Same, tvs));
877 }
878 for (id, ts) in stis.into_iter() {
879 sv.cur_trait().supertraits.get_mut(&id).unwrap().extend(ts);
880 }
881
882 drop(sv);
883 tracing::debug!("after exports, ncur_needs_vars is {:?}", needs_vars);
884 for v in needs_vars {
885 let id = s.noff_var_id(v);
886 s.cur_trait().tvs.insert(id, (Some(v), TokenStream::new()));
887 }
888
889 s.cur_trait().items.extend(quote! { #(#exports)* });
890 });
891}
892
893fn emit_component<'a, 'b, 'c>(s: &'c mut State<'a, 'b>, wn: WitName, ct: &'c Component<'b>) {
899 let mut s = s.with_cursor(wn.namespace_idents());
900
901 let base_name = kebab_to_type(wn.name);
902
903 s.cur_helper_mod = Some(kebab_to_namespace(wn.name));
904
905 let import_name = kebab_to_imports_name(wn.name);
906 *s.bound_vars = ct
907 .uvars
908 .iter()
909 .rev()
910 .map(Clone::clone)
911 .collect::<VecDeque<_>>();
912 s.cur_trait = Some(import_name.clone());
913 s.colliding_import_names = find_colliding_import_names(&ct.imports);
914 let imports = ct
915 .imports
916 .iter()
917 .map(|ed| emit_extern_decl(false, &mut s, ed))
918 .collect::<Vec<TokenStream>>();
919 s.cur_trait().items.extend(quote! { #(#imports)* });
920
921 s.adjust_vars(ct.instance.evars.len() as u32);
922 s.import_param_var = Some(format_ident!("I"));
923
924 let export_name = kebab_to_exports_name(wn.name);
925 *s.bound_vars = ct
926 .instance
927 .evars
928 .iter()
929 .rev()
930 .chain(ct.uvars.iter().rev())
931 .map(Clone::clone)
932 .collect::<VecDeque<_>>();
933 s.cur_trait = Some(export_name.clone());
934 let exports = ct
935 .instance
936 .unqualified
937 .exports
938 .iter()
939 .map(|ed| emit_extern_decl(true, &mut s, ed))
940 .collect::<Vec<_>>();
941 s.cur_trait().tvs.insert(
942 format_ident!("I"),
943 (
944 None,
945 quote! { #import_name<P::NegativeOfThis> + ::core::marker::Send },
946 ),
947 );
948 s.cur_trait().items.extend(quote! { #(#exports)* });
949
950 s.cur_helper_mod = None;
951 s.cur_trait = None;
952
953 s.cur_mod().items.extend(quote! {
954 pub trait #base_name<P: ::hyperlight_common::component::Positivity> {
955 type Exports<I: #import_name<P::NegativeOfThis> + ::core::marker::Send>: #export_name<P, I>;
956 fn instantiate<I: #import_name<P::NegativeOfThis> + ::core::marker::Send + 'static>(self, imports: I) -> <P as ::hyperlight_common::component::Positivity>::CallResult<Self::Exports<I>>;
959 }
960 });
961}
962
963pub fn emit_toplevel<'a, 'b, 'c>(s: &'c mut State<'a, 'b>, n: &str, ct: &'c Component<'b>) {
965 let wn = split_wit_name(n);
966 emit_component(s, wn, ct);
967}