Skip to main content

wac_types/
aggregator.rs

1use crate::{
2    names::{alternate_lookup_key, are_semver_compatible},
3    DefinedType, DefinedTypeId, FuncType, FuncTypeId, Interface, InterfaceId, ItemKind,
4    ModuleTypeId, Record, Resource, ResourceAlias, ResourceId, SubtypeChecker, Type, Types,
5    UsedType, ValueType, Variant, World, WorldId,
6};
7use anyhow::{bail, Context, Result};
8use indexmap::IndexMap;
9use std::collections::HashMap;
10
11/// Used to aggregate types defined in different `Types` collections.
12///
13/// A type aggregator can be used to merge compatible definitions of
14/// the same type into a single supertype definition stored within the
15/// aggregator; this is useful for imports that are shared across
16/// different instantiation arguments.
17///
18/// It works by first recursively remapping a type from a foreign `Types`
19/// collection into its own `Types` collection; any further attempt
20/// to aggregate the type causes a merge of type definitions provided
21/// they are compatible.
22#[derive(Default, Debug)]
23pub struct TypeAggregator {
24    /// The aggregated types collection.
25    types: Types,
26    /// The map from import name to aggregated item kind.
27    imports: IndexMap<String, ItemKind>,
28    /// A map from foreign type to remapped local type.
29    remapped: HashMap<Type, Type>,
30    /// A map of interface names to remapped interface id.
31    interfaces: HashMap<String, InterfaceId>,
32    /// Maps import names that were superseded by a higher semver-compatible
33    /// version to the canonical (highest version) name.
34    name_redirects: HashMap<String, String>,
35}
36
37impl TypeAggregator {
38    /// Creates a new type aggregator.
39    pub fn new() -> Self {
40        Self::default()
41    }
42
43    /// Gets the aggregator's type collection.
44    pub fn types(&self) -> &Types {
45        &self.types
46    }
47
48    /// Iterates the imported types in the aggregator.
49    pub fn imports(&self) -> impl Iterator<Item = (&str, ItemKind)> {
50        self.imports.iter().map(|(n, k)| (n.as_str(), *k))
51    }
52
53    /// Returns the canonical (highest semver version) import name for the given name.
54    ///
55    /// If the name was superseded by a higher semver-compatible version,
56    /// the canonical name is returned. Otherwise, the name itself is returned.
57    pub fn canonical_import_name<'a>(&'a self, name: &'a str) -> &'a str {
58        self.name_redirects
59            .get(name)
60            .map(|s| s.as_str())
61            .unwrap_or(name)
62    }
63
64    /// Finds an import whose name is on a compatible semver track with `name`.
65    ///
66    /// Returns both the existing import name and its `ItemKind`.
67    fn find_semver_compatible_import(&self, name: &str) -> Option<(&str, ItemKind)> {
68        let (alt_key, _) = alternate_lookup_key(name)?;
69        for (existing_name, kind) in &self.imports {
70            if let Some((existing_alt, _)) = alternate_lookup_key(existing_name) {
71                if existing_alt == alt_key {
72                    return Some((existing_name.as_str(), *kind));
73                }
74            }
75        }
76        None
77    }
78
79    /// Finds an interface whose name is on a compatible semver track with `name`.
80    fn find_semver_compatible_interface(&self, name: &str) -> Option<InterfaceId> {
81        let (alt_key, _) = alternate_lookup_key(name)?;
82        for (existing_name, id) in &self.interfaces {
83            if let Some((existing_alt, _)) = alternate_lookup_key(existing_name) {
84                if existing_alt == alt_key {
85                    return Some(*id);
86                }
87            }
88        }
89        None
90    }
91
92    /// Aggregates a item kind from a specified type collection using the given
93    /// import name.
94    ///
95    /// Note that if the aggregate operation fails, the aggregator is consumed.
96    pub fn aggregate(
97        mut self,
98        name: &str,
99        types: &Types,
100        kind: ItemKind,
101        checker: &mut SubtypeChecker,
102    ) -> Result<Self> {
103        // First check if this import has already been remapped into our
104        // types collection.
105        // If it has already been remapped, do a merge; otherwise, remap it.
106        if let Some(existing) = self.imports.get(name).copied() {
107            self.merge_item_kind(existing, types, kind, checker)?;
108            return Ok(self);
109        }
110
111        // Check for a semver-compatible import (e.g., a:b/c@0.2.0 matching a:b/c@0.2.1)
112        if let Some((existing_name, existing_kind)) = self.find_semver_compatible_import(name) {
113            // Copy values before the mutable borrow in merge_item_kind
114            let existing_name = existing_name.to_string();
115            self.merge_item_kind(existing_kind, types, kind, checker)?;
116            let (_, new_version) = alternate_lookup_key(name).unwrap();
117            let (_, existing_version) = alternate_lookup_key(&existing_name).unwrap();
118
119            if new_version > existing_version {
120                // New version is higher: remove old entry, insert new name
121                let merged_kind = self.imports.shift_remove(&existing_name).unwrap();
122                self.imports.insert(name.to_string(), merged_kind);
123                // Update any existing redirects that pointed to the old name
124                for redirect in self.name_redirects.values_mut() {
125                    if *redirect == existing_name {
126                        *redirect = name.to_string();
127                    }
128                }
129                self.name_redirects.insert(existing_name, name.to_string());
130            } else {
131                // Existing version is higher or equal: redirect new name to existing
132                self.name_redirects.insert(name.to_string(), existing_name);
133            }
134
135            return Ok(self);
136        }
137
138        let remapped = self.remap_item_kind(types, kind, checker)?;
139        let prev = self.imports.insert(name.to_string(), remapped);
140        assert!(prev.is_none());
141        Ok(self)
142    }
143
144    fn merge_item_kind(
145        &mut self,
146        existing: ItemKind,
147        types: &Types,
148        kind: ItemKind,
149        checker: &mut SubtypeChecker,
150    ) -> Result<()> {
151        match (existing, kind) {
152            (ItemKind::Instance(existing), ItemKind::Instance(id)) => {
153                self.merge_interface(existing, types, id, checker)
154            }
155            (ItemKind::Component(existing), ItemKind::Component(id)) => {
156                self.merge_world(existing, types, id, checker)
157            }
158            (ItemKind::Func(existing), ItemKind::Func(id)) => {
159                self.merge_func_type(existing, types, id, checker)
160            }
161            (ItemKind::Module(existing), ItemKind::Module(id)) => {
162                self.merge_module_type(existing, types, id, checker)
163            }
164            (ItemKind::Type(existing), ItemKind::Type(ty)) => {
165                self.merge_type(existing, types, ty, checker)
166            }
167            (ItemKind::Value(existing), ItemKind::Value(ty)) => {
168                self.merge_value_type(existing, types, ty, checker)
169            }
170            (existing, kind) => {
171                bail!(
172                    "{existing} cannot be merged with {kind}",
173                    existing = existing.desc(&self.types),
174                    kind = kind.desc(types)
175                );
176            }
177        }
178    }
179
180    fn merge_interface(
181        &mut self,
182        existing: InterfaceId,
183        types: &Types,
184        id: InterfaceId,
185        checker: &mut SubtypeChecker,
186    ) -> Result<()> {
187        // Merge the used types of the two interfaces
188        self.merge_interface_used_types(existing, types, id, checker)?;
189
190        // Merge the interface's exports
191        for (name, source_kind) in &types[id].exports {
192            if let Some(target_kind) = self.types[existing].exports.get(name).copied() {
193                // If the source kind is already a subtype of the target, do nothing
194                if checker
195                    .is_subtype(*source_kind, types, target_kind, &self.types)
196                    .is_ok()
197                {
198                    // Keep track that the source type should be replaced with the
199                    // target type wherever it's used.
200                    self.remapped.insert(source_kind.ty(), target_kind.ty());
201                    continue;
202                }
203
204                // Otherwise, the target *must* be a subtype of the source
205                // We'll remap the source below and replace
206                checker
207                    .is_subtype(target_kind, &self.types, *source_kind, types)
208                    .with_context(|| format!("mismatched type for export `{name}`"))?;
209            }
210
211            let remapped = self.remap_item_kind(types, *source_kind, checker)?;
212            self.types[existing].exports.insert(name.clone(), remapped);
213        }
214
215        Ok(())
216    }
217
218    fn merge_interface_used_types(
219        &mut self,
220        existing: InterfaceId,
221        types: &Types,
222        id: InterfaceId,
223        checker: &mut SubtypeChecker,
224    ) -> Result<()> {
225        let source = &types[id];
226        for (name, used) in &source.uses {
227            let used_interface = types[used.interface]
228                .id
229                .as_ref()
230                .context("used type has no interface identifier")?;
231
232            // Validate any existing used type of the same name
233            if let Some(existing) = self.types[existing].uses.get(name) {
234                let existing_interface = self.types[existing.interface]
235                    .id
236                    .as_ref()
237                    .context("used type has no interface identifier")?;
238
239                // The interface names must be on compatible semver tracks
240                if !are_semver_compatible(existing_interface, used_interface) {
241                    bail!("cannot merge used type `{name}` as it is expected to be from interface `{existing_interface}` but it is from interface `{used_interface}`");
242                }
243
244                // The types must be exported with the same name
245                if existing.name != used.name {
246                    bail!("cannot merge used type `{name}` as the export names are mismatched");
247                }
248            }
249
250            // Remap the used interface; this will handle merging if we've seen the interface before
251            let remapped = self.remap_interface(types, used.interface, checker)?;
252            match self.types[existing].uses.get(name) {
253                Some(existing) => {
254                    assert_eq!(
255                        existing.interface, remapped,
256                        "expected a merge to have occurred"
257                    );
258                }
259                None => {
260                    self.types[existing].uses.insert(
261                        name.clone(),
262                        UsedType {
263                            interface: remapped,
264                            name: used.name.clone(),
265                        },
266                    );
267                }
268            }
269        }
270
271        Ok(())
272    }
273
274    fn merge_world(
275        &mut self,
276        existing: WorldId,
277        types: &Types,
278        id: WorldId,
279        checker: &mut SubtypeChecker,
280    ) -> Result<()> {
281        // Merge the used types of the two worlds
282        self.merge_world_used_types(existing, types, id, checker)?;
283
284        // Merge the worlds's imports
285        checker.invert();
286        for (name, source_kind) in &types[id].imports {
287            if let Some(target_kind) = self.types[existing].imports.get(name).copied() {
288                // If the target kind is already a subtype of the source, do nothing
289                if checker
290                    .is_subtype(target_kind, &self.types, *source_kind, types)
291                    .is_ok()
292                {
293                    continue;
294                }
295
296                // Otherwise, the source *must* be a subtype of the target
297                // We'll remap the source below and replace
298                checker
299                    .is_subtype(*source_kind, types, target_kind, &self.types)
300                    .with_context(|| format!("mismatched type for import `{name}`"))?;
301            }
302
303            let remapped = self.remap_item_kind(types, *source_kind, checker)?;
304            self.types[existing].imports.insert(name.clone(), remapped);
305        }
306
307        checker.revert();
308
309        // Merge the worlds's exports
310        for (name, source_kind) in &types[id].exports {
311            if let Some(target_kind) = self.types[existing].exports.get(name).copied() {
312                // If the source kind is already a subtype of the target, do nothing
313                if checker
314                    .is_subtype(*source_kind, types, target_kind, &self.types)
315                    .is_ok()
316                {
317                    continue;
318                }
319
320                // Otherwise, the target *must* be a subtype of the source
321                // We'll remap the source below and replace
322                checker
323                    .is_subtype(target_kind, &self.types, *source_kind, types)
324                    .with_context(|| format!("mismatched type for export `{name}`"))?;
325            }
326
327            let remapped = self.remap_item_kind(types, *source_kind, checker)?;
328            self.types[existing].exports.insert(name.clone(), remapped);
329        }
330
331        Ok(())
332    }
333
334    fn merge_world_used_types(
335        &mut self,
336        existing: WorldId,
337        types: &Types,
338        id: WorldId,
339        checker: &mut SubtypeChecker,
340    ) -> Result<()> {
341        let source = &types[id];
342        for (name, used) in &source.uses {
343            let used_interface = types[used.interface]
344                .id
345                .as_ref()
346                .context("used type has no interface identifier")?;
347
348            // Validate any existing used type of the same name
349            if let Some(existing) = self.types[existing].uses.get(name) {
350                let existing_interface = self.types[existing.interface]
351                    .id
352                    .as_ref()
353                    .context("used type has no interface identifier")?;
354
355                // The interface names must be on compatible semver tracks
356                if !are_semver_compatible(existing_interface, used_interface) {
357                    bail!("cannot merge used type `{name}` as it is expected to be from interface `{existing_interface}` but it is from interface `{used_interface}`");
358                }
359
360                // The types must be exported with the same name
361                if existing.name != used.name {
362                    bail!("cannot merge used type `{name}` as the export names are mismatched");
363                }
364            }
365
366            // Remap the used interface; this will handle merging if we've seen the interface before
367            let remapped = self.remap_interface(types, used.interface, checker)?;
368            match self.types[existing].uses.get(name) {
369                Some(existing) => {
370                    assert_eq!(
371                        existing.interface, remapped,
372                        "expected a merge to have occurred"
373                    );
374                }
375                None => {
376                    let prev = self.types[existing].uses.insert(
377                        name.clone(),
378                        UsedType {
379                            interface: remapped,
380                            name: used.name.clone(),
381                        },
382                    );
383                    assert!(prev.is_none());
384                }
385            }
386        }
387
388        Ok(())
389    }
390
391    fn merge_func_type(
392        &mut self,
393        existing: FuncTypeId,
394        types: &Types,
395        id: FuncTypeId,
396        checker: &mut SubtypeChecker,
397    ) -> Result<()> {
398        // Currently function types are full equality for subtype checking, so
399        // simply do a subtype check in both directions
400        checker.is_subtype(
401            ItemKind::Func(id),
402            types,
403            ItemKind::Func(existing),
404            &self.types,
405        )?;
406        checker.is_subtype(
407            ItemKind::Func(existing),
408            &self.types,
409            ItemKind::Func(id),
410            types,
411        )?;
412
413        Ok(())
414    }
415
416    fn merge_module_type(
417        &mut self,
418        existing: ModuleTypeId,
419        types: &Types,
420        id: ModuleTypeId,
421        checker: &mut SubtypeChecker,
422    ) -> Result<()> {
423        // Merge the module type's imports
424        checker.invert();
425        for (name, source_extern) in &types[id].imports {
426            if let Some(target_extern) = self.types[existing].imports.get(name) {
427                // If the target extern is already a subtype of the source, do nothing
428                if checker
429                    .core_extern(target_extern, &self.types, source_extern, types)
430                    .is_ok()
431                {
432                    continue;
433                }
434
435                // Otherwise, the source *must* be a subtype of the target
436                // We'll remap the source below and replace
437                checker
438                    .core_extern(source_extern, types, target_extern, &self.types)
439                    .with_context(|| {
440                        format!(
441                            "mismatched type for import `{m}::{n}`",
442                            m = name.0,
443                            n = name.1
444                        )
445                    })?;
446            }
447
448            self.types[existing]
449                .imports
450                .insert(name.clone(), source_extern.clone());
451        }
452
453        checker.revert();
454
455        // Merge the module type's exports
456        for (name, source_extern) in &types[id].exports {
457            if let Some(target_extern) = self.types[existing].exports.get(name) {
458                // If the source kind is already a subtype of the target, do nothing
459                // If the target extern is already a subtype of the source, do nothing
460                if checker
461                    .core_extern(source_extern, types, target_extern, &self.types)
462                    .is_ok()
463                {
464                    continue;
465                }
466
467                // Otherwise, the target *must* be a subtype of the source
468                // We'll remap the source below and replace
469                checker
470                    .core_extern(target_extern, &self.types, source_extern, types)
471                    .with_context(|| format!("mismatched type for export `{name}`"))?;
472            }
473
474            self.types[existing]
475                .exports
476                .insert(name.clone(), source_extern.clone());
477        }
478
479        Ok(())
480    }
481
482    fn merge_type(
483        &mut self,
484        existing: Type,
485        types: &Types,
486        ty: Type,
487        checker: &mut SubtypeChecker,
488    ) -> Result<()> {
489        match (existing, ty) {
490            (Type::Resource(existing), Type::Resource(id)) => {
491                self.merge_resource(existing, types, id, checker)
492            }
493            (Type::Func(existing), Type::Func(id)) => {
494                self.merge_func_type(existing, types, id, checker)
495            }
496            (Type::Value(existing), Type::Value(ty)) => {
497                self.merge_value_type(existing, types, ty, checker)
498            }
499            (Type::Interface(existing), Type::Interface(id)) => {
500                self.merge_interface(existing, types, id, checker)
501            }
502            (Type::World(existing), Type::World(id)) => {
503                self.merge_world(existing, types, id, checker)
504            }
505            (Type::Module(existing), Type::Module(id)) => {
506                self.merge_module_type(existing, types, id, checker)
507            }
508            _ => bail!(
509                "{existing} cannot be merged with {ty}",
510                existing = existing.desc(&self.types),
511                ty = ty.desc(types)
512            ),
513        }
514    }
515
516    fn merge_resource(
517        &mut self,
518        existing: ResourceId,
519        types: &Types,
520        id: ResourceId,
521        checker: &mut SubtypeChecker,
522    ) -> Result<()> {
523        // Currently the subtype check is only checking that the underlying
524        // resource names are the same; check for equality
525        checker.is_subtype(
526            ItemKind::Type(Type::Resource(id)),
527            types,
528            ItemKind::Type(Type::Resource(existing)),
529            &self.types,
530        )?;
531
532        checker.is_subtype(
533            ItemKind::Type(Type::Resource(existing)),
534            &self.types,
535            ItemKind::Type(Type::Resource(id)),
536            types,
537        )?;
538
539        Ok(())
540    }
541
542    fn merge_value_type(
543        &mut self,
544        existing: ValueType,
545        types: &Types,
546        ty: ValueType,
547        checker: &mut SubtypeChecker,
548    ) -> Result<()> {
549        // Currently the subtype check for value types is done by equality
550        checker.is_subtype(
551            ItemKind::Value(ty),
552            types,
553            ItemKind::Value(existing),
554            &self.types,
555        )?;
556
557        checker.is_subtype(
558            ItemKind::Value(existing),
559            &self.types,
560            ItemKind::Value(ty),
561            types,
562        )?;
563
564        Ok(())
565    }
566
567    fn remap_item_kind(
568        &mut self,
569        types: &Types,
570        kind: ItemKind,
571        checker: &mut SubtypeChecker,
572    ) -> Result<ItemKind> {
573        match kind {
574            ItemKind::Type(ty) => Ok(ItemKind::Type(self.remap_type(types, ty, checker)?)),
575            ItemKind::Func(id) => Ok(ItemKind::Func(self.remap_func_type(types, id, checker)?)),
576            ItemKind::Instance(id) => Ok(ItemKind::Instance(
577                self.remap_interface(types, id, checker)?,
578            )),
579            ItemKind::Component(id) => {
580                Ok(ItemKind::Component(self.remap_world(types, id, checker)?))
581            }
582            ItemKind::Module(id) => Ok(ItemKind::Module(self.remap_module_type(types, id))),
583            ItemKind::Value(ty) => Ok(ItemKind::Value(self.remap_value_type(types, ty, checker)?)),
584        }
585    }
586
587    fn remap_type(
588        &mut self,
589        types: &Types,
590        ty: Type,
591        checker: &mut SubtypeChecker,
592    ) -> Result<Type> {
593        match ty {
594            Type::Resource(id) => Ok(Type::Resource(self.remap_resource(types, id, checker)?)),
595            Type::Func(id) => Ok(Type::Func(self.remap_func_type(types, id, checker)?)),
596            Type::Value(ty) => Ok(Type::Value(self.remap_value_type(types, ty, checker)?)),
597            Type::Interface(id) => Ok(Type::Interface(self.remap_interface(types, id, checker)?)),
598            Type::World(id) => Ok(Type::World(self.remap_world(types, id, checker)?)),
599            Type::Module(id) => Ok(Type::Module(self.remap_module_type(types, id))),
600        }
601    }
602
603    fn remap_resource(
604        &mut self,
605        types: &Types,
606        id: ResourceId,
607        checker: &mut SubtypeChecker,
608    ) -> Result<ResourceId> {
609        if let Some(kind) = self.remapped.get(&Type::Resource(id)) {
610            return match kind {
611                Type::Resource(id) => Ok(*id),
612                _ => panic!("expected a resource"),
613            };
614        }
615
616        let resource = &types[id];
617        let remapped = Resource {
618            name: resource.name.clone(),
619            alias: resource
620                .alias
621                .map(|a| -> Result<_> {
622                    let owner = a
623                        .owner
624                        .map(|id| {
625                            // There's no need to merge the interface here as
626                            // merging is done as part of the interface remapping
627                            self.remap_interface(types, id, checker)
628                        })
629                        .transpose()?;
630                    // If there is an owning interface, ensure it is imported
631                    if let Some(owner) = owner {
632                        let name = self.types()[owner]
633                            .id
634                            .as_deref()
635                            .expect("interface has no id");
636                        if !self.imports.contains_key(name) {
637                            self.imports
638                                .insert(name.to_owned(), ItemKind::Instance(owner));
639                        }
640                    }
641                    Ok(ResourceAlias {
642                        owner,
643                        source: self.remap_resource(types, a.source, checker)?,
644                    })
645                })
646                .transpose()?,
647        };
648        let remapped_id = self.types.add_resource(remapped);
649
650        let prev = self
651            .remapped
652            .insert(Type::Resource(id), Type::Resource(remapped_id));
653        assert!(prev.is_none());
654        Ok(remapped_id)
655    }
656
657    fn remap_func_type(
658        &mut self,
659        types: &Types,
660        id: FuncTypeId,
661        checker: &mut SubtypeChecker,
662    ) -> Result<FuncTypeId> {
663        if let Some(kind) = self.remapped.get(&Type::Func(id)) {
664            return match kind {
665                Type::Func(id) => Ok(*id),
666                _ => panic!("expected a function type"),
667            };
668        }
669
670        let ty = &types[id];
671        let remapped = FuncType {
672            params: ty
673                .params
674                .iter()
675                .map(|(n, ty)| Ok((n.clone(), self.remap_value_type(types, *ty, checker)?)))
676                .collect::<Result<_>>()?,
677            result: ty
678                .result
679                .map(|ty| self.remap_value_type(types, ty, checker))
680                .transpose()?,
681            is_async: ty.is_async,
682        };
683
684        let remapped_id = self.types.add_func_type(remapped);
685        let prev = self
686            .remapped
687            .insert(Type::Func(id), Type::Func(remapped_id));
688        assert!(prev.is_none());
689        Ok(remapped_id)
690    }
691
692    fn remap_value_type(
693        &mut self,
694        types: &Types,
695        ty: ValueType,
696        checker: &mut SubtypeChecker,
697    ) -> Result<ValueType> {
698        match ty {
699            ValueType::Primitive(ty) => Ok(ValueType::Primitive(ty)),
700            ValueType::Borrow(id) => {
701                Ok(ValueType::Borrow(self.remap_resource(types, id, checker)?))
702            }
703            ValueType::Own(id) => Ok(ValueType::Own(self.remap_resource(types, id, checker)?)),
704            ValueType::Defined(id) => Ok(ValueType::Defined(
705                self.remap_defined_type(types, id, checker)?,
706            )),
707        }
708    }
709
710    fn remap_interface(
711        &mut self,
712        types: &Types,
713        id: InterfaceId,
714        checker: &mut SubtypeChecker,
715    ) -> Result<InterfaceId> {
716        // If we've seen this interface before, perform a merge
717        // This will ensure that there's only a singular definition of "named" interfaces,
718        // including interfaces on compatible semver tracks
719        if let Some(name) = types[id].id.as_ref() {
720            if let Some(existing) = self
721                .interfaces
722                .get(name)
723                .copied()
724                .or_else(|| self.find_semver_compatible_interface(name))
725            {
726                self.merge_interface(existing, types, id, checker)
727                    .with_context(|| format!("failed to merge interface `{name}`"))?;
728                // Also register this name so it can be looked up directly
729                self.interfaces.insert(name.clone(), existing);
730                return Ok(existing);
731            }
732        }
733
734        if let Some(kind) = self.remapped.get(&Type::Interface(id)) {
735            return match kind {
736                Type::Interface(id) => Ok(*id),
737                _ => panic!("expected an interface"),
738            };
739        }
740
741        let ty = &types[id];
742        let interface = Interface {
743            id: ty.id.clone(),
744            uses: ty
745                .uses
746                .iter()
747                .map(|(n, u)| {
748                    if types[u.interface].id.is_none() {
749                        bail!("used type `{n}` is from an interface without an identifier");
750                    }
751
752                    Ok((
753                        n.clone(),
754                        UsedType {
755                            interface: self.remap_interface(types, u.interface, checker)?,
756                            name: u.name.clone(),
757                        },
758                    ))
759                })
760                .collect::<Result<_>>()?,
761            exports: ty
762                .exports
763                .iter()
764                .map(|(n, k)| Ok((n.clone(), self.remap_item_kind(types, *k, checker)?)))
765                .collect::<Result<_>>()?,
766        };
767
768        let remapped = self.types.add_interface(interface);
769        let prev = self
770            .remapped
771            .insert(Type::Interface(id), Type::Interface(remapped));
772        assert!(prev.is_none());
773
774        if let Some(name) = self.types[remapped].id.as_ref() {
775            let prev = self.interfaces.insert(name.clone(), remapped);
776            assert!(prev.is_none());
777        }
778
779        Ok(remapped)
780    }
781
782    fn remap_world(
783        &mut self,
784        types: &Types,
785        id: WorldId,
786        checker: &mut SubtypeChecker,
787    ) -> Result<WorldId> {
788        if let Some(kind) = self.remapped.get(&Type::World(id)) {
789            return match kind {
790                Type::World(id) => Ok(*id),
791                _ => panic!("expected a world"),
792            };
793        }
794
795        let ty = &types[id];
796        let world = World {
797            id: ty.id.clone(),
798            uses: ty
799                .uses
800                .iter()
801                .map(|(n, u)| {
802                    if types[u.interface].id.is_none() {
803                        bail!("used type `{n}` is from an interface without an identifier");
804                    }
805
806                    Ok((
807                        n.clone(),
808                        UsedType {
809                            interface: self.remap_interface(types, u.interface, checker)?,
810                            name: u.name.clone(),
811                        },
812                    ))
813                })
814                .collect::<Result<_>>()?,
815            imports: ty
816                .imports
817                .iter()
818                .map(|(n, k)| Ok((n.clone(), self.remap_item_kind(types, *k, checker)?)))
819                .collect::<Result<_>>()?,
820            exports: ty
821                .exports
822                .iter()
823                .map(|(n, k)| Ok((n.clone(), self.remap_item_kind(types, *k, checker)?)))
824                .collect::<Result<_>>()?,
825        };
826
827        let remapped = self.types.add_world(world);
828        let prev = self.remapped.insert(Type::World(id), Type::World(remapped));
829        assert!(prev.is_none());
830
831        Ok(remapped)
832    }
833
834    fn remap_module_type(&mut self, types: &Types, id: ModuleTypeId) -> ModuleTypeId {
835        if let Some(kind) = self.remapped.get(&Type::Module(id)) {
836            return match kind {
837                Type::Module(id) => *id,
838                _ => panic!("expected a module type"),
839            };
840        }
841
842        let ty = &types[id];
843        let remapped = self.types.add_module_type(ty.clone());
844        let prev = self
845            .remapped
846            .insert(Type::Module(id), Type::Module(remapped));
847        assert!(prev.is_none());
848        remapped
849    }
850
851    fn remap_defined_type(
852        &mut self,
853        types: &Types,
854        id: DefinedTypeId,
855        checker: &mut SubtypeChecker,
856    ) -> Result<DefinedTypeId> {
857        if let Some(kind) = self.remapped.get(&Type::Value(ValueType::Defined(id))) {
858            return match kind {
859                Type::Value(ValueType::Defined(id)) => Ok(*id),
860                _ => panic!("expected a defined type got {kind:?}"),
861            };
862        }
863
864        let defined = match &types[id] {
865            DefinedType::Tuple(tys) => DefinedType::Tuple(
866                tys.iter()
867                    .map(|ty| self.remap_value_type(types, *ty, checker))
868                    .collect::<Result<_>>()?,
869            ),
870            DefinedType::List(ty) => DefinedType::List(self.remap_value_type(types, *ty, checker)?),
871            DefinedType::FixedSizeList(ty, elements) => {
872                DefinedType::FixedSizeList(self.remap_value_type(types, *ty, checker)?, *elements)
873            }
874            DefinedType::Option(ty) => {
875                DefinedType::Option(self.remap_value_type(types, *ty, checker)?)
876            }
877            DefinedType::Result { ok, err } => DefinedType::Result {
878                ok: ok
879                    .as_ref()
880                    .map(|ty| self.remap_value_type(types, *ty, checker))
881                    .transpose()?,
882                err: err
883                    .as_ref()
884                    .map(|ty| self.remap_value_type(types, *ty, checker))
885                    .transpose()?,
886            },
887            DefinedType::Variant(v) => DefinedType::Variant(Variant {
888                cases: v
889                    .cases
890                    .iter()
891                    .map(|(n, ty)| {
892                        Ok((
893                            n.clone(),
894                            ty.as_ref()
895                                .map(|ty| self.remap_value_type(types, *ty, checker))
896                                .transpose()?,
897                        ))
898                    })
899                    .collect::<Result<_>>()?,
900            }),
901            DefinedType::Record(r) => DefinedType::Record(Record {
902                fields: r
903                    .fields
904                    .iter()
905                    .map(|(n, ty)| Ok((n.clone(), self.remap_value_type(types, *ty, checker)?)))
906                    .collect::<Result<_>>()?,
907            }),
908            DefinedType::Flags(f) => DefinedType::Flags(f.clone()),
909            DefinedType::Enum(e) => DefinedType::Enum(e.clone()),
910            DefinedType::Alias(ty) => {
911                DefinedType::Alias(self.remap_value_type(types, *ty, checker)?)
912            }
913            DefinedType::Stream(s) => DefinedType::Stream(
914                s.as_ref()
915                    .map(|ty| self.remap_value_type(types, *ty, checker))
916                    .transpose()?,
917            ),
918            DefinedType::Future(f) => DefinedType::Future(
919                f.as_ref()
920                    .map(|ty| self.remap_value_type(types, *ty, checker))
921                    .transpose()?,
922            ),
923        };
924
925        let remapped = self.types.add_defined_type(defined);
926        let prev = self.remapped.insert(
927            Type::Value(ValueType::Defined(id)),
928            Type::Value(ValueType::Defined(remapped)),
929        );
930        assert!(prev.is_none());
931        Ok(remapped)
932    }
933}
934
935#[cfg(test)]
936mod tests {
937    use super::*;
938    use std::collections::HashSet;
939
940    // Helper to create a simple interface with optional id and exports
941    fn make_interface(
942        types: &mut Types,
943        name: Option<&str>,
944        exports: Vec<(&str, ItemKind)>,
945    ) -> InterfaceId {
946        let interface = Interface {
947            id: name.map(|n| n.to_string()),
948            uses: IndexMap::new(),
949            exports: exports
950                .into_iter()
951                .map(|(n, k)| (n.to_string(), k))
952                .collect(),
953        };
954        types.add_interface(interface)
955    }
956
957    // Helper to create a simple func type (no params, no result)
958    fn make_func_type(types: &mut Types) -> FuncTypeId {
959        types.add_func_type(FuncType {
960            params: IndexMap::new(),
961            result: None,
962            is_async: false,
963        })
964    }
965
966    #[test]
967    fn semver_compatible_same_name() {
968        assert!(are_semver_compatible("a:b/c@0.2.0", "a:b/c@0.2.0"));
969    }
970
971    #[test]
972    fn semver_compatible_patch_versions() {
973        assert!(are_semver_compatible("a:b/c@0.2.0", "a:b/c@0.2.1"));
974        assert!(are_semver_compatible("a:b/c@0.2.1", "a:b/c@0.2.0"));
975        assert!(are_semver_compatible("a:b/c@0.2.0", "a:b/c@0.2.3"));
976    }
977
978    #[test]
979    fn semver_compatible_major_track() {
980        assert!(are_semver_compatible("a:b/c@1.0.0", "a:b/c@1.1.0"));
981        assert!(are_semver_compatible("a:b/c@1.0.0", "a:b/c@1.2.3"));
982        assert!(are_semver_compatible("a:b/c@2.0.0", "a:b/c@2.1.0"));
983    }
984
985    #[test]
986    fn semver_incompatible_minor_versions() {
987        assert!(!are_semver_compatible("a:b/c@0.2.0", "a:b/c@0.3.0"));
988    }
989
990    #[test]
991    fn semver_incompatible_major_versions() {
992        assert!(!are_semver_compatible("a:b/c@1.0.0", "a:b/c@2.0.0"));
993    }
994
995    #[test]
996    fn semver_compatible_no_version() {
997        // Same name without version
998        assert!(are_semver_compatible("a:b/c", "a:b/c"));
999        // Different names without version
1000        assert!(!are_semver_compatible("a:b/c", "a:b/d"));
1001        // One with version, one without
1002        assert!(!are_semver_compatible("a:b/c@1.0.0", "a:b/c"));
1003        assert!(!are_semver_compatible("a:b/c", "a:b/c@1.0.0"));
1004    }
1005
1006    #[test]
1007    fn semver_compatible_prerelease_rejected() {
1008        assert!(!are_semver_compatible("a:b/c@1.0.0-rc.1", "a:b/c@1.0.0"));
1009        assert!(!are_semver_compatible("a:b/c@0.2.0-pre", "a:b/c@0.2.0"));
1010    }
1011
1012    #[test]
1013    fn semver_compatible_zero_patch_rejected() {
1014        // 0.0.x versions are not compatible with anything
1015        assert!(!are_semver_compatible("a:b/c@0.0.1", "a:b/c@0.0.2"));
1016    }
1017
1018    #[test]
1019    fn aggregate_exact_match_merges() {
1020        let mut types = Types::default();
1021        let func_id = make_func_type(&mut types);
1022        let iface_id = make_interface(
1023            &mut types,
1024            Some("a:b/c@0.2.0"),
1025            vec![("foo", ItemKind::Func(func_id))],
1026        );
1027
1028        let mut cache = HashSet::new();
1029        let mut checker = SubtypeChecker::new(&mut cache);
1030
1031        let agg = TypeAggregator::new();
1032        let agg = agg
1033            .aggregate(
1034                "a:b/c@0.2.0",
1035                &types,
1036                ItemKind::Instance(iface_id),
1037                &mut checker,
1038            )
1039            .unwrap();
1040        // Aggregate again with the same exact name
1041        let agg = agg
1042            .aggregate(
1043                "a:b/c@0.2.0",
1044                &types,
1045                ItemKind::Instance(iface_id),
1046                &mut checker,
1047            )
1048            .unwrap();
1049
1050        // Should still have one import entry (exact match merges in place)
1051        assert_eq!(agg.imports().count(), 1);
1052    }
1053
1054    #[test]
1055    fn aggregate_semver_compatible_imports_merge() {
1056        let mut types1 = Types::default();
1057        let func_id1 = make_func_type(&mut types1);
1058        let iface_id1 = make_interface(
1059            &mut types1,
1060            Some("a:b/c@0.2.0"),
1061            vec![("foo", ItemKind::Func(func_id1))],
1062        );
1063
1064        let mut types2 = Types::default();
1065        let func_id2 = make_func_type(&mut types2);
1066        let iface_id2 = make_interface(
1067            &mut types2,
1068            Some("a:b/c@0.2.1"),
1069            vec![("foo", ItemKind::Func(func_id2))],
1070        );
1071
1072        let mut cache = HashSet::new();
1073        let mut checker = SubtypeChecker::new(&mut cache);
1074
1075        let agg = TypeAggregator::new();
1076        let agg = agg
1077            .aggregate(
1078                "a:b/c@0.2.0",
1079                &types1,
1080                ItemKind::Instance(iface_id1),
1081                &mut checker,
1082            )
1083            .unwrap();
1084        let agg = agg
1085            .aggregate(
1086                "a:b/c@0.2.1",
1087                &types2,
1088                ItemKind::Instance(iface_id2),
1089                &mut checker,
1090            )
1091            .unwrap();
1092
1093        // Only the highest version is retained in the imports
1094        let items: Vec<_> = agg.imports().collect();
1095        assert_eq!(items.len(), 1);
1096        assert_eq!(items[0].0, "a:b/c@0.2.1");
1097
1098        // The lower version name redirects to the canonical (highest) name
1099        assert_eq!(agg.canonical_import_name("a:b/c@0.2.0"), "a:b/c@0.2.1");
1100        assert_eq!(agg.canonical_import_name("a:b/c@0.2.1"), "a:b/c@0.2.1");
1101    }
1102
1103    #[test]
1104    fn aggregate_semver_incompatible_imports_stay_separate() {
1105        let mut types1 = Types::default();
1106        let func_id1 = make_func_type(&mut types1);
1107        let iface_id1 = make_interface(
1108            &mut types1,
1109            Some("a:b/c@0.2.0"),
1110            vec![("foo", ItemKind::Func(func_id1))],
1111        );
1112
1113        let mut types2 = Types::default();
1114        let func_id2 = make_func_type(&mut types2);
1115        let iface_id2 = make_interface(
1116            &mut types2,
1117            Some("a:b/c@0.3.0"),
1118            vec![("foo", ItemKind::Func(func_id2))],
1119        );
1120
1121        let mut cache = HashSet::new();
1122        let mut checker = SubtypeChecker::new(&mut cache);
1123
1124        let agg = TypeAggregator::new();
1125        let agg = agg
1126            .aggregate(
1127                "a:b/c@0.2.0",
1128                &types1,
1129                ItemKind::Instance(iface_id1),
1130                &mut checker,
1131            )
1132            .unwrap();
1133        let agg = agg
1134            .aggregate(
1135                "a:b/c@0.3.0",
1136                &types2,
1137                ItemKind::Instance(iface_id2),
1138                &mut checker,
1139            )
1140            .unwrap();
1141
1142        // Two separate imports on different semver tracks
1143        let items: Vec<_> = agg.imports().collect();
1144        assert_eq!(items.len(), 2);
1145        assert_ne!(items[0].1, items[1].1);
1146    }
1147
1148    #[test]
1149    fn aggregate_semver_compatible_major_merge() {
1150        let mut types1 = Types::default();
1151        let func_id1 = make_func_type(&mut types1);
1152        let iface_id1 = make_interface(
1153            &mut types1,
1154            Some("a:b/c@1.0.0"),
1155            vec![("foo", ItemKind::Func(func_id1))],
1156        );
1157
1158        let mut types2 = Types::default();
1159        let func_id2 = make_func_type(&mut types2);
1160        let iface_id2 = make_interface(
1161            &mut types2,
1162            Some("a:b/c@1.1.0"),
1163            vec![("foo", ItemKind::Func(func_id2))],
1164        );
1165
1166        let mut cache = HashSet::new();
1167        let mut checker = SubtypeChecker::new(&mut cache);
1168
1169        let agg = TypeAggregator::new();
1170        let agg = agg
1171            .aggregate(
1172                "a:b/c@1.0.0",
1173                &types1,
1174                ItemKind::Instance(iface_id1),
1175                &mut checker,
1176            )
1177            .unwrap();
1178        let agg = agg
1179            .aggregate(
1180                "a:b/c@1.1.0",
1181                &types2,
1182                ItemKind::Instance(iface_id2),
1183                &mut checker,
1184            )
1185            .unwrap();
1186
1187        // Only the highest version is retained in the imports
1188        let items: Vec<_> = agg.imports().collect();
1189        assert_eq!(items.len(), 1);
1190        assert_eq!(items[0].0, "a:b/c@1.1.0");
1191
1192        // The lower version name redirects to the canonical (highest) name
1193        assert_eq!(agg.canonical_import_name("a:b/c@1.0.0"), "a:b/c@1.1.0");
1194        assert_eq!(agg.canonical_import_name("a:b/c@1.1.0"), "a:b/c@1.1.0");
1195    }
1196
1197    #[test]
1198    fn find_compatible_import_returns_match() {
1199        let mut types = Types::default();
1200        let func_id = make_func_type(&mut types);
1201        let iface_id = make_interface(
1202            &mut types,
1203            Some("a:b/c@0.2.0"),
1204            vec![("foo", ItemKind::Func(func_id))],
1205        );
1206
1207        let mut cache = HashSet::new();
1208        let mut checker = SubtypeChecker::new(&mut cache);
1209
1210        let agg = TypeAggregator::new();
1211        let agg = agg
1212            .aggregate(
1213                "a:b/c@0.2.0",
1214                &types,
1215                ItemKind::Instance(iface_id),
1216                &mut checker,
1217            )
1218            .unwrap();
1219
1220        assert!(agg.find_semver_compatible_import("a:b/c@0.2.1").is_some());
1221        assert!(agg.find_semver_compatible_import("a:b/c@0.2.5").is_some());
1222        assert!(agg.find_semver_compatible_import("a:b/c@0.3.0").is_none());
1223        assert!(agg.find_semver_compatible_import("a:b/c@1.0.0").is_none());
1224        assert!(agg.find_semver_compatible_import("x:y/z@0.2.0").is_none());
1225    }
1226
1227    #[test]
1228    fn find_compatible_interface_returns_match() {
1229        let mut types = Types::default();
1230        let func_id = make_func_type(&mut types);
1231        let iface_id = make_interface(
1232            &mut types,
1233            Some("a:b/c@0.2.0"),
1234            vec![("foo", ItemKind::Func(func_id))],
1235        );
1236
1237        let mut cache = HashSet::new();
1238        let mut checker = SubtypeChecker::new(&mut cache);
1239
1240        let agg = TypeAggregator::new();
1241        let agg = agg
1242            .aggregate(
1243                "a:b/c@0.2.0",
1244                &types,
1245                ItemKind::Instance(iface_id),
1246                &mut checker,
1247            )
1248            .unwrap();
1249
1250        assert!(agg
1251            .find_semver_compatible_interface("a:b/c@0.2.1")
1252            .is_some());
1253        assert!(agg
1254            .find_semver_compatible_interface("a:b/c@0.3.0")
1255            .is_none());
1256    }
1257
1258    #[test]
1259    fn merge_used_types_from_semver_compatible_interfaces() {
1260        // Types1: interface "dep:pkg/types@0.2.0" used by "my:pkg/iface@1.0.0"
1261        let mut types1 = Types::default();
1262        let func1 = make_func_type(&mut types1);
1263        let dep_iface1 = make_interface(
1264            &mut types1,
1265            Some("dep:pkg/types@0.2.0"),
1266            vec![("my-func", ItemKind::Func(func1))],
1267        );
1268        let main_iface1 = {
1269            let mut uses = IndexMap::new();
1270            uses.insert(
1271                "my-used".to_string(),
1272                UsedType {
1273                    interface: dep_iface1,
1274                    name: None,
1275                },
1276            );
1277            let interface = Interface {
1278                id: Some("my:pkg/iface@1.0.0".to_string()),
1279                uses,
1280                exports: IndexMap::new(),
1281            };
1282            types1.add_interface(interface)
1283        };
1284
1285        // Types2: interface "dep:pkg/types@0.2.1" (compatible) used by "my:pkg/iface@1.0.0"
1286        let mut types2 = Types::default();
1287        let func2 = make_func_type(&mut types2);
1288        let dep_iface2 = make_interface(
1289            &mut types2,
1290            Some("dep:pkg/types@0.2.1"),
1291            vec![("my-func", ItemKind::Func(func2))],
1292        );
1293        let main_iface2 = {
1294            let mut uses = IndexMap::new();
1295            uses.insert(
1296                "my-used".to_string(),
1297                UsedType {
1298                    interface: dep_iface2,
1299                    name: None,
1300                },
1301            );
1302            let interface = Interface {
1303                id: Some("my:pkg/iface@1.0.0".to_string()),
1304                uses,
1305                exports: IndexMap::new(),
1306            };
1307            types2.add_interface(interface)
1308        };
1309
1310        let mut cache = HashSet::new();
1311        let mut checker = SubtypeChecker::new(&mut cache);
1312
1313        let agg = TypeAggregator::new();
1314        let agg = agg
1315            .aggregate(
1316                "my:pkg/iface@1.0.0",
1317                &types1,
1318                ItemKind::Instance(main_iface1),
1319                &mut checker,
1320            )
1321            .unwrap();
1322
1323        // This should succeed because dep:pkg/types@0.2.0 and @0.2.1 are compatible
1324        let _agg = agg
1325            .aggregate(
1326                "my:pkg/iface@1.0.0",
1327                &types2,
1328                ItemKind::Instance(main_iface2),
1329                &mut checker,
1330            )
1331            .unwrap();
1332    }
1333
1334    #[test]
1335    fn merge_used_types_from_semver_incompatible_interfaces_fails() {
1336        // Types1: interface "dep:pkg/types@0.2.0" used by "my:pkg/iface@1.0.0"
1337        let mut types1 = Types::default();
1338        let func1 = make_func_type(&mut types1);
1339        let dep_iface1 = make_interface(
1340            &mut types1,
1341            Some("dep:pkg/types@0.2.0"),
1342            vec![("my-func", ItemKind::Func(func1))],
1343        );
1344        let main_iface1 = {
1345            let mut uses = IndexMap::new();
1346            uses.insert(
1347                "my-used".to_string(),
1348                UsedType {
1349                    interface: dep_iface1,
1350                    name: None,
1351                },
1352            );
1353            let interface = Interface {
1354                id: Some("my:pkg/iface@1.0.0".to_string()),
1355                uses,
1356                exports: IndexMap::new(),
1357            };
1358            types1.add_interface(interface)
1359        };
1360
1361        // Types2: interface "dep:pkg/types@0.3.0" (INCOMPATIBLE) used by "my:pkg/iface@1.0.0"
1362        let mut types2 = Types::default();
1363        let func2 = make_func_type(&mut types2);
1364        let dep_iface2 = make_interface(
1365            &mut types2,
1366            Some("dep:pkg/types@0.3.0"),
1367            vec![("my-func", ItemKind::Func(func2))],
1368        );
1369        let main_iface2 = {
1370            let mut uses = IndexMap::new();
1371            uses.insert(
1372                "my-used".to_string(),
1373                UsedType {
1374                    interface: dep_iface2,
1375                    name: None,
1376                },
1377            );
1378            let interface = Interface {
1379                id: Some("my:pkg/iface@1.0.0".to_string()),
1380                uses,
1381                exports: IndexMap::new(),
1382            };
1383            types2.add_interface(interface)
1384        };
1385
1386        let mut cache = HashSet::new();
1387        let mut checker = SubtypeChecker::new(&mut cache);
1388
1389        let agg = TypeAggregator::new();
1390        let agg = agg
1391            .aggregate(
1392                "my:pkg/iface@1.0.0",
1393                &types1,
1394                ItemKind::Instance(main_iface1),
1395                &mut checker,
1396            )
1397            .unwrap();
1398
1399        // This should fail because dep:pkg/types@0.2.0 and @0.3.0 are incompatible
1400        let result = agg.aggregate(
1401            "my:pkg/iface@1.0.0",
1402            &types2,
1403            ItemKind::Instance(main_iface2),
1404            &mut checker,
1405        );
1406        assert!(result.is_err());
1407        let err = result.unwrap_err().to_string();
1408        assert!(
1409            err.contains("cannot merge used type"),
1410            "unexpected error message: {err}"
1411        );
1412    }
1413
1414    #[test]
1415    fn remap_interface_merges_semver_compatible() {
1416        // First aggregate an interface at @0.2.0
1417        let mut types1 = Types::default();
1418        let func_id1 = make_func_type(&mut types1);
1419        let iface1 = make_interface(
1420            &mut types1,
1421            Some("dep:pkg/iface@0.2.0"),
1422            vec![("do-thing", ItemKind::Func(func_id1))],
1423        );
1424
1425        // Second aggregate a different interface that depends on @0.2.1 of the same
1426        let mut types2 = Types::default();
1427        let func_id2 = make_func_type(&mut types2);
1428        let dep_iface2 = make_interface(
1429            &mut types2,
1430            Some("dep:pkg/iface@0.2.1"),
1431            vec![("do-thing", ItemKind::Func(func_id2))],
1432        );
1433        let wrapper = {
1434            let mut exports = IndexMap::new();
1435            exports.insert("dep".to_string(), ItemKind::Instance(dep_iface2));
1436            let interface = Interface {
1437                id: Some("wrap:pkg/wrapper@1.0.0".to_string()),
1438                uses: IndexMap::new(),
1439                exports,
1440            };
1441            types2.add_interface(interface)
1442        };
1443
1444        let mut cache = HashSet::new();
1445        let mut checker = SubtypeChecker::new(&mut cache);
1446
1447        let agg = TypeAggregator::new();
1448        // First, aggregate the dep interface directly
1449        let agg = agg
1450            .aggregate(
1451                "dep:pkg/iface@0.2.0",
1452                &types1,
1453                ItemKind::Instance(iface1),
1454                &mut checker,
1455            )
1456            .unwrap();
1457
1458        // Then aggregate a wrapper that references a compatible version.
1459        // The remap_interface call inside should find and merge with the existing one.
1460        let _agg = agg
1461            .aggregate(
1462                "wrap:pkg/wrapper@1.0.0",
1463                &types2,
1464                ItemKind::Instance(wrapper),
1465                &mut checker,
1466            )
1467            .unwrap();
1468    }
1469}