Skip to content

Commit e4f9833

Browse files
authored
Merge pull request #766 from wado-lang/claude/fix-cross-module-types-WE3ky
Fix cross-module type identity and unify WirName
2 parents 0dd3662 + db25299 commit e4f9833

File tree

747 files changed

+47357
-43838
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

747 files changed

+47357
-43838
lines changed

wado-compiler/src/codegen/emit.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2251,7 +2251,7 @@ impl<'a> WirEmitter<'a> {
22512251
for (i, func) in self.wir.functions.iter().enumerate() {
22522252
let wir_func_idx = crate::wir_build::DEFINED_FUNC_BASE + u32::try_from(i).unwrap();
22532253
if let Some(&wasm_idx) = self.func_index_map.get(&wir_func_idx) {
2254-
name_map.append(wasm_idx, &func.name.display);
2254+
name_map.append(wasm_idx, &func.name.fq);
22552255
}
22562256
}
22572257
names.functions(&name_map);

wado-compiler/src/monomorphize/call_rewrite.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -119,6 +119,7 @@ impl Monomorphizer {
119119
if let Some(inferred_args) = effective_type_args {
120120
let key = InstantiationKey {
121121
name: qualified_func_name,
122+
module_source: func.module_source.clone(),
122123
impl_type_args: vec![],
123124
method_type_args: inferred_args,
124125
method_info: original_method_info.clone(),
@@ -169,6 +170,7 @@ impl Monomorphizer {
169170
for generic_method_name in names_to_try {
170171
let key = InstantiationKey {
171172
name: generic_method_name.clone(),
173+
module_source: func.module_source.clone(),
172174
impl_type_args: monomorph.impl_type_args.clone(),
173175
method_type_args: monomorph.method_type_args.clone(),
174176
method_info: Some(info.clone()),
@@ -235,6 +237,7 @@ impl Monomorphizer {
235237
for (full_method_name, _tn) in &names_to_try {
236238
let key = InstantiationKey {
237239
name: full_method_name.clone(),
240+
module_source: method_func.module_source.clone(),
238241
impl_type_args: vec![],
239242
method_type_args: type_args.clone(),
240243
method_info: None,
@@ -287,6 +290,7 @@ impl Monomorphizer {
287290
for (generic_method_name, _tn) in &dg_names {
288291
let combined_key = InstantiationKey {
289292
name: generic_method_name.clone(),
293+
module_source: method_func.module_source.clone(),
290294
impl_type_args: impl_type_args.clone(),
291295
method_type_args: type_args.clone(),
292296
method_info: None,
@@ -351,6 +355,7 @@ impl Monomorphizer {
351355
Some(trait_name),
352356
&method_name,
353357
),
358+
module_source: method_func.module_source.clone(),
354359
impl_type_args: impl_type_args.clone(),
355360
method_type_args: vec![],
356361
method_info: None,
@@ -360,6 +365,7 @@ impl Monomorphizer {
360365
MethodName::format_local(&base_struct, Some(trait_name), &method_name);
361366
possible_keys.push(InstantiationKey {
362367
name: trait_method_name,
368+
module_source: method_func.module_source.clone(),
363369
impl_type_args: impl_type_args.clone(),
364370
method_type_args: vec![],
365371
method_info: None,
@@ -368,6 +374,7 @@ impl Monomorphizer {
368374
// Also try regular method format
369375
possible_keys.push(InstantiationKey {
370376
name: MethodName::format_local(&base_struct, None, &method_name),
377+
module_source: method_func.module_source.clone(),
371378
impl_type_args,
372379
method_type_args: vec![],
373380
method_info: None,
@@ -404,6 +411,7 @@ impl Monomorphizer {
404411
{
405412
let key = InstantiationKey {
406413
name: mono.generic_name.clone(),
414+
module_source: method_func.module_source.clone(),
407415
impl_type_args: mono.impl_type_args.clone(),
408416
method_type_args: mono.method_type_args.clone(),
409417
method_info: None,
@@ -462,6 +470,7 @@ impl Monomorphizer {
462470
{
463471
let key = InstantiationKey {
464472
name: generic_name.clone(),
473+
module_source: method_func.module_source.clone(),
465474
impl_type_args: impl_type_args.clone(),
466475
method_type_args: vec![],
467476
method_info: None,

wado-compiler/src/monomorphize/func_inst.rs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,7 @@ impl Monomorphizer {
190190
if !type_args.is_empty() && generic_functions.contains_key(&qualified_func_name) {
191191
let key = InstantiationKey {
192192
name: qualified_func_name.clone(),
193+
module_source: func.module_source.clone(),
193194
impl_type_args: vec![],
194195
method_type_args: type_args.clone(),
195196
method_info: func.method_info.clone(),
@@ -214,6 +215,7 @@ impl Monomorphizer {
214215
{
215216
let key = InstantiationKey {
216217
name: qualified_func_name.clone(),
218+
module_source: func.module_source.clone(),
217219
impl_type_args: vec![],
218220
method_type_args: inferred,
219221
method_info: func.method_info.clone(),
@@ -267,6 +269,7 @@ impl Monomorphizer {
267269
let method_info = generic_func.method_info.clone();
268270
let key = InstantiationKey {
269271
name: generic_method_name,
272+
module_source: func.module_source.clone(),
270273
impl_type_args: impl_type_args.clone(),
271274
method_type_args,
272275
method_info,
@@ -338,6 +341,7 @@ impl Monomorphizer {
338341
});
339342
let key = InstantiationKey {
340343
name: full_method_name.clone(),
344+
module_source: method_func.module_source.clone(),
341345
impl_type_args: vec![],
342346
method_type_args: type_args.clone(),
343347
method_info: Some(method_info),
@@ -409,6 +413,7 @@ impl Monomorphizer {
409413
);
410414
let key = InstantiationKey {
411415
name: generic_method_name.clone(),
416+
module_source: method_func.module_source.clone(),
412417
impl_type_args: impl_type_args.clone(),
413418
method_type_args: type_args.clone(),
414419
method_info: Some(method_info),
@@ -479,6 +484,7 @@ impl Monomorphizer {
479484
let method_info = generic_func.method_info.clone();
480485
let key = InstantiationKey {
481486
name: generic_method_name.clone(),
487+
module_source: method_func.module_source.clone(),
482488
impl_type_args: impl_type_args.clone(),
483489
method_type_args: method_type_args_for_key,
484490
method_info,
@@ -540,6 +546,7 @@ impl Monomorphizer {
540546
let method_info = generic_func.method_info.clone();
541547
let key = InstantiationKey {
542548
name: generic_method_name.clone(),
549+
module_source: method_func.module_source.clone(),
543550
impl_type_args: impl_type_args.clone(),
544551
method_type_args: method_type_args_for_key,
545552
method_info,
@@ -625,6 +632,7 @@ impl Monomorphizer {
625632
};
626633
let key = InstantiationKey {
627634
name: mono.generic_name.clone(),
635+
module_source: method_func.module_source.clone(),
628636
impl_type_args: impl_ta,
629637
method_type_args: method_ta,
630638
method_info,
@@ -677,6 +685,7 @@ impl Monomorphizer {
677685
drop(generic_func);
678686
let key = InstantiationKey {
679687
name: generic_name,
688+
module_source: method_func.module_source.clone(),
680689
impl_type_args,
681690
method_type_args: vec![],
682691
method_info,

wado-compiler/src/monomorphize/struct_inst.rs

Lines changed: 7 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -17,26 +17,9 @@ impl Monomorphizer {
1717
) -> Option<TirStruct> {
1818
let mangled_name = self.structs.instantiated.get(key)?.clone();
1919

20-
// Find the GenericInstance's module_source from the type table.
21-
// Use the generic's original module (where it was defined) for the struct type,
22-
// ensuring consistency across modules that share the same type table.
23-
let struct_module_source = type_table
24-
.iter_type_ids()
25-
.find_map(|id| {
26-
if let ResolvedType::GenericInstance {
27-
name,
28-
module_source,
29-
type_args,
30-
} = type_table.get(id)
31-
&& name == &key.name
32-
&& type_args == &key.impl_type_args
33-
{
34-
Some(module_source.clone())
35-
} else {
36-
None
37-
}
38-
})
39-
.unwrap_or_else(|| self.current_module_source.clone());
20+
// Use module_source from the InstantiationKey, which carries
21+
// the module where the generic struct was defined.
22+
let struct_module_source = key.module_source.clone();
4023

4124
// Register the concrete struct type in the type table BEFORE substituting field types.
4225
// This is critical for self-referential structs like:
@@ -53,9 +36,12 @@ impl Monomorphizer {
5336
// so that substitute_type can use it for self-references
5437
for id in type_table.iter_type_ids() {
5538
if let ResolvedType::GenericInstance {
56-
name, type_args, ..
39+
name,
40+
module_source,
41+
type_args,
5742
} = type_table.get(id)
5843
&& name == &key.name
44+
&& module_source == &key.module_source
5945
&& type_args == &key.impl_type_args
6046
{
6147
self.structs.type_substitutions.insert(id, concrete_type_id);

wado-compiler/src/monomorphize/substitute.rs

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,9 @@ impl Monomorphizer {
136136
) {
137137
for id in type_table.iter_type_ids() {
138138
if let ResolvedType::GenericInstance {
139-
name, type_args, ..
139+
name,
140+
module_source,
141+
type_args,
140142
} = type_table.get(id)
141143
{
142144
// Skip empty type_args (invalid generic instances)
@@ -164,6 +166,7 @@ impl Monomorphizer {
164166
if all_concrete {
165167
let key = crate::tir::InstantiationKey {
166168
name: name.clone(),
169+
module_source: module_source.clone(),
167170
impl_type_args: type_args.clone(),
168171
method_type_args: vec![],
169172
method_info: None, // Struct instantiation,

wado-compiler/src/name.rs

Lines changed: 3 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -881,13 +881,7 @@ impl StructName {
881881

882882
impl fmt::Display for StructName {
883883
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
884-
match &self.module_source {
885-
ModuleSource::EntryPoint { .. } => write!(f, "{}", self.name),
886-
ModuleSource::Core { name: module } => write!(f, "core/{}/{}", module, self.name),
887-
ModuleSource::Wasi { interface } => write!(f, "wasi/{}/{}", interface, self.name),
888-
ModuleSource::Local { path } => write!(f, "{}/{}", path, self.name),
889-
ModuleSource::Remote { url } => write!(f, "{}/{}", url, self.name),
890-
}
884+
write!(f, "{}/{}", self.module_source, self.name)
891885
}
892886
}
893887

@@ -1359,13 +1353,13 @@ mod tests {
13591353
#[test]
13601354
fn test_struct_name_from_strs() {
13611355
let struct_name = StructName::from_strs(&["core", "internal"], "SomeType");
1362-
assert_eq!(struct_name.to_string(), "core/internal/SomeType");
1356+
assert_eq!(struct_name.to_string(), "core:internal/SomeType");
13631357
}
13641358

13651359
#[test]
13661360
fn test_struct_name_empty_path() {
13671361
let struct_name = StructName::from_path_and_name(&[], "Point");
1368-
assert_eq!(struct_name.to_string(), "Point");
1362+
assert_eq!(struct_name.to_string(), "<entry>/Point");
13691363
}
13701364

13711365
#[test]

wado-compiler/src/resolver.rs

Lines changed: 12 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -265,23 +265,24 @@ impl<'a, H: CompilerHost> Resolver<'a, H> {
265265
/// Look up struct field info by (name, `module_source`).
266266
///
267267
/// This is the correct way to look up struct fields — it disambiguates
268-
/// structs with the same name from different modules. Falls back to the
269-
/// module-local flat map when the requested `module_source` has no entry in
270-
/// the global map (e.g. during early resolution before all modules are loaded).
268+
/// The flat `struct_fields` map is a visibility-scoped projection of `all_struct_fields`,
269+
/// containing only types visible to the current module (own definitions + explicit imports).
270+
///
271+
/// `all_struct_fields` covers ALL modules and is needed for cross-module lookups where
272+
/// the type wasn't explicitly imported (e.g., a struct returned by a function from another
273+
/// module).
271274
fn lookup_struct_fields(
272275
&self,
273276
name: &str,
274277
module_source: &ModuleSource,
275278
) -> Option<&StructFieldInfo> {
276-
self.all_struct_fields
277-
.get(module_source)
278-
.and_then(|m| m.get(name))
279+
self.struct_fields
280+
.get(name)
281+
.filter(|info| info.module_source == *module_source)
279282
.or_else(|| {
280-
// Fallback: if all_struct_fields is not yet populated (early init),
281-
// try the flat map but only if module_source matches.
282-
self.struct_fields
283-
.get(name)
284-
.filter(|info| info.module_source == *module_source)
283+
self.all_struct_fields
284+
.get(module_source)
285+
.and_then(|m| m.get(name))
285286
})
286287
}
287288

wado-compiler/src/resolver/expr.rs

Lines changed: 6 additions & 76 deletions
Original file line numberDiff line numberDiff line change
@@ -661,22 +661,10 @@ impl<H: CompilerHost> Resolver<'_, H> {
661661
module_source,
662662
..
663663
} => {
664-
// Use the flat struct_fields map first. When there's a name collision
665-
// (the entry is from a different module), re-resolve fields from the
666-
// source module to get the correct struct definition.
667-
if let Some(struct_info) = self.struct_fields.get(&name) {
668-
if struct_info.module_source == module_source {
669-
for (index, (fname, ftype, _)) in struct_info.fields.iter().enumerate() {
670-
if fname == field_name {
671-
return (index as u32, *ftype);
672-
}
673-
}
674-
} else {
675-
// Name collision: re-resolve field type from the loaded module
676-
if let Some(ftype) =
677-
self.resolve_field_in_source_module(&name, field_name, &module_source)
678-
{
679-
return ftype;
664+
if let Some(struct_info) = self.lookup_struct_fields(&name, &module_source) {
665+
for (index, (fname, ftype, _)) in struct_info.fields.iter().enumerate() {
666+
if fname == field_name {
667+
return (index as u32, *ftype);
680668
}
681669
}
682670
}
@@ -715,7 +703,7 @@ impl<H: CompilerHost> Resolver<'_, H> {
715703
return (0, TypeTable::UNKNOWN);
716704
}
717705
// Clone fields to avoid borrow issues
718-
let fields_clone = self.struct_fields.get(&name).cloned();
706+
let fields_clone = self.lookup_struct_fields(&name, &module_source).cloned();
719707
if let Some(struct_info) = fields_clone {
720708
for (index, (fname, ftype, _)) in struct_info.fields.iter().enumerate() {
721709
if fname == field_name {
@@ -731,64 +719,6 @@ impl<H: CompilerHost> Resolver<'_, H> {
731719
(0, TypeTable::UNKNOWN)
732720
}
733721

734-
/// Resolve a struct field type from the source module where the struct is defined.
735-
/// Used when the flat `struct_fields` map has a name collision (two modules define
736-
/// a struct with the same name). This re-resolves the field type in the source
737-
/// module's type context to get the correct result.
738-
pub(super) fn resolve_field_in_source_module(
739-
&mut self,
740-
struct_name: &str,
741-
field_name: &str,
742-
module_source: &ModuleSource,
743-
) -> Option<(u32, TypeId)> {
744-
// Pre-populate module type maps cache before borrowing loaded_modules
745-
self.ensure_module_maps_cached(module_source);
746-
747-
let loaded_module = self.loaded_modules.get(module_source)?;
748-
// Find the struct declaration in the loaded module
749-
let struct_decl = loaded_module.items.iter().find_map(|item| {
750-
if let Item::Struct(s) = item
751-
&& s.name == struct_name
752-
{
753-
Some(s)
754-
} else {
755-
None
756-
}
757-
})?;
758-
759-
// Swap to source module's type context using cached maps (O(1))
760-
let mut cached = self
761-
.module_type_maps_cache
762-
.shift_remove(module_source)
763-
.expect("cache populated by ensure_module_maps_cached");
764-
std::mem::swap(&mut self.struct_fields, &mut cached.struct_fields);
765-
std::mem::swap(&mut self.variant_cases, &mut cached.variant_cases);
766-
std::mem::swap(&mut self.enum_cases, &mut cached.enum_cases);
767-
std::mem::swap(&mut self.flags_cases, &mut cached.flags_cases);
768-
std::mem::swap(&mut self.newtypes, &mut cached.newtypes);
769-
std::mem::swap(&mut self.resource_types, &mut cached.resource_types);
770-
771-
// Find and resolve the field type
772-
let result = struct_decl
773-
.fields
774-
.iter()
775-
.enumerate()
776-
.find(|(_, f)| f.name == field_name)
777-
.map(|(index, f)| (index as u32, self.resolve_type(&f.ty)));
778-
779-
// Restore type maps and re-cache
780-
std::mem::swap(&mut self.struct_fields, &mut cached.struct_fields);
781-
std::mem::swap(&mut self.variant_cases, &mut cached.variant_cases);
782-
std::mem::swap(&mut self.enum_cases, &mut cached.enum_cases);
783-
std::mem::swap(&mut self.flags_cases, &mut cached.flags_cases);
784-
std::mem::swap(&mut self.newtypes, &mut cached.newtypes);
785-
std::mem::swap(&mut self.resource_types, &mut cached.resource_types);
786-
self.module_type_maps_cache
787-
.insert(module_source.clone(), cached);
788-
789-
result
790-
}
791-
792722
/// Check if a struct field is accessible from the current module.
793723
/// Non-pub fields are private to the module that defines them.
794724
fn check_field_visibility(&mut self, struct_type: TypeId, field_name: &str, span: Span) {
@@ -821,7 +751,7 @@ impl<H: CompilerHost> Resolver<'_, H> {
821751
}
822752

823753
// Look up field visibility
824-
if let Some(struct_info) = self.struct_fields.get(&struct_name) {
754+
if let Some(struct_info) = self.lookup_struct_fields(&struct_name, &module_source) {
825755
for (fname, _, is_pub) in &struct_info.fields {
826756
if fname == field_name && !is_pub {
827757
let _ = self.logger.error(TypeError::TypeMismatch {

0 commit comments

Comments
 (0)