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#[derive(Default, Debug)]
23pub struct TypeAggregator {
24 types: Types,
26 imports: IndexMap<String, ItemKind>,
28 remapped: HashMap<Type, Type>,
30 interfaces: HashMap<String, InterfaceId>,
32 name_redirects: HashMap<String, String>,
35}
36
37impl TypeAggregator {
38 pub fn new() -> Self {
40 Self::default()
41 }
42
43 pub fn types(&self) -> &Types {
45 &self.types
46 }
47
48 pub fn imports(&self) -> impl Iterator<Item = (&str, ItemKind)> {
50 self.imports.iter().map(|(n, k)| (n.as_str(), *k))
51 }
52
53 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 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 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 pub fn aggregate(
97 mut self,
98 name: &str,
99 types: &Types,
100 kind: ItemKind,
101 checker: &mut SubtypeChecker,
102 ) -> Result<Self> {
103 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 if let Some((existing_name, existing_kind)) = self.find_semver_compatible_import(name) {
113 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 let merged_kind = self.imports.shift_remove(&existing_name).unwrap();
122 self.imports.insert(name.to_string(), merged_kind);
123 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 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 self.merge_interface_used_types(existing, types, id, checker)?;
189
190 for (name, source_kind) in &types[id].exports {
192 if let Some(target_kind) = self.types[existing].exports.get(name).copied() {
193 if checker
195 .is_subtype(*source_kind, types, target_kind, &self.types)
196 .is_ok()
197 {
198 self.remapped.insert(source_kind.ty(), target_kind.ty());
201 continue;
202 }
203
204 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 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 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 if existing.name != used.name {
246 bail!("cannot merge used type `{name}` as the export names are mismatched");
247 }
248 }
249
250 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 self.merge_world_used_types(existing, types, id, checker)?;
283
284 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 checker
290 .is_subtype(target_kind, &self.types, *source_kind, types)
291 .is_ok()
292 {
293 continue;
294 }
295
296 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 for (name, source_kind) in &types[id].exports {
311 if let Some(target_kind) = self.types[existing].exports.get(name).copied() {
312 if checker
314 .is_subtype(*source_kind, types, target_kind, &self.types)
315 .is_ok()
316 {
317 continue;
318 }
319
320 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 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 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 if existing.name != used.name {
362 bail!("cannot merge used type `{name}` as the export names are mismatched");
363 }
364 }
365
366 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 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 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 checker
429 .core_extern(target_extern, &self.types, source_extern, types)
430 .is_ok()
431 {
432 continue;
433 }
434
435 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 for (name, source_extern) in &types[id].exports {
457 if let Some(target_extern) = self.types[existing].exports.get(name) {
458 if checker
461 .core_extern(source_extern, types, target_extern, &self.types)
462 .is_ok()
463 {
464 continue;
465 }
466
467 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 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 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 self.remap_interface(types, id, checker)
628 })
629 .transpose()?;
630 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 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 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 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 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 assert!(are_semver_compatible("a:b/c", "a:b/c"));
999 assert!(!are_semver_compatible("a:b/c", "a:b/d"));
1001 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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}