Skip to content

Commit 3ac042c

Browse files
authored
fix: disambiguate colliding WIT import names (#1562)
* fix: disambiguate colliding WIT import names Apply collision-aware import member naming across host, guest, resource, and type-variable codegen. Keep bare imports stable, qualify namespaced collisions, preserve version suffixes, and avoid applying import collision disambiguation to exports. Signed-off-by: James Sturtevant <jsturtevant@gmail.com> * test: cover WIT import collision cases Extend the bindgen-test-cases fixture with colliding package, versioned, hyphenated, and bare import cases, plus export-name coverage. Signed-off-by: James Sturtevant <jsturtevant@gmail.com> --------- Signed-off-by: James Sturtevant <jsturtevant@gmail.com>
1 parent ea98d03 commit 3ac042c

13 files changed

Lines changed: 610 additions & 31 deletions

File tree

src/hyperlight_component_util/src/emit.rs

Lines changed: 376 additions & 4 deletions
Large diffs are not rendered by default.

src/hyperlight_component_util/src/guest.rs

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,9 @@ use proc_macro2::TokenStream;
1818
use quote::{format_ident, quote};
1919

2020
use crate::emit::{
21-
FnName, ResolvedBoundVar, ResourceItemName, State, WitName, kebab_to_exports_name, kebab_to_fn,
22-
kebab_to_getter, kebab_to_imports_name, kebab_to_namespace, kebab_to_type, kebab_to_var,
23-
split_wit_name,
21+
FnName, ResolvedBoundVar, ResourceItemName, State, WitName, find_colliding_import_names,
22+
import_member_names, kebab_to_exports_name, kebab_to_fn, kebab_to_getter,
23+
kebab_to_imports_name, kebab_to_namespace, kebab_to_type, kebab_to_var, split_wit_name,
2424
};
2525
use crate::etypes::{Component, Defined, ExternDecl, ExternDesc, Handleable, Instance, Tyvar};
2626
use crate::hl::{
@@ -117,8 +117,7 @@ fn emit_import_extern_decl<'a, 'b, 'c>(
117117
let wn = split_wit_name(ed.kebab_name);
118118
emit_import_instance(s, wn.clone(), it);
119119

120-
let getter = kebab_to_getter(wn.name);
121-
let tn = kebab_to_type(wn.name);
120+
let (tn, getter) = import_member_names(&wn, &s.colliding_import_names);
122121
quote! {
123122
type #tn = Self;
124123
#[allow(refining_impl_trait)]
@@ -287,6 +286,7 @@ fn emit_component<'a, 'b, 'c>(
287286
let export_trait = kebab_to_exports_name(wn.name);
288287
s.import_param_var = Some(format_ident!("I"));
289288
s.self_param_var = Some(format_ident!("S"));
289+
s.colliding_import_names = find_colliding_import_names(&ct.imports);
290290

291291
let rtsid = format_ident!("{}Resources", r#trait);
292292
resource::emit_tables(

src/hyperlight_component_util/src/host.rs

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,9 @@ use proc_macro2::{Ident, TokenStream};
1818
use quote::{format_ident, quote};
1919

2020
use crate::emit::{
21-
FnName, ResourceItemName, State, WitName, kebab_to_exports_name, kebab_to_fn, kebab_to_getter,
22-
kebab_to_imports_name, kebab_to_namespace, kebab_to_type, kebab_to_var, split_wit_name,
21+
FnName, ResourceItemName, State, WitName, find_colliding_import_names, import_member_names,
22+
kebab_to_exports_name, kebab_to_fn, kebab_to_getter, kebab_to_imports_name, kebab_to_namespace,
23+
kebab_to_type, kebab_to_var, split_wit_name,
2324
};
2425
use crate::etypes::{Component, ExternDecl, ExternDesc, Instance, Tyvar};
2526
use crate::hl::{
@@ -264,10 +265,9 @@ fn emit_import_extern_decl<'a, 'b, 'c>(
264265
ExternDesc::Instance(it) => {
265266
let mut s = s.clone();
266267
let wn = split_wit_name(ed.kebab_name);
267-
let type_name = kebab_to_type(wn.name);
268-
let getter = kebab_to_getter(wn.name);
268+
let (type_name, getter) = import_member_names(&wn, &s.colliding_import_names);
269269
let tp = s.cur_trait_path();
270-
let get_self = get_self.with_getter(tp, type_name, getter); //quote! { #get_self let mut slf = &mut #tp::#getter(&mut *slf); };
270+
let get_self = get_self.with_getter(tp, type_name, getter);
271271
emit_import_instance(&mut s, get_self, wn.clone(), it)
272272
}
273273
ExternDesc::Component(_) => {
@@ -326,6 +326,7 @@ fn emit_component<'a, 'b, 'c>(s: &'c mut State<'a, 'b>, wn: WitName, ct: &'c Com
326326

327327
let rtsid = format_ident!("{}Resources", r#trait);
328328
s.import_param_var = Some(format_ident!("I"));
329+
s.colliding_import_names = find_colliding_import_names(&ct.imports);
329330
resource::emit_tables(
330331
&mut s,
331332
rtsid.clone(),

src/hyperlight_component_util/src/rtypes.rs

Lines changed: 35 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -24,9 +24,9 @@ use quote::{format_ident, quote};
2424
use syn::Ident;
2525

2626
use crate::emit::{
27-
FnName, ResourceItemName, State, WitName, kebab_to_cons, kebab_to_exports_name,
28-
kebab_to_flags_const, kebab_to_fn, kebab_to_getter, kebab_to_imports_name, kebab_to_namespace,
29-
kebab_to_type, kebab_to_var, split_wit_name,
27+
FnName, ResourceItemName, State, WitName, find_colliding_import_names, import_member_names,
28+
kebab_to_cons, kebab_to_exports_name, kebab_to_flags_const, kebab_to_fn, kebab_to_getter,
29+
kebab_to_imports_name, kebab_to_namespace, kebab_to_type, kebab_to_var, split_wit_name,
3030
};
3131
use crate::etypes::{
3232
self, Component, Defined, ExternDecl, ExternDesc, Func, Handleable, ImportExport, Instance,
@@ -68,9 +68,10 @@ fn emit_resource_ref(s: &mut State, n: u32, path: Vec<ImportExport>) -> TokenStr
6868
let id = format_ident!("HostResource{}", n);
6969
return quote! { #id };
7070
}
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());
71+
let Some(resource) = path.last() else {
72+
panic!("resource reference path must contain the resource type");
73+
};
74+
let rtrait = kebab_to_type(resource.name());
7475

7576
// Deal specially with being in the local instance, where there is
7677
// no instance type & so it is not easy to resolve the
@@ -92,22 +93,34 @@ fn emit_resource_ref(s: &mut State, n: u32, path: Vec<ImportExport>) -> TokenStr
9293
// followed by the resource type itself. We locate the resource
9394
// trait by using that final instance name directly; any other
9495
// 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);
96+
let instance = &path[path.len() - 2];
97+
let iwn = split_wit_name(instance.name());
9798
let extras = path[0..path.len() - 2]
9899
.iter()
99100
.map(|p| {
100101
let wn = split_wit_name(p.name());
101-
kebab_to_type(wn.name)
102+
if p.imported() && s.colliding_import_names.contains(wn.name) {
103+
let (tn, _) = import_member_names(&wn, &s.colliding_import_names);
104+
tn
105+
} else {
106+
kebab_to_type(wn.name)
107+
}
102108
})
103109
.collect::<Vec<_>>();
104110
let extras = quote! { #(#extras::)* };
105111
let rp = s.root_path();
106112
let tns = iwn.namespace_path();
107113
let instance_mod = kebab_to_namespace(iwn.name);
108-
let instance_type = kebab_to_type(iwn.name);
114+
// Use the disambiguated trait member only for imported instances. Exported
115+
// instances must keep their public WIT member names.
116+
let instance_type = if instance.imported() {
117+
let (tn, _) = import_member_names(&iwn, &s.colliding_import_names);
118+
tn
119+
} else {
120+
kebab_to_type(iwn.name)
121+
};
109122
let mut sv = quote! { Self };
110-
if path[path.len() - 2].imported() {
123+
if instance.imported() {
111124
if let Some(iv) = &s.import_param_var {
112125
sv = quote! { #iv }
113126
};
@@ -760,18 +773,22 @@ fn emit_extern_decl<'a, 'b, 'c>(
760773
TokenStream::new()
761774
};
762775

763-
let getter = kebab_to_getter(wn.name);
776+
let (member_tn, member_getter) = if origin_was_export {
777+
(kebab_to_type(wn.name), kebab_to_getter(wn.name))
778+
} else {
779+
import_member_names(&wn, &s.colliding_import_names)
780+
};
764781
let rp = s.root_path();
765782
let tns = wn.namespace_path();
766-
let tn = kebab_to_type(wn.name);
783+
let trait_tn = kebab_to_type(wn.name);
767784
let trait_bound = if tns.is_empty() {
768-
quote! { #rp #tn }
785+
quote! { #rp #trait_tn }
769786
} else {
770-
quote! { #rp #tns::#tn }
787+
quote! { #rp #tns::#trait_tn }
771788
};
772789
quote! {
773-
type #tn: #trait_bound #vs;
774-
fn #getter(&mut self) -> impl ::core::borrow::BorrowMut<Self::#tn>;
790+
type #member_tn: #trait_bound #vs;
791+
fn #member_getter(&mut self) -> impl ::core::borrow::BorrowMut<Self::#member_tn>;
775792
}
776793
}
777794
ExternDesc::Component(_) => {
@@ -864,6 +881,7 @@ fn emit_component<'a, 'b, 'c>(s: &'c mut State<'a, 'b>, wn: WitName, ct: &'c Com
864881
.map(Clone::clone)
865882
.collect::<VecDeque<_>>();
866883
s.cur_trait = Some(import_name.clone());
884+
s.colliding_import_names = find_colliding_import_names(&ct.imports);
867885
let imports = ct
868886
.imports
869887
.iter()

src/hyperlight_host/tests/wit_test.rs

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -487,4 +487,53 @@ mod bindgen_test_cases {
487487
};
488488
assert_eq!(result.message, "executed");
489489
}
490+
491+
#[allow(dead_code)]
492+
struct ExportHost;
493+
494+
impl test::bindgen_test_cases::Executor for ExportHost {
495+
fn execute(&mut self) -> test::bindgen_test_cases::executor::ExecutionResult {
496+
test::bindgen_test_cases::executor::ExecutionResult {
497+
message: String::from("executed"),
498+
}
499+
}
500+
}
501+
502+
impl test::bindgen_test_cases::Types for ExportHost {
503+
fn get_status(&mut self) -> test::bindgen_test_cases::types::Status {
504+
test::bindgen_test_cases::types::Status {
505+
message: String::from("ok"),
506+
}
507+
}
508+
}
509+
510+
impl test::bindgen_test_cases::UsesExportedTypes<test::bindgen_test_cases::types::Status>
511+
for ExportHost
512+
{
513+
fn get_status(&mut self) -> test::bindgen_test_cases::types::Status {
514+
test::bindgen_test_cases::types::Status {
515+
message: String::from("ok"),
516+
}
517+
}
518+
}
519+
520+
#[allow(refining_impl_trait)]
521+
impl<I: test::bindgen_test_cases::BindgenTestCasesImports + Send>
522+
test::bindgen_test_cases::BindgenTestCasesExports<I> for ExportHost
523+
{
524+
type Executor = Self;
525+
fn executor(&mut self) -> &mut Self {
526+
self
527+
}
528+
529+
type Types = Self;
530+
fn types(&mut self) -> &mut Self {
531+
self
532+
}
533+
534+
type UsesExportedTypes = Self;
535+
fn uses_exported_types(&mut self) -> &mut Self {
536+
self
537+
}
538+
}
490539
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package a:bc;
2+
3+
interface types {
4+
resource thing;
5+
6+
record plain-bc-info {
7+
label: string,
8+
}
9+
get-plain-bc-info: func() -> plain-bc-info;
10+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package a:b-c;
2+
3+
interface types {
4+
resource thing;
5+
6+
record bc-info {
7+
label: string,
8+
}
9+
get-bc-info: func() -> bc-info;
10+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package a:pkg;
2+
3+
interface types {
4+
resource thing;
5+
6+
record info {
7+
name: string,
8+
value: u32,
9+
}
10+
get-info: func() -> info;
11+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package a-b:c;
2+
3+
interface types {
4+
resource thing;
5+
6+
record ab-c-info {
7+
tag: string,
8+
}
9+
get-ab-c-info: func() -> ab-c-info;
10+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package b:pkg;
2+
3+
interface types {
4+
resource thing;
5+
6+
record detail {
7+
label: string,
8+
count: u64,
9+
}
10+
get-detail: func() -> detail;
11+
}

0 commit comments

Comments
 (0)