wac_types/
checker.rs

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/// Represents the kind of subtyping check to perform.
11#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
12pub enum SubtypeCheck {
13    /// The type is a covariant check.
14    Covariant,
15    /// The type is a contravariant check.
16    Contravariant,
17}
18
19/// Implements a subtype checker.
20///
21/// Subtype checking is used to type check instantiation arguments.
22pub struct SubtypeChecker<'a> {
23    kinds: Vec<SubtypeCheck>,
24    cache: &'a mut HashSet<(ItemKind, ItemKind)>,
25}
26
27impl<'a> SubtypeChecker<'a> {
28    /// Creates a new subtype checker with the given cache.
29    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    /// Checks if `a` is a subtype of `b`.
44    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    /// Inverts the current subtype check being performed.
58    ///
59    /// Returns the previous subtype check.
60    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    /// Reverts to the previous check kind.
70    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            // For covariant checks, the supertype is the expected type
109            SubtypeCheck::Covariant => (b, bt, a, at),
110            // For contravariant checks, the subtype is the expected type
111            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        // Note: currently subtyping for functions is done in terms of equality
171        // rather than actual subtyping; the reason for this is that implementing
172        // runtimes don't yet support more complex subtyping rules.
173
174        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                // Handle the mismatch below
202            }
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 instance type subtyping, all exports in the other
225        // instance type must be present in this instance type's
226        // exports (i.e. it can export *more* than what this instance
227        // type needs).
228        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        // For component type subtyping, all exports in the other component
269        // type must be present in this component type's exports (i.e. it
270        // can export *more* than what this component type needs).
271        // However, for imports, the check is reversed (i.e. it is okay
272        // to import *less* than what this component type needs).
273        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        // For module type subtyping, all exports in the other module
334        // type must be present in expected module type's exports (i.e. it
335        // can export *more* than what is expected module type needs).
336        // However, for imports, the check is reversed (i.e. it is okay
337        // to import *less* than what this module type needs).
338        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                // Handle mismatch below
630            }
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        // Note: currently subtyping for primitive types is done in terms of equality
784        // rather than actual subtyping; the reason for this is that implementing
785        // runtimes don't yet support more complex subtyping rules.
786        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}