1use crate::{
2 CoreExtern, CoreFuncType, DefinedType, DefinedTypeId, Enum, Flags, FuncTypeId, InterfaceId,
3 ItemKind, ModuleTypeId, PrimitiveType, Record, ResourceId, Type, Types, ValueType, Variant,
4 WorldId,
5};
6use anyhow::{bail, Context, Result};
7use indexmap::IndexMap;
8use std::collections::HashSet;
9
10#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
12pub enum SubtypeCheck {
13 Covariant,
15 Contravariant,
17}
18
19pub struct SubtypeChecker<'a> {
23 kinds: Vec<SubtypeCheck>,
24 cache: &'a mut HashSet<(ItemKind, ItemKind)>,
25}
26
27impl<'a> SubtypeChecker<'a> {
28 pub fn new(cache: &'a mut HashSet<(ItemKind, ItemKind)>) -> Self {
30 Self {
31 kinds: Default::default(),
32 cache,
33 }
34 }
35
36 fn kind(&self) -> SubtypeCheck {
37 self.kinds
38 .last()
39 .copied()
40 .unwrap_or(SubtypeCheck::Covariant)
41 }
42
43 pub fn is_subtype(&mut self, a: ItemKind, at: &Types, b: ItemKind, bt: &Types) -> Result<()> {
45 if self.cache.contains(&(a, b)) {
46 return Ok(());
47 }
48
49 let result = self.is_subtype_(a, at, b, bt);
50 if result.is_ok() {
51 self.cache.insert((a, b));
52 }
53
54 result
55 }
56
57 pub fn invert(&mut self) -> SubtypeCheck {
61 let prev = self.kind();
62 self.kinds.push(match prev {
63 SubtypeCheck::Covariant => SubtypeCheck::Contravariant,
64 SubtypeCheck::Contravariant => SubtypeCheck::Covariant,
65 });
66 prev
67 }
68
69 pub fn revert(&mut self) {
71 self.kinds.pop().expect("mismatched stack");
72 }
73
74 fn is_subtype_(&mut self, a: ItemKind, at: &Types, b: ItemKind, bt: &Types) -> Result<()> {
75 match (a, b) {
76 (ItemKind::Type(a), ItemKind::Type(b)) => self.ty(a, at, b, bt),
77 (ItemKind::Func(a), ItemKind::Func(b)) => self.func(a, at, b, bt),
78 (ItemKind::Instance(a), ItemKind::Instance(b)) => self.interface(a, at, b, bt),
79 (ItemKind::Component(a), ItemKind::Component(b)) => self.world(a, at, b, bt),
80 (ItemKind::Module(a), ItemKind::Module(b)) => self.module(a, at, b, bt),
81 (ItemKind::Value(a), ItemKind::Value(b)) => self.value_type(a, at, b, bt),
82
83 (ItemKind::Type(_), _)
84 | (ItemKind::Func(_), _)
85 | (ItemKind::Instance(_), _)
86 | (ItemKind::Component(_), _)
87 | (ItemKind::Module(_), _)
88 | (ItemKind::Value(_), _) => {
89 let (expected, expected_types, found, found_types) =
90 self.expected_found(&a, at, &b, bt);
91 bail!(
92 "expected {expected}, found {found}",
93 expected = expected.desc(expected_types),
94 found = found.desc(found_types)
95 )
96 }
97 }
98 }
99
100 fn expected_found<'b, T>(
101 &self,
102 a: &'b T,
103 at: &'b Types,
104 b: &'b T,
105 bt: &'b Types,
106 ) -> (&'b T, &'b Types, &'b T, &'b Types) {
107 match self.kind() {
108 SubtypeCheck::Covariant => (b, bt, a, at),
110 SubtypeCheck::Contravariant => (a, at, b, bt),
112 }
113 }
114
115 fn resource(&self, a: ResourceId, at: &Types, b: ResourceId, bt: &Types) -> Result<()> {
116 if a == b {
117 return Ok(());
118 }
119
120 let a = &at[at.resolve_resource(a)];
121 let b = &bt[bt.resolve_resource(b)];
122 if a.name != b.name {
123 let (expected, _, found, _) = self.expected_found(a, at, b, bt);
124
125 bail!(
126 "expected resource `{expected}`, found resource `{found}`",
127 expected = expected.name,
128 found = found.name
129 );
130 }
131
132 Ok(())
133 }
134
135 fn ty(&mut self, a: Type, at: &Types, b: Type, bt: &Types) -> Result<()> {
136 match (a, b) {
137 (Type::Resource(a), Type::Resource(b)) => self.resource(a, at, b, bt),
138 (Type::Func(a), Type::Func(b)) => self.func(a, at, b, bt),
139 (Type::Value(a), Type::Value(b)) => self.value_type(a, at, b, bt),
140 (Type::Interface(a), Type::Interface(b)) => self.interface(a, at, b, bt),
141 (Type::World(a), Type::World(b)) => self.world(a, at, b, bt),
142 (Type::Module(a), Type::Module(b)) => self.module(a, at, b, bt),
143
144 (Type::Func(_), _)
145 | (Type::Resource(_), _)
146 | (Type::Value(_), _)
147 | (Type::Interface(_), _)
148 | (Type::World(_), _)
149 | (Type::Module(_), _) => {
150 let (expected, expected_types, found, found_types) =
151 self.expected_found(&a, at, &b, bt);
152
153 bail!(
154 "expected {expected}, found {found}",
155 expected = expected.desc(expected_types),
156 found = found.desc(found_types)
157 )
158 }
159 }
160 }
161
162 fn func(&self, a: FuncTypeId, at: &Types, b: FuncTypeId, bt: &Types) -> Result<()> {
163 if a == b {
164 return Ok(());
165 }
166
167 let a = &at[a];
168 let b = &bt[b];
169
170 if a.is_async != b.is_async {
175 let (expected, _, found, _) = self.expected_found(a, at, b, bt);
176 bail!(
177 "expected {} function, found {} function",
178 if expected.is_async { "async" } else { "sync" },
179 if found.is_async { "async" } else { "sync" },
180 );
181 }
182
183 if a.params.len() != b.params.len() {
184 let (expected, _, found, _) = self.expected_found(a, at, b, bt);
185 bail!(
186 "expected function with parameter count {expected}, found parameter count {found}",
187 expected = expected.params.len(),
188 found = found.params.len(),
189 );
190 }
191
192 for (i, ((an, a), (bn, b))) in a.params.iter().zip(b.params.iter()).enumerate() {
193 if an != bn {
194 let (expected, _, found, _) = self.expected_found(an, at, bn, bt);
195 bail!("expected function parameter {i} to be named `{expected}`, found name `{found}`");
196 }
197
198 self.value_type(*a, at, *b, bt)
199 .with_context(|| format!("mismatched type for function parameter `{bn}`"))?;
200 }
201
202 match (&a.result, &b.result) {
203 (None, None) => return Ok(()),
204 (Some(a), Some(b)) => {
205 return self
206 .value_type(*a, at, *b, bt)
207 .context("mismatched type for function result");
208 }
209 (None, _) | (Some(_), _) => {
210 }
212 }
213
214 let (expected, _, found, _) = self.expected_found(a, at, b, bt);
215 match (&expected.result, &found.result) {
216 (Some(_), None) => {
217 bail!("expected function with a result, found function without a result")
218 }
219 (None, Some(_)) => {
220 bail!("expected function without a result, found function with a result")
221 }
222 (Some(_), Some(_)) | (None, None) => panic!("should already be handled"),
223 }
224 }
225
226 fn instance_exports(
227 &mut self,
228 a: &IndexMap<String, ItemKind>,
229 at: &Types,
230 b: &IndexMap<String, ItemKind>,
231 bt: &Types,
232 ) -> Result<()> {
233 for (k, b) in b.iter() {
238 match a.get(k) {
239 Some(a) => {
240 self.is_subtype(*a, at, *b, bt)
241 .with_context(|| format!("mismatched type for export `{k}`"))?;
242 }
243 None => match self.kind() {
244 SubtypeCheck::Covariant => {
245 bail!(
246 "instance is missing expected {kind} export `{k}`",
247 kind = b.desc(bt)
248 )
249 }
250 SubtypeCheck::Contravariant => {
251 bail!(
252 "instance has unexpected {kind} export `{k}`",
253 kind = b.desc(bt)
254 )
255 }
256 },
257 }
258 }
259
260 Ok(())
261 }
262
263 fn interface(&mut self, a: InterfaceId, at: &Types, b: InterfaceId, bt: &Types) -> Result<()> {
264 if a == b {
265 return Ok(());
266 }
267
268 let a = &at[a];
269 let b = &bt[b];
270 self.instance_exports(&a.exports, at, &b.exports, bt)
271 }
272
273 fn world(&mut self, a: WorldId, at: &Types, b: WorldId, bt: &Types) -> Result<()> {
274 let a = &at[a];
275 let b = &bt[b];
276
277 let prev = self.invert();
283 for (k, a) in a.imports.iter() {
284 match b.imports.get(k) {
285 Some(b) => {
286 self.is_subtype(*b, bt, *a, at)
287 .with_context(|| format!("mismatched type for import `{k}`"))?;
288 }
289 None => match prev {
290 SubtypeCheck::Covariant => {
291 bail!(
292 "component is missing expected {kind} import `{k}`",
293 kind = a.desc(at)
294 )
295 }
296 SubtypeCheck::Contravariant => {
297 bail!(
298 "component has unexpected import {kind} `{k}`",
299 kind = a.desc(at)
300 )
301 }
302 },
303 }
304 }
305
306 self.revert();
307
308 for (k, b) in b.exports.iter() {
309 match a.exports.get(k) {
310 Some(a) => {
311 self.is_subtype(*a, at, *b, bt)
312 .with_context(|| format!("mismatched type for export `{k}`"))?;
313 }
314 None => match self.kind() {
315 SubtypeCheck::Covariant => {
316 bail!(
317 "component is missing expected {kind} export `{k}`",
318 kind = b.desc(bt)
319 )
320 }
321 SubtypeCheck::Contravariant => {
322 bail!(
323 "component has unexpected {kind} export `{k}`",
324 kind = b.desc(bt)
325 )
326 }
327 },
328 }
329 }
330
331 Ok(())
332 }
333
334 fn module(&mut self, a: ModuleTypeId, at: &Types, b: ModuleTypeId, bt: &Types) -> Result<()> {
335 if a == b {
336 return Ok(());
337 }
338
339 let a = &at[a];
340 let b = &bt[b];
341
342 let prev = self.invert();
348 for (k, a) in a.imports.iter() {
349 match b.imports.get(k) {
350 Some(b) => {
351 self.core_extern(b, bt, a, at).with_context(|| {
352 format!("mismatched type for import `{m}::{n}`", m = k.0, n = k.1)
353 })?;
354 }
355 None => match prev {
356 SubtypeCheck::Covariant => bail!(
357 "module is missing expected {a} import `{m}::{n}`",
358 m = k.0,
359 n = k.1
360 ),
361 SubtypeCheck::Contravariant => {
362 bail!(
363 "module has unexpected {a} import `{m}::{n}`",
364 m = k.0,
365 n = k.1
366 )
367 }
368 },
369 }
370 }
371
372 self.revert();
373
374 for (k, b) in b.exports.iter() {
375 match a.exports.get(k) {
376 Some(a) => {
377 self.kinds.push(SubtypeCheck::Covariant);
378 let r = self
379 .core_extern(a, at, b, bt)
380 .with_context(|| format!("mismatched type for export `{k}`"));
381 self.kinds.pop();
382 r?;
383 }
384 None => match self.kind() {
385 SubtypeCheck::Covariant => {
386 bail!("module is missing expected {b} export `{k}`")
387 }
388 SubtypeCheck::Contravariant => {
389 bail!("module has unexpected {b} export `{k}`")
390 }
391 },
392 }
393 }
394
395 Ok(())
396 }
397
398 pub(crate) fn core_extern(
399 &self,
400 a: &CoreExtern,
401 at: &Types,
402 b: &CoreExtern,
403 bt: &Types,
404 ) -> Result<()> {
405 macro_rules! limits_match {
406 ($ai:expr, $am:expr, $bi:expr, $bm:expr) => {{
407 $ai >= $bi
408 && match ($am, $bm) {
409 (Some(am), Some(bm)) => am <= bm,
410 (None, Some(_)) => false,
411 _ => true,
412 }
413 }};
414 }
415
416 match (a, b) {
417 (CoreExtern::Func(a), CoreExtern::Func(b)) => self.core_func(a, at, b, bt),
418 (
419 CoreExtern::Table {
420 element_type: ae,
421 initial: ai,
422 maximum: am,
423 table64: a64,
424 shared: ashared,
425 },
426 CoreExtern::Table {
427 element_type: be,
428 initial: bi,
429 maximum: bm,
430 table64: b64,
431 shared: bshared,
432 },
433 ) => {
434 if ae != be {
435 let (expected, _, found, _) = self.expected_found(ae, at, be, bt);
436 bail!("expected table element type {expected}, found {found}");
437 }
438
439 if !limits_match!(ai, am, bi, bm) {
440 bail!("mismatched table limits");
441 }
442
443 if a64 != b64 {
444 bail!("mismatched table64 flag for tables");
445 }
446
447 if ashared != bshared {
448 bail!("mismatched shared flag for tables");
449 }
450
451 Ok(())
452 }
453 (
454 CoreExtern::Memory {
455 memory64: a64,
456 shared: ashared,
457 initial: ai,
458 maximum: am,
459 page_size_log2: apsl,
460 },
461 CoreExtern::Memory {
462 memory64: b64,
463 shared: bshared,
464 initial: bi,
465 maximum: bm,
466 page_size_log2: bpsl,
467 },
468 ) => {
469 if ashared != bshared {
470 bail!("mismatched shared flag for memories");
471 }
472
473 if a64 != b64 {
474 bail!("mismatched memory64 flag for memories");
475 }
476
477 if !limits_match!(ai, am, bi, bm) {
478 bail!("mismatched memory limits");
479 }
480
481 if apsl != bpsl {
482 bail!("mismatched page_size_log2 for memories");
483 }
484
485 Ok(())
486 }
487 (
488 CoreExtern::Global {
489 val_type: avt,
490 mutable: am,
491 shared: ashared,
492 },
493 CoreExtern::Global {
494 val_type: bvt,
495 mutable: bm,
496 shared: bshared,
497 },
498 ) => {
499 if am != bm {
500 bail!("mismatched mutable flag for globals");
501 }
502
503 if avt != bvt {
504 let (expected, _, found, _) = self.expected_found(avt, at, bvt, bt);
505 bail!("expected global type {expected}, found {found}");
506 }
507
508 if ashared != bshared {
509 bail!("mismatched shared flag for globals");
510 }
511
512 Ok(())
513 }
514 (CoreExtern::Tag(a), CoreExtern::Tag(b)) => self.core_func(a, at, b, bt),
515
516 (CoreExtern::Func(_), _)
517 | (CoreExtern::Table { .. }, _)
518 | (CoreExtern::Memory { .. }, _)
519 | (CoreExtern::Global { .. }, _)
520 | (CoreExtern::Tag(_), _) => {
521 let (expected, _, found, _) = self.expected_found(a, at, b, bt);
522 bail!("expected {expected}, found {found}");
523 }
524 }
525 }
526
527 fn core_func(&self, a: &CoreFuncType, at: &Types, b: &CoreFuncType, bt: &Types) -> Result<()> {
528 if a != b {
529 let (expected, _, found, _) = self.expected_found(a, at, b, bt);
530 bail!("expected {expected}, found {found}");
531 }
532
533 Ok(())
534 }
535
536 fn value_type(&self, a: ValueType, at: &Types, b: ValueType, bt: &Types) -> Result<()> {
537 let a = at.resolve_value_type(a);
538 let b = bt.resolve_value_type(b);
539
540 match (a, b) {
541 (ValueType::Primitive(a), ValueType::Primitive(b)) => self.primitive(a, at, b, bt),
542 (ValueType::Defined(a), ValueType::Defined(b)) => self.defined_type(a, at, b, bt),
543 (ValueType::Borrow(a), ValueType::Borrow(b))
544 | (ValueType::Own(a), ValueType::Own(b)) => self.resource(a, at, b, bt),
545
546 (ValueType::Primitive(_), _)
547 | (ValueType::Defined(_), _)
548 | (ValueType::Borrow(_), _)
549 | (ValueType::Own(_), _) => {
550 let (expected, expected_types, found, found_types) =
551 self.expected_found(&a, at, &b, bt);
552 bail!(
553 "expected {expected}, found {found}",
554 expected = expected.desc(expected_types),
555 found = found.desc(found_types)
556 )
557 }
558 }
559 }
560
561 fn defined_type(
562 &self,
563 a: DefinedTypeId,
564 at: &Types,
565 b: DefinedTypeId,
566 bt: &Types,
567 ) -> std::result::Result<(), anyhow::Error> {
568 if a == b {
569 return Ok(());
570 }
571
572 let a = &at[a];
573 let b = &bt[b];
574 match (a, b) {
575 (DefinedType::Tuple(a), DefinedType::Tuple(b)) => self.tuple(a, at, b, bt),
576 (DefinedType::List(a), DefinedType::List(b)) => self
577 .value_type(*a, at, *b, bt)
578 .context("mismatched type for list element"),
579 (DefinedType::FixedSizeList(a, asize), DefinedType::FixedSizeList(b, bsize)) => {
580 if asize != bsize {
581 bail!("mismatched size for fixed size list element");
582 }
583 self.value_type(*a, at, *b, bt)
584 .context("mismatched type for fixed size list element")
585 }
586 (DefinedType::Future(a), DefinedType::Future(b)) => self
587 .payload(*a, at, *b, bt)
588 .context("mismatched type for future payload"),
589 (DefinedType::Stream(a), DefinedType::Stream(b)) => self
590 .payload(*a, at, *b, bt)
591 .context("mismatched type for stream payload"),
592 (DefinedType::Option(a), DefinedType::Option(b)) => self
593 .value_type(*a, at, *b, bt)
594 .context("mismatched type for option"),
595 (
596 DefinedType::Result {
597 ok: a_ok,
598 err: a_err,
599 },
600 DefinedType::Result {
601 ok: b_ok,
602 err: b_err,
603 },
604 ) => {
605 self.result("ok", a_ok, at, b_ok, bt)?;
606 self.result("err", a_err, at, b_err, bt)
607 }
608 (DefinedType::Variant(a), DefinedType::Variant(b)) => self.variant(a, at, b, bt),
609 (DefinedType::Record(a), DefinedType::Record(b)) => self.record(a, at, b, bt),
610 (DefinedType::Flags(a), DefinedType::Flags(b)) => self.flags(a, at, b, bt),
611 (DefinedType::Enum(a), DefinedType::Enum(b)) => self.enum_type(a, at, b, bt),
612 (DefinedType::Alias(_), _) | (_, DefinedType::Alias(_)) => {
613 panic!("aliases should have been resolved")
614 }
615
616 (DefinedType::Tuple(_), _)
617 | (DefinedType::List(_), _)
618 | (DefinedType::FixedSizeList(_, _), _)
619 | (DefinedType::Option(_), _)
620 | (DefinedType::Result { .. }, _)
621 | (DefinedType::Variant(_), _)
622 | (DefinedType::Record(_), _)
623 | (DefinedType::Flags(_), _)
624 | (DefinedType::Enum(_), _)
625 | (DefinedType::Stream(_), _)
626 | (DefinedType::Future(_), _) => {
627 let (expected, expected_types, found, found_types) =
628 self.expected_found(a, at, b, bt);
629 bail!(
630 "expected {expected}, found {found}",
631 expected = expected.desc(expected_types),
632 found = found.desc(found_types)
633 )
634 }
635 }
636 }
637
638 fn result(
639 &self,
640 desc: &str,
641 a: &Option<ValueType>,
642 at: &Types,
643 b: &Option<ValueType>,
644 bt: &Types,
645 ) -> Result<()> {
646 match (a, b) {
647 (None, None) => return Ok(()),
648 (Some(a), Some(b)) => {
649 return self
650 .value_type(*a, at, *b, bt)
651 .with_context(|| format!("mismatched type for result `{desc}`"))
652 }
653 (Some(_), None) | (None, Some(_)) => {
654 }
656 }
657
658 let (expected, _, found, _) = self.expected_found(a, at, b, bt);
659 match (expected, found) {
660 (Some(_), None) => bail!("expected an `{desc}` for result type"),
661 (None, Some(_)) => bail!("expected no `{desc}` for result type"),
662 (None, None) | (Some(_), Some(_)) => panic!("expected to be handled"),
663 }
664 }
665
666 fn enum_type(&self, a: &Enum, at: &Types, b: &Enum, bt: &Types) -> Result<()> {
667 if a.0.len() != b.0.len() {
668 let (expected, _, found, _) = self.expected_found(a, at, b, bt);
669 bail!(
670 "expected an enum type case count of {expected}, found a count of {found}",
671 expected = expected.0.len(),
672 found = found.0.len()
673 );
674 }
675
676 if let Some((index, (a, b))) =
677 a.0.iter()
678 .zip(b.0.iter())
679 .enumerate()
680 .find(|(_, (a, b))| a != b)
681 {
682 let (expected, _, found, _) = self.expected_found(a, at, b, bt);
683 bail!("expected enum case {index} to be named `{expected}`, found an enum case named `{found}`");
684 }
685
686 Ok(())
687 }
688
689 fn flags(&self, a: &Flags, at: &Types, b: &Flags, bt: &Types) -> Result<()> {
690 if a.0.len() != b.0.len() {
691 let (expected, _, found, _) = self.expected_found(a, at, b, bt);
692 bail!(
693 "expected a flags type flag count of {expected}, found a count of {found}",
694 expected = expected.0.len(),
695 found = found.0.len()
696 );
697 }
698
699 if let Some((index, (a, b))) =
700 a.0.iter()
701 .zip(b.0.iter())
702 .enumerate()
703 .find(|(_, (a, b))| a != b)
704 {
705 let (expected, _, found, _) = self.expected_found(a, at, b, bt);
706 bail!("expected flag {index} to be named `{expected}`, found a flag named `{found}`");
707 }
708
709 Ok(())
710 }
711
712 fn record(&self, a: &Record, at: &Types, b: &Record, bt: &Types) -> Result<()> {
713 if a.fields.len() != b.fields.len() {
714 let (expected, _, found, _) = self.expected_found(a, at, b, bt);
715 bail!(
716 "expected a record field count of {expected}, found a count of {found}",
717 expected = expected.fields.len(),
718 found = found.fields.len()
719 );
720 }
721
722 for (i, ((an, a), (bn, b))) in a.fields.iter().zip(b.fields.iter()).enumerate() {
723 if an != bn {
724 let (expected, _, found, _) = self.expected_found(an, at, bn, bt);
725 bail!("expected record field {i} to be named `{expected}`, found a field named `{found}`");
726 }
727
728 self.value_type(*a, at, *b, bt)
729 .with_context(|| format!("mismatched type for record field `{bn}`"))?;
730 }
731
732 Ok(())
733 }
734
735 fn variant(&self, a: &Variant, at: &Types, b: &Variant, bt: &Types) -> Result<()> {
736 if a.cases.len() != b.cases.len() {
737 let (expected, _, found, _) = self.expected_found(a, at, b, bt);
738 bail!(
739 "expected a variant case count of {expected}, found a count of {found}",
740 expected = expected.cases.len(),
741 found = found.cases.len()
742 );
743 }
744
745 for (i, ((an, a), (bn, b))) in a.cases.iter().zip(b.cases.iter()).enumerate() {
746 if an != bn {
747 let (expected, _, found, _) = self.expected_found(an, at, bn, bt);
748 bail!("expected variant case {i} to be named `{expected}`, found a case named `{found}`");
749 }
750
751 match (a, b) {
752 (None, None) => {}
753 (Some(a), Some(b)) => self
754 .value_type(*a, at, *b, bt)
755 .with_context(|| format!("mismatched type for variant case `{bn}`"))?,
756 _ => {
757 let (expected, _, found, _) = self.expected_found(a, at, b, bt);
758 match (expected, found) {
759 (None, Some(_)) => {
760 bail!("expected variant case `{bn}` to be untyped, found a typed case")
761 }
762 (Some(_), None) => {
763 bail!("expected variant case `{bn}` to be typed, found an untyped case")
764 }
765 (None, None) | (Some(_), Some(_)) => panic!("expected to be handled"),
766 }
767 }
768 }
769 }
770
771 Ok(())
772 }
773
774 fn tuple(&self, a: &Vec<ValueType>, at: &Types, b: &Vec<ValueType>, bt: &Types) -> Result<()> {
775 if a.len() != b.len() {
776 let (expected, _, found, _) = self.expected_found(a, at, b, bt);
777 bail!(
778 "expected a tuple of size {expected}, found a tuple of size {found}",
779 expected = expected.len(),
780 found = found.len()
781 );
782 }
783
784 for (i, (a, b)) in a.iter().zip(b.iter()).enumerate() {
785 self.value_type(*a, at, *b, bt)
786 .with_context(|| format!("mismatched type for tuple item {i}"))?;
787 }
788
789 Ok(())
790 }
791
792 fn payload(
793 &self,
794 a: Option<ValueType>,
795 at: &Types,
796 b: Option<ValueType>,
797 bt: &Types,
798 ) -> Result<()> {
799 match (a, b) {
800 (Some(a), Some(b)) => self.value_type(a, at, b, bt),
801 (None, None) => Ok(()),
802 (Some(_), None) => bail!("expected a type payload, found none"),
803 (None, Some(_)) => bail!("expected no type payload, found one"),
804 }
805 }
806
807 fn primitive(&self, a: PrimitiveType, at: &Types, b: PrimitiveType, bt: &Types) -> Result<()> {
808 if a != b {
812 let (expected, _, found, _) = self.expected_found(&a, at, &b, bt);
813 bail!(
814 "expected {expected}, found {found}",
815 expected = expected.desc(),
816 found = found.desc()
817 );
818 }
819
820 Ok(())
821 }
822}
823
824#[cfg(test)]
825mod tests {
826 use super::*;
827 use crate::{CoreRefType, CoreType, HeapType};
828
829 fn check_core_extern(a: &CoreExtern, b: &CoreExtern) -> Result<()> {
830 let types = Types::default();
831 let mut cache = HashSet::new();
832 let checker = SubtypeChecker::new(&mut cache);
833 checker.core_extern(a, &types, b, &types)
834 }
835
836 fn base_table() -> CoreExtern {
837 CoreExtern::Table {
838 element_type: CoreRefType {
839 nullable: true,
840 heap_type: HeapType::Func,
841 },
842 initial: 1,
843 maximum: None,
844 table64: false,
845 shared: false,
846 }
847 }
848
849 #[test]
850 fn mismatched_table64_is_rejected() {
851 let a = base_table();
852 let mut b = base_table();
853 if let CoreExtern::Table { table64, .. } = &mut b {
854 *table64 = true;
855 }
856 assert!(
857 check_core_extern(&a, &b).is_err(),
858 "mismatched table64 should be rejected"
859 );
860 }
861
862 #[test]
863 fn mismatched_table_shared_is_rejected() {
864 let a = base_table();
865 let mut b = base_table();
866 if let CoreExtern::Table { shared, .. } = &mut b {
867 *shared = true;
868 }
869 assert!(
870 check_core_extern(&a, &b).is_err(),
871 "mismatched table shared should be rejected"
872 );
873 }
874
875 #[test]
876 fn mismatched_memory_page_size_log2_is_rejected() {
877 let a = CoreExtern::Memory {
878 memory64: false,
879 shared: false,
880 initial: 1,
881 maximum: None,
882 page_size_log2: Some(16),
883 };
884 let b = CoreExtern::Memory {
885 memory64: false,
886 shared: false,
887 initial: 1,
888 maximum: None,
889 page_size_log2: Some(14),
890 };
891 assert!(
892 check_core_extern(&a, &b).is_err(),
893 "mismatched page_size_log2 should be rejected"
894 );
895 }
896
897 #[test]
898 fn mismatched_global_shared_is_rejected() {
899 let a = CoreExtern::Global {
900 val_type: CoreType::I32,
901 mutable: false,
902 shared: false,
903 };
904 let b = CoreExtern::Global {
905 val_type: CoreType::I32,
906 mutable: false,
907 shared: true,
908 };
909 assert!(
910 check_core_extern(&a, &b).is_err(),
911 "mismatched global shared should be rejected"
912 );
913 }
914}