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.params.len() != b.params.len() {
175 let (expected, _, found, _) = self.expected_found(a, at, b, bt);
176 bail!(
177 "expected function with parameter count {expected}, found parameter count {found}",
178 expected = expected.params.len(),
179 found = found.params.len(),
180 );
181 }
182
183 for (i, ((an, a), (bn, b))) in a.params.iter().zip(b.params.iter()).enumerate() {
184 if an != bn {
185 let (expected, _, found, _) = self.expected_found(an, at, bn, bt);
186 bail!("expected function parameter {i} to be named `{expected}`, found name `{found}`");
187 }
188
189 self.value_type(*a, at, *b, bt)
190 .with_context(|| format!("mismatched type for function parameter `{bn}`"))?;
191 }
192
193 match (&a.result, &b.result) {
194 (None, None) => return Ok(()),
195 (Some(a), Some(b)) => {
196 return self
197 .value_type(*a, at, *b, bt)
198 .context("mismatched type for function result");
199 }
200 (None, _) | (Some(_), _) => {
201 }
203 }
204
205 let (expected, _, found, _) = self.expected_found(a, at, b, bt);
206 match (&expected.result, &found.result) {
207 (Some(_), None) => {
208 bail!("expected function with a result, found function without a result")
209 }
210 (None, Some(_)) => {
211 bail!("expected function without a result, found function with a result")
212 }
213 (Some(_), Some(_)) | (None, None) => panic!("should already be handled"),
214 }
215 }
216
217 fn instance_exports(
218 &mut self,
219 a: &IndexMap<String, ItemKind>,
220 at: &Types,
221 b: &IndexMap<String, ItemKind>,
222 bt: &Types,
223 ) -> Result<()> {
224 for (k, b) in b.iter() {
229 match a.get(k) {
230 Some(a) => {
231 self.is_subtype(*a, at, *b, bt)
232 .with_context(|| format!("mismatched type for export `{k}`"))?;
233 }
234 None => match self.kind() {
235 SubtypeCheck::Covariant => {
236 bail!(
237 "instance is missing expected {kind} export `{k}`",
238 kind = b.desc(bt)
239 )
240 }
241 SubtypeCheck::Contravariant => {
242 bail!(
243 "instance has unexpected {kind} export `{k}`",
244 kind = b.desc(bt)
245 )
246 }
247 },
248 }
249 }
250
251 Ok(())
252 }
253
254 fn interface(&mut self, a: InterfaceId, at: &Types, b: InterfaceId, bt: &Types) -> Result<()> {
255 if a == b {
256 return Ok(());
257 }
258
259 let a = &at[a];
260 let b = &bt[b];
261 self.instance_exports(&a.exports, at, &b.exports, bt)
262 }
263
264 fn world(&mut self, a: WorldId, at: &Types, b: WorldId, bt: &Types) -> Result<()> {
265 let a = &at[a];
266 let b = &bt[b];
267
268 let prev = self.invert();
274 for (k, a) in a.imports.iter() {
275 match b.imports.get(k) {
276 Some(b) => {
277 self.is_subtype(*b, bt, *a, at)
278 .with_context(|| format!("mismatched type for import `{k}`"))?;
279 }
280 None => match prev {
281 SubtypeCheck::Covariant => {
282 bail!(
283 "component is missing expected {kind} import `{k}`",
284 kind = a.desc(at)
285 )
286 }
287 SubtypeCheck::Contravariant => {
288 bail!(
289 "component has unexpected import {kind} `{k}`",
290 kind = a.desc(at)
291 )
292 }
293 },
294 }
295 }
296
297 self.revert();
298
299 for (k, b) in b.exports.iter() {
300 match a.exports.get(k) {
301 Some(a) => {
302 self.is_subtype(*a, at, *b, bt)
303 .with_context(|| format!("mismatched type for export `{k}`"))?;
304 }
305 None => match self.kind() {
306 SubtypeCheck::Covariant => {
307 bail!(
308 "component is missing expected {kind} export `{k}`",
309 kind = b.desc(bt)
310 )
311 }
312 SubtypeCheck::Contravariant => {
313 bail!(
314 "component has unexpected {kind} export `{k}`",
315 kind = b.desc(bt)
316 )
317 }
318 },
319 }
320 }
321
322 Ok(())
323 }
324
325 fn module(&mut self, a: ModuleTypeId, at: &Types, b: ModuleTypeId, bt: &Types) -> Result<()> {
326 if a == b {
327 return Ok(());
328 }
329
330 let a = &at[a];
331 let b = &bt[b];
332
333 let prev = self.invert();
339 for (k, a) in a.imports.iter() {
340 match b.imports.get(k) {
341 Some(b) => {
342 self.core_extern(b, bt, a, at).with_context(|| {
343 format!("mismatched type for import `{m}::{n}`", m = k.0, n = k.1)
344 })?;
345 }
346 None => match prev {
347 SubtypeCheck::Covariant => bail!(
348 "module is missing expected {a} import `{m}::{n}`",
349 m = k.0,
350 n = k.1
351 ),
352 SubtypeCheck::Contravariant => {
353 bail!(
354 "module has unexpected {a} import `{m}::{n}`",
355 m = k.0,
356 n = k.1
357 )
358 }
359 },
360 }
361 }
362
363 self.revert();
364
365 for (k, b) in b.exports.iter() {
366 match a.exports.get(k) {
367 Some(a) => {
368 self.kinds.push(SubtypeCheck::Covariant);
369 let r = self
370 .core_extern(a, at, b, bt)
371 .with_context(|| format!("mismatched type for export `{k}`"));
372 self.kinds.pop();
373 r?;
374 }
375 None => match self.kind() {
376 SubtypeCheck::Covariant => {
377 bail!("module is missing expected {b} export `{k}`")
378 }
379 SubtypeCheck::Contravariant => {
380 bail!("module has unexpected {b} export `{k}`")
381 }
382 },
383 }
384 }
385
386 Ok(())
387 }
388
389 pub(crate) fn core_extern(
390 &self,
391 a: &CoreExtern,
392 at: &Types,
393 b: &CoreExtern,
394 bt: &Types,
395 ) -> Result<()> {
396 macro_rules! limits_match {
397 ($ai:expr, $am:expr, $bi:expr, $bm:expr) => {{
398 $ai >= $bi
399 && match ($am, $bm) {
400 (Some(am), Some(bm)) => am <= bm,
401 (None, Some(_)) => false,
402 _ => true,
403 }
404 }};
405 }
406
407 match (a, b) {
408 (CoreExtern::Func(a), CoreExtern::Func(b)) => self.core_func(a, at, b, bt),
409 (
410 CoreExtern::Table {
411 element_type: ae,
412 initial: ai,
413 maximum: am,
414 table64: _a64,
415 shared: _ashared,
416 },
417 CoreExtern::Table {
418 element_type: be,
419 initial: bi,
420 maximum: bm,
421 table64: _b64,
422 shared: _bshared,
423 },
424 ) => {
425 if ae != be {
426 let (expected, _, found, _) = self.expected_found(ae, at, be, bt);
427 bail!("expected table element type {expected}, found {found}");
428 }
429
430 if !limits_match!(ai, am, bi, bm) {
431 bail!("mismatched table limits");
432 }
433
434 Ok(())
435 }
436 (
437 CoreExtern::Memory {
438 memory64: a64,
439 shared: ashared,
440 initial: ai,
441 maximum: am,
442 page_size_log2: _apsl,
443 },
444 CoreExtern::Memory {
445 memory64: b64,
446 shared: bshared,
447 initial: bi,
448 maximum: bm,
449 page_size_log2: _bpsl,
450 },
451 ) => {
452 if ashared != bshared {
453 bail!("mismatched shared flag for memories");
454 }
455
456 if a64 != b64 {
457 bail!("mismatched memory64 flag for memories");
458 }
459
460 if !limits_match!(ai, am, bi, bm) {
461 bail!("mismatched memory limits");
462 }
463
464 Ok(())
465 }
466 (
467 CoreExtern::Global {
468 val_type: avt,
469 mutable: am,
470 shared: _ashared,
471 },
472 CoreExtern::Global {
473 val_type: bvt,
474 mutable: bm,
475 shared: _bshared,
476 },
477 ) => {
478 if am != bm {
479 bail!("mismatched mutable flag for globals");
480 }
481
482 if avt != bvt {
483 let (expected, _, found, _) = self.expected_found(avt, at, bvt, bt);
484 bail!("expected global type {expected}, found {found}");
485 }
486
487 Ok(())
488 }
489 (CoreExtern::Tag(a), CoreExtern::Tag(b)) => self.core_func(a, at, b, bt),
490
491 (CoreExtern::Func(_), _)
492 | (CoreExtern::Table { .. }, _)
493 | (CoreExtern::Memory { .. }, _)
494 | (CoreExtern::Global { .. }, _)
495 | (CoreExtern::Tag(_), _) => {
496 let (expected, _, found, _) = self.expected_found(a, at, b, bt);
497 bail!("expected {expected}, found {found}");
498 }
499 }
500 }
501
502 fn core_func(&self, a: &CoreFuncType, at: &Types, b: &CoreFuncType, bt: &Types) -> Result<()> {
503 if a != b {
504 let (expected, _, found, _) = self.expected_found(a, at, b, bt);
505 bail!("expected {expected}, found {found}");
506 }
507
508 Ok(())
509 }
510
511 fn value_type(&self, a: ValueType, at: &Types, b: ValueType, bt: &Types) -> Result<()> {
512 let a = at.resolve_value_type(a);
513 let b = bt.resolve_value_type(b);
514
515 match (a, b) {
516 (ValueType::Primitive(a), ValueType::Primitive(b)) => self.primitive(a, at, b, bt),
517 (ValueType::Defined(a), ValueType::Defined(b)) => self.defined_type(a, at, b, bt),
518 (ValueType::Borrow(a), ValueType::Borrow(b))
519 | (ValueType::Own(a), ValueType::Own(b)) => self.resource(a, at, b, bt),
520
521 (ValueType::Primitive(_), _)
522 | (ValueType::Defined(_), _)
523 | (ValueType::Borrow(_), _)
524 | (ValueType::Own(_), _) => {
525 let (expected, expected_types, found, found_types) =
526 self.expected_found(&a, at, &b, bt);
527 bail!(
528 "expected {expected}, found {found}",
529 expected = expected.desc(expected_types),
530 found = found.desc(found_types)
531 )
532 }
533 }
534 }
535
536 fn defined_type(
537 &self,
538 a: DefinedTypeId,
539 at: &Types,
540 b: DefinedTypeId,
541 bt: &Types,
542 ) -> std::result::Result<(), anyhow::Error> {
543 if a == b {
544 return Ok(());
545 }
546
547 let a = &at[a];
548 let b = &bt[b];
549 match (a, b) {
550 (DefinedType::Tuple(a), DefinedType::Tuple(b)) => self.tuple(a, at, b, bt),
551 (DefinedType::List(a), DefinedType::List(b)) => self
552 .value_type(*a, at, *b, bt)
553 .context("mismatched type for list element"),
554 (DefinedType::FixedSizeList(a, asize), DefinedType::FixedSizeList(b, bsize)) => {
555 if asize != bsize {
556 bail!("mismatched size for fixed size list element");
557 }
558 self.value_type(*a, at, *b, bt)
559 .context("mismatched type for fixed size list element")
560 }
561 (DefinedType::Future(a), DefinedType::Future(b)) => self
562 .payload(*a, at, *b, bt)
563 .context("mismatched type for future payload"),
564 (DefinedType::Stream(a), DefinedType::Stream(b)) => self
565 .payload(*a, at, *b, bt)
566 .context("mismatched type for stream payload"),
567 (DefinedType::Option(a), DefinedType::Option(b)) => self
568 .value_type(*a, at, *b, bt)
569 .context("mismatched type for option"),
570 (
571 DefinedType::Result {
572 ok: a_ok,
573 err: a_err,
574 },
575 DefinedType::Result {
576 ok: b_ok,
577 err: b_err,
578 },
579 ) => {
580 self.result("ok", a_ok, at, b_ok, bt)?;
581 self.result("err", a_err, at, b_err, bt)
582 }
583 (DefinedType::Variant(a), DefinedType::Variant(b)) => self.variant(a, at, b, bt),
584 (DefinedType::Record(a), DefinedType::Record(b)) => self.record(a, at, b, bt),
585 (DefinedType::Flags(a), DefinedType::Flags(b)) => self.flags(a, at, b, bt),
586 (DefinedType::Enum(a), DefinedType::Enum(b)) => self.enum_type(a, at, b, bt),
587 (DefinedType::Alias(_), _) | (_, DefinedType::Alias(_)) => {
588 panic!("aliases should have been resolved")
589 }
590
591 (DefinedType::Tuple(_), _)
592 | (DefinedType::List(_), _)
593 | (DefinedType::FixedSizeList(_, _), _)
594 | (DefinedType::Option(_), _)
595 | (DefinedType::Result { .. }, _)
596 | (DefinedType::Variant(_), _)
597 | (DefinedType::Record(_), _)
598 | (DefinedType::Flags(_), _)
599 | (DefinedType::Enum(_), _)
600 | (DefinedType::Stream(_), _)
601 | (DefinedType::Future(_), _) => {
602 let (expected, expected_types, found, found_types) =
603 self.expected_found(a, at, b, bt);
604 bail!(
605 "expected {expected}, found {found}",
606 expected = expected.desc(expected_types),
607 found = found.desc(found_types)
608 )
609 }
610 }
611 }
612
613 fn result(
614 &self,
615 desc: &str,
616 a: &Option<ValueType>,
617 at: &Types,
618 b: &Option<ValueType>,
619 bt: &Types,
620 ) -> Result<()> {
621 match (a, b) {
622 (None, None) => return Ok(()),
623 (Some(a), Some(b)) => {
624 return self
625 .value_type(*a, at, *b, bt)
626 .with_context(|| format!("mismatched type for result `{desc}`"))
627 }
628 (Some(_), None) | (None, Some(_)) => {
629 }
631 }
632
633 let (expected, _, found, _) = self.expected_found(a, at, b, bt);
634 match (expected, found) {
635 (Some(_), None) => bail!("expected an `{desc}` for result type"),
636 (None, Some(_)) => bail!("expected no `{desc}` for result type"),
637 (None, None) | (Some(_), Some(_)) => panic!("expected to be handled"),
638 }
639 }
640
641 fn enum_type(&self, a: &Enum, at: &Types, b: &Enum, bt: &Types) -> Result<()> {
642 if a.0.len() != b.0.len() {
643 let (expected, _, found, _) = self.expected_found(a, at, b, bt);
644 bail!(
645 "expected an enum type case count of {expected}, found a count of {found}",
646 expected = expected.0.len(),
647 found = found.0.len()
648 );
649 }
650
651 if let Some((index, (a, b))) =
652 a.0.iter()
653 .zip(b.0.iter())
654 .enumerate()
655 .find(|(_, (a, b))| a != b)
656 {
657 let (expected, _, found, _) = self.expected_found(a, at, b, bt);
658 bail!("expected enum case {index} to be named `{expected}`, found an enum case named `{found}`");
659 }
660
661 Ok(())
662 }
663
664 fn flags(&self, a: &Flags, at: &Types, b: &Flags, bt: &Types) -> Result<()> {
665 if a.0.len() != b.0.len() {
666 let (expected, _, found, _) = self.expected_found(a, at, b, bt);
667 bail!(
668 "expected a flags type flag count of {expected}, found a count of {found}",
669 expected = expected.0.len(),
670 found = found.0.len()
671 );
672 }
673
674 if let Some((index, (a, b))) =
675 a.0.iter()
676 .zip(b.0.iter())
677 .enumerate()
678 .find(|(_, (a, b))| a != b)
679 {
680 let (expected, _, found, _) = self.expected_found(a, at, b, bt);
681 bail!("expected flag {index} to be named `{expected}`, found a flag named `{found}`");
682 }
683
684 Ok(())
685 }
686
687 fn record(&self, a: &Record, at: &Types, b: &Record, bt: &Types) -> Result<()> {
688 if a.fields.len() != b.fields.len() {
689 let (expected, _, found, _) = self.expected_found(a, at, b, bt);
690 bail!(
691 "expected a record field count of {expected}, found a count of {found}",
692 expected = expected.fields.len(),
693 found = found.fields.len()
694 );
695 }
696
697 for (i, ((an, a), (bn, b))) in a.fields.iter().zip(b.fields.iter()).enumerate() {
698 if an != bn {
699 let (expected, _, found, _) = self.expected_found(an, at, bn, bt);
700 bail!("expected record field {i} to be named `{expected}`, found a field named `{found}`");
701 }
702
703 self.value_type(*a, at, *b, bt)
704 .with_context(|| format!("mismatched type for record field `{bn}`"))?;
705 }
706
707 Ok(())
708 }
709
710 fn variant(&self, a: &Variant, at: &Types, b: &Variant, bt: &Types) -> Result<()> {
711 if a.cases.len() != b.cases.len() {
712 let (expected, _, found, _) = self.expected_found(a, at, b, bt);
713 bail!(
714 "expected a variant case count of {expected}, found a count of {found}",
715 expected = expected.cases.len(),
716 found = found.cases.len()
717 );
718 }
719
720 for (i, ((an, a), (bn, b))) in a.cases.iter().zip(b.cases.iter()).enumerate() {
721 if an != bn {
722 let (expected, _, found, _) = self.expected_found(an, at, bn, bt);
723 bail!("expected variant case {i} to be named `{expected}`, found a case named `{found}`");
724 }
725
726 match (a, b) {
727 (None, None) => {}
728 (Some(a), Some(b)) => self
729 .value_type(*a, at, *b, bt)
730 .with_context(|| format!("mismatched type for variant case `{bn}`"))?,
731 _ => {
732 let (expected, _, found, _) = self.expected_found(a, at, b, bt);
733 match (expected, found) {
734 (None, Some(_)) => {
735 bail!("expected variant case `{bn}` to be untyped, found a typed case")
736 }
737 (Some(_), None) => {
738 bail!("expected variant case `{bn}` to be typed, found an untyped case")
739 }
740 (None, None) | (Some(_), Some(_)) => panic!("expected to be handled"),
741 }
742 }
743 }
744 }
745
746 Ok(())
747 }
748
749 fn tuple(&self, a: &Vec<ValueType>, at: &Types, b: &Vec<ValueType>, bt: &Types) -> Result<()> {
750 if a.len() != b.len() {
751 let (expected, _, found, _) = self.expected_found(a, at, b, bt);
752 bail!(
753 "expected a tuple of size {expected}, found a tuple of size {found}",
754 expected = expected.len(),
755 found = found.len()
756 );
757 }
758
759 for (i, (a, b)) in a.iter().zip(b.iter()).enumerate() {
760 self.value_type(*a, at, *b, bt)
761 .with_context(|| format!("mismatched type for tuple item {i}"))?;
762 }
763
764 Ok(())
765 }
766
767 fn payload(
768 &self,
769 a: Option<ValueType>,
770 at: &Types,
771 b: Option<ValueType>,
772 bt: &Types,
773 ) -> Result<()> {
774 match (a, b) {
775 (Some(a), Some(b)) => self.value_type(a, at, b, bt),
776 (None, None) => Ok(()),
777 (Some(_), None) => bail!("expected a type payload, found none"),
778 (None, Some(_)) => bail!("expected no type payload, found one"),
779 }
780 }
781
782 fn primitive(&self, a: PrimitiveType, at: &Types, b: PrimitiveType, bt: &Types) -> Result<()> {
783 if a != b {
787 let (expected, _, found, _) = self.expected_found(&a, at, &b, bt);
788 bail!(
789 "expected {expected}, found {found}",
790 expected = expected.desc(),
791 found = found.desc()
792 );
793 }
794
795 Ok(())
796 }
797}