1 use wasmtime::*;
2
field(heap_ty: HeapType) -> FieldType3 fn field(heap_ty: HeapType) -> FieldType {
4 FieldType::new(
5 Mutability::Var,
6 StorageType::ValType(RefType::new(true, heap_ty).into()),
7 )
8 }
9
imm_field(heap_ty: HeapType) -> FieldType10 fn imm_field(heap_ty: HeapType) -> FieldType {
11 FieldType::new(
12 Mutability::Const,
13 StorageType::ValType(RefType::new(true, heap_ty).into()),
14 )
15 }
16
valty(heap_ty: HeapType) -> ValType17 fn valty(heap_ty: HeapType) -> ValType {
18 ValType::Ref(RefType::new(true, heap_ty))
19 }
20
21 #[test]
basic_array_types() -> Result<()>22 fn basic_array_types() -> Result<()> {
23 let engine = Engine::default();
24 for mutability in [Mutability::Const, Mutability::Var] {
25 for storage_ty in [
26 StorageType::I8,
27 StorageType::I16,
28 StorageType::ValType(ValType::I32),
29 StorageType::ValType(RefType::new(true, FuncType::new(&engine, [], []).into()).into()),
30 ] {
31 let field_ty = FieldType::new(mutability, storage_ty.clone());
32 assert_eq!(field_ty.mutability(), mutability);
33 assert!(StorageType::eq(field_ty.element_type(), &storage_ty));
34
35 let array_ty = ArrayType::new(&engine, field_ty.clone());
36 assert!(Engine::same(array_ty.engine(), &engine));
37 assert!(FieldType::eq(&array_ty.field_type(), &field_ty));
38 assert_eq!(array_ty.mutability(), mutability);
39 assert!(StorageType::eq(&array_ty.element_type(), &storage_ty));
40 }
41 }
42 Ok(())
43 }
44
45 #[test]
empty_struct_type() -> Result<()>46 fn empty_struct_type() -> Result<()> {
47 let engine = Engine::default();
48 let struct_ty = StructType::new(&engine, [])?;
49 assert_eq!(struct_ty.fields().len(), 0);
50 assert!(struct_ty.field(0).is_none());
51 Ok(())
52 }
53
54 #[test]
basic_struct_types() -> Result<()>55 fn basic_struct_types() -> Result<()> {
56 let engine = Engine::default();
57
58 let field_types = || {
59 [Mutability::Const, Mutability::Var]
60 .into_iter()
61 .flat_map(|mutability| {
62 [
63 StorageType::I8,
64 StorageType::I16,
65 StorageType::ValType(ValType::I32),
66 StorageType::ValType(
67 RefType::new(true, FuncType::new(&engine, [], []).into()).into(),
68 ),
69 ]
70 .into_iter()
71 .map(move |storage_ty| FieldType::new(mutability, storage_ty))
72 })
73 };
74
75 let struct_ty = StructType::new(&engine, field_types())?;
76
77 assert_eq!(struct_ty.fields().len(), field_types().count());
78 for ((i, expected), actual) in field_types().enumerate().zip(struct_ty.fields()) {
79 assert!(FieldType::eq(&expected, &actual));
80 assert!(FieldType::eq(&expected, &struct_ty.field(i).unwrap()));
81 }
82 assert!(struct_ty.field(struct_ty.fields().len()).is_none());
83
84 Ok(())
85 }
86
87 #[test]
struct_type_matches() -> Result<()>88 fn struct_type_matches() -> Result<()> {
89 let engine = Engine::default();
90
91 let super_ty = StructType::with_finality_and_supertype(
92 &engine,
93 Finality::NonFinal,
94 None,
95 [imm_field(HeapType::Func)],
96 )?;
97
98 // Depth.
99 let sub_ty = StructType::with_finality_and_supertype(
100 &engine,
101 Finality::Final,
102 Some(&super_ty),
103 [imm_field(HeapType::NoFunc)],
104 )?;
105 assert!(sub_ty.matches(&super_ty));
106 let not_sub_ty = StructType::new(&engine, [imm_field(HeapType::NoFunc)])?;
107 assert!(!not_sub_ty.matches(&super_ty));
108
109 // Width.
110 let sub_ty = StructType::with_finality_and_supertype(
111 &engine,
112 Finality::Final,
113 Some(&super_ty),
114 [imm_field(HeapType::Func), imm_field(HeapType::Extern)],
115 )?;
116 assert!(sub_ty.matches(&super_ty));
117 let not_sub_ty = StructType::new(
118 &engine,
119 [imm_field(HeapType::Func), imm_field(HeapType::Extern)],
120 )?;
121 assert!(!not_sub_ty.matches(&super_ty));
122
123 // Depth and width.
124 let sub_ty = StructType::with_finality_and_supertype(
125 &engine,
126 Finality::Final,
127 Some(&super_ty),
128 [imm_field(HeapType::NoFunc), imm_field(HeapType::Extern)],
129 )?;
130 assert!(sub_ty.matches(&super_ty));
131 let not_sub_ty = StructType::new(
132 &engine,
133 [imm_field(HeapType::NoFunc), imm_field(HeapType::Extern)],
134 )?;
135 assert!(!not_sub_ty.matches(&super_ty));
136
137 // Unrelated structs.
138 let not_sub_ty = StructType::new(&engine, [imm_field(HeapType::Extern)])?;
139 assert!(!not_sub_ty.matches(&super_ty));
140 let not_sub_ty = StructType::new(&engine, [field(HeapType::Extern)])?;
141 assert!(!not_sub_ty.matches(&super_ty));
142 let not_sub_ty = StructType::new(&engine, [])?;
143 assert!(!not_sub_ty.matches(&super_ty));
144
145 Ok(())
146 }
147
148 #[test]
struct_subtyping_fields_must_match() -> Result<()>149 fn struct_subtyping_fields_must_match() -> Result<()> {
150 let engine = Engine::default();
151
152 let a = StructType::with_finality_and_supertype(
153 &engine,
154 Finality::NonFinal,
155 None,
156 [imm_field(HeapType::Any)],
157 )?;
158
159 for (msg, expected, fields) in [
160 ("Missing field", false, vec![]),
161 (
162 "Non-matching field",
163 false,
164 vec![imm_field(HeapType::Extern)],
165 ),
166 ("Wrong mutability field", false, vec![field(HeapType::Any)]),
167 ("Exact match is okay", true, vec![imm_field(HeapType::Any)]),
168 (
169 "Subtype of the field is okay",
170 true,
171 vec![imm_field(HeapType::Eq)],
172 ),
173 (
174 "Extra fields are okay",
175 true,
176 vec![imm_field(HeapType::Any), imm_field(HeapType::Extern)],
177 ),
178 ] {
179 let actual =
180 StructType::with_finality_and_supertype(&engine, Finality::NonFinal, Some(&a), fields)
181 .is_ok();
182 assert_eq!(
183 expected, actual,
184 "expected valid? {expected}; actually valid? {actual}; {msg}"
185 );
186 }
187
188 Ok(())
189 }
190
191 #[test]
struct_subtyping_supertype_and_finality() -> Result<()>192 fn struct_subtyping_supertype_and_finality() -> Result<()> {
193 let engine = Engine::default();
194
195 for (expected, finality) in [(true, Finality::NonFinal), (false, Finality::Final)] {
196 let a = StructType::with_finality_and_supertype(&engine, finality, None, [])?;
197 let actual =
198 StructType::with_finality_and_supertype(&engine, Finality::Final, Some(&a), []).is_ok();
199 assert_eq!(expected, actual);
200 }
201
202 Ok(())
203 }
204
205 #[test]
struct_subtyping() -> Result<()>206 fn struct_subtyping() -> Result<()> {
207 let engine = Engine::default();
208
209 // These types produce the following trees:
210 //
211 // base g
212 // / \ /
213 // a b h
214 // / \ /
215 // c d i
216 // /
217 // e
218 // /
219 // f
220 let base = StructType::with_finality_and_supertype(&engine, Finality::NonFinal, None, [])?;
221 let a = StructType::with_finality_and_supertype(&engine, Finality::NonFinal, Some(&base), [])?;
222 let b = StructType::with_finality_and_supertype(
223 &engine,
224 Finality::NonFinal,
225 Some(&base),
226 // Have to add a field so that `b` doesn't dedupe to `a`.
227 [field(HeapType::Any)],
228 )?;
229 let c = StructType::with_finality_and_supertype(&engine, Finality::NonFinal, Some(&a), [])?;
230 let d = StructType::with_finality_and_supertype(
231 &engine,
232 Finality::NonFinal,
233 Some(&a),
234 // Have to add a field so that `d` doesn't dedupe to `c`.
235 [field(HeapType::Any)],
236 )?;
237 let e = StructType::with_finality_and_supertype(&engine, Finality::NonFinal, Some(&c), [])?;
238 let f = StructType::with_finality_and_supertype(&engine, Finality::NonFinal, Some(&e), [])?;
239 let g = StructType::with_finality_and_supertype(
240 &engine,
241 Finality::NonFinal,
242 None,
243 // Have to add a field so that `g` doesn't dedupe to `base`.
244 [field(HeapType::Any)],
245 )?;
246 let h = StructType::with_finality_and_supertype(
247 &engine,
248 Finality::NonFinal,
249 Some(&g),
250 [field(HeapType::Any)],
251 )?;
252 let i = StructType::with_finality_and_supertype(
253 &engine,
254 Finality::NonFinal,
255 Some(&h),
256 [field(HeapType::Any)],
257 )?;
258
259 for (expected, sub_name, sub, sup_name, sup) in [
260 // Identity, at root.
261 (true, "base", &base, "base", &base),
262 // Identity, in middle.
263 (true, "c", &c, "c", &c),
264 // Identity, at leaf.
265 (true, "f", &f, "f", &f),
266 // Direct, at root.
267 (true, "a", &a, "base", &base),
268 // Direct, in middle.
269 (true, "c", &c, "a", &a),
270 // Direct, at leaf.
271 (true, "f", &f, "e", &e),
272 // Transitive, at root.
273 (true, "c", &c, "base", &base),
274 // Transitive, in middle.
275 (true, "e", &e, "a", &a),
276 // Transitive, at leaf.
277 (true, "f", &f, "c", &c),
278 // Unrelated roots.
279 (false, "base", &base, "g", &g),
280 (false, "g", &g, "base", &base),
281 // Unrelated siblings.
282 (false, "a", &a, "b", &b),
283 (false, "b", &b, "a", &a),
284 (false, "c", &c, "d", &d),
285 (false, "d", &d, "c", &c),
286 // Unrelated root and middle.
287 (false, "base", &base, "h", &h),
288 (false, "h", &h, "base", &base),
289 // Unrelated root and leaf.
290 (false, "base", &base, "i", &i),
291 (false, "i", &i, "base", &base),
292 // Unrelated middles.
293 (false, "a", &a, "h", &h),
294 (false, "h", &h, "a", &a),
295 // Unrelated middle and leaf.
296 (false, "a", &a, "i", &i),
297 (false, "i", &i, "a", &a),
298 ] {
299 eprintln!("expect that `{sub_name} <: {sup_name}` is `{expected}`");
300 let sub = HeapType::ConcreteStruct(sub.clone());
301 let sup = HeapType::ConcreteStruct(sup.clone());
302 let actual = sub.matches(&sup);
303 assert_eq!(expected, actual);
304 }
305
306 Ok(())
307 }
308
309 #[test]
array_subtyping_field_must_match() -> Result<()>310 fn array_subtyping_field_must_match() -> Result<()> {
311 let engine = Engine::default();
312
313 let a = ArrayType::with_finality_and_supertype(
314 &engine,
315 Finality::NonFinal,
316 None,
317 imm_field(HeapType::Any),
318 )?;
319
320 for (expected, field) in [
321 // Non-matching field.
322 (false, imm_field(HeapType::Extern)),
323 // Wrong mutability field.
324 (false, field(HeapType::Any)),
325 // Exact match is okay.
326 (true, imm_field(HeapType::Any)),
327 // Subtype of the field is okay.
328 (true, imm_field(HeapType::Eq)),
329 ] {
330 let actual =
331 ArrayType::with_finality_and_supertype(&engine, Finality::NonFinal, Some(&a), field)
332 .is_ok();
333 assert_eq!(expected, actual);
334 }
335
336 Ok(())
337 }
338
339 #[test]
array_subtyping_supertype_and_finality() -> Result<()>340 fn array_subtyping_supertype_and_finality() -> Result<()> {
341 let engine = Engine::default();
342
343 for (expected, finality) in [(true, Finality::NonFinal), (false, Finality::Final)] {
344 let superty =
345 ArrayType::with_finality_and_supertype(&engine, finality, None, field(HeapType::Any))?;
346 let actual = ArrayType::with_finality_and_supertype(
347 &engine,
348 Finality::Final,
349 Some(&superty),
350 field(HeapType::Any),
351 )
352 .is_ok();
353 assert_eq!(expected, actual);
354 }
355
356 Ok(())
357 }
358
359 #[test]
array_subtyping() -> Result<()>360 fn array_subtyping() -> Result<()> {
361 let engine = Engine::default();
362
363 // These types produce the following trees:
364 //
365 // base g
366 // / \ /
367 // a b h
368 // / \ /
369 // c d i
370 // /
371 // e
372 // /
373 // f
374 let base = ArrayType::with_finality_and_supertype(
375 &engine,
376 Finality::NonFinal,
377 None,
378 imm_field(HeapType::Any),
379 )?;
380 let a = ArrayType::with_finality_and_supertype(
381 &engine,
382 Finality::NonFinal,
383 Some(&base),
384 imm_field(HeapType::Any),
385 )?;
386 let b = ArrayType::with_finality_and_supertype(
387 &engine,
388 Finality::NonFinal,
389 Some(&base),
390 imm_field(HeapType::Eq),
391 )?;
392 let c = ArrayType::with_finality_and_supertype(
393 &engine,
394 Finality::NonFinal,
395 Some(&a),
396 imm_field(HeapType::Any),
397 )?;
398 let d = ArrayType::with_finality_and_supertype(
399 &engine,
400 Finality::NonFinal,
401 Some(&a),
402 imm_field(HeapType::Eq),
403 )?;
404 let e = ArrayType::with_finality_and_supertype(
405 &engine,
406 Finality::NonFinal,
407 Some(&c),
408 imm_field(HeapType::Any),
409 )?;
410 let f = ArrayType::with_finality_and_supertype(
411 &engine,
412 Finality::NonFinal,
413 Some(&e),
414 imm_field(HeapType::Any),
415 )?;
416 let g = ArrayType::with_finality_and_supertype(
417 &engine,
418 Finality::NonFinal,
419 None,
420 imm_field(HeapType::Eq),
421 )?;
422 let h = ArrayType::with_finality_and_supertype(
423 &engine,
424 Finality::NonFinal,
425 Some(&g),
426 imm_field(HeapType::Eq),
427 )?;
428 let i = ArrayType::with_finality_and_supertype(
429 &engine,
430 Finality::NonFinal,
431 Some(&h),
432 imm_field(HeapType::Eq),
433 )?;
434
435 for (expected, sub_name, sub, sup_name, sup) in [
436 // Identity, at root.
437 (true, "base", &base, "base", &base),
438 // Identity, in middle.
439 (true, "c", &c, "c", &c),
440 // Identity, at leaf.
441 (true, "f", &f, "f", &f),
442 // Direct, at root.
443 (true, "a", &a, "base", &base),
444 // Direct, in middle.
445 (true, "c", &c, "a", &a),
446 // Direct, at leaf.
447 (true, "f", &f, "e", &e),
448 // Transitive, at root.
449 (true, "c", &c, "base", &base),
450 // Transitive, in middle.
451 (true, "e", &e, "a", &a),
452 // Transitive, at leaf.
453 (true, "f", &f, "c", &c),
454 // Unrelated roots.
455 (false, "base", &base, "g", &g),
456 (false, "g", &g, "base", &base),
457 // Unrelated siblings.
458 (false, "a", &a, "b", &b),
459 (false, "b", &b, "a", &a),
460 (false, "c", &c, "d", &d),
461 (false, "d", &d, "c", &c),
462 // Unrelated root and middle.
463 (false, "base", &base, "h", &h),
464 (false, "h", &h, "base", &base),
465 // Unrelated root and leaf.
466 (false, "base", &base, "i", &i),
467 (false, "i", &i, "base", &base),
468 // Unrelated middles.
469 (false, "a", &a, "h", &h),
470 (false, "h", &h, "a", &a),
471 // Unrelated middle and leaf.
472 (false, "a", &a, "i", &i),
473 (false, "i", &i, "a", &a),
474 ] {
475 eprintln!("expect that `{sub_name} <: {sup_name}` is `{expected}`");
476 let sub = HeapType::ConcreteArray(sub.clone());
477 let sup = HeapType::ConcreteArray(sup.clone());
478 let actual = sub.matches(&sup);
479 assert_eq!(expected, actual);
480 }
481
482 Ok(())
483 }
484
485 #[test]
func_subtyping_field_must_match() -> Result<()>486 fn func_subtyping_field_must_match() -> Result<()> {
487 let engine = Engine::default();
488
489 let superty = FuncType::with_finality_and_supertype(
490 &engine,
491 Finality::NonFinal,
492 None,
493 [valty(HeapType::Struct)],
494 [valty(HeapType::Any)],
495 )?;
496
497 for (expected, param, ret) in [
498 // Non-matching param type.
499 (false, valty(HeapType::Extern), valty(HeapType::Any)),
500 // Non-matching return type.
501 (false, valty(HeapType::Struct), valty(HeapType::Extern)),
502 // Exact match is okay.
503 (true, valty(HeapType::Struct), valty(HeapType::Any)),
504 // Subtype of the return type is okay.
505 (true, valty(HeapType::Struct), valty(HeapType::Eq)),
506 // Supertype of the param type is okay.
507 (true, valty(HeapType::Eq), valty(HeapType::Any)),
508 ] {
509 let actual = FuncType::with_finality_and_supertype(
510 &engine,
511 Finality::NonFinal,
512 Some(&superty),
513 [param],
514 [ret],
515 )
516 .is_ok();
517 assert_eq!(expected, actual);
518 }
519
520 Ok(())
521 }
522
523 #[test]
func_subtyping_supertype_and_finality() -> Result<()>524 fn func_subtyping_supertype_and_finality() -> Result<()> {
525 let engine = Engine::default();
526
527 for (expected, finality) in [(true, Finality::NonFinal), (false, Finality::Final)] {
528 let superty = FuncType::with_finality_and_supertype(
529 &engine,
530 finality,
531 None,
532 [],
533 [valty(HeapType::Any)],
534 )?;
535 let actual = FuncType::with_finality_and_supertype(
536 &engine,
537 Finality::Final,
538 Some(&superty),
539 [],
540 [valty(HeapType::Any)],
541 )
542 .is_ok();
543 assert_eq!(expected, actual);
544 }
545
546 Ok(())
547 }
548
549 #[test]
func_subtyping() -> Result<()>550 fn func_subtyping() -> Result<()> {
551 let engine = Engine::default();
552
553 // These types produce the following trees:
554 //
555 // base g
556 // / \ /
557 // a b h
558 // / \ /
559 // c d i
560 // /
561 // e
562 // /
563 // f
564 let base = FuncType::with_finality_and_supertype(
565 &engine,
566 Finality::NonFinal,
567 None,
568 [],
569 [valty(HeapType::Any)],
570 )?;
571 let a = FuncType::with_finality_and_supertype(
572 &engine,
573 Finality::NonFinal,
574 Some(&base),
575 [],
576 [valty(HeapType::Any)],
577 )?;
578 let b = FuncType::with_finality_and_supertype(
579 &engine,
580 Finality::NonFinal,
581 Some(&base),
582 [],
583 [valty(HeapType::Eq)],
584 )?;
585 let c = FuncType::with_finality_and_supertype(
586 &engine,
587 Finality::NonFinal,
588 Some(&a),
589 [],
590 [valty(HeapType::Any)],
591 )?;
592 let d = FuncType::with_finality_and_supertype(
593 &engine,
594 Finality::NonFinal,
595 Some(&a),
596 [],
597 [valty(HeapType::Eq)],
598 )?;
599 let e = FuncType::with_finality_and_supertype(
600 &engine,
601 Finality::NonFinal,
602 Some(&c),
603 [],
604 [valty(HeapType::Any)],
605 )?;
606 let f = FuncType::with_finality_and_supertype(
607 &engine,
608 Finality::NonFinal,
609 Some(&e),
610 [],
611 [valty(HeapType::Any)],
612 )?;
613 let g = FuncType::with_finality_and_supertype(
614 &engine,
615 Finality::NonFinal,
616 None,
617 [],
618 [valty(HeapType::Eq)],
619 )?;
620 let h = FuncType::with_finality_and_supertype(
621 &engine,
622 Finality::NonFinal,
623 Some(&g),
624 [],
625 [valty(HeapType::Eq)],
626 )?;
627 let i = FuncType::with_finality_and_supertype(
628 &engine,
629 Finality::NonFinal,
630 Some(&h),
631 [],
632 [valty(HeapType::Eq)],
633 )?;
634
635 for (expected, sub_name, sub, sup_name, sup) in [
636 // Identity, at root.
637 (true, "base", &base, "base", &base),
638 // Identity, in middle.
639 (true, "c", &c, "c", &c),
640 // Identity, at leaf.
641 (true, "f", &f, "f", &f),
642 // Direct, at root.
643 (true, "a", &a, "base", &base),
644 // Direct, in middle.
645 (true, "c", &c, "a", &a),
646 // Direct, at leaf.
647 (true, "f", &f, "e", &e),
648 // Transitive, at root.
649 (true, "c", &c, "base", &base),
650 // Transitive, in middle.
651 (true, "e", &e, "a", &a),
652 // Transitive, at leaf.
653 (true, "f", &f, "c", &c),
654 // Unrelated roots.
655 (false, "base", &base, "g", &g),
656 (false, "g", &g, "base", &base),
657 // Unrelated siblings.
658 (false, "a", &a, "b", &b),
659 (false, "b", &b, "a", &a),
660 (false, "c", &c, "d", &d),
661 (false, "d", &d, "c", &c),
662 // Unrelated root and middle.
663 (false, "base", &base, "h", &h),
664 (false, "h", &h, "base", &base),
665 // Unrelated root and leaf.
666 (false, "base", &base, "i", &i),
667 (false, "i", &i, "base", &base),
668 // Unrelated middles.
669 (false, "a", &a, "h", &h),
670 (false, "h", &h, "a", &a),
671 // Unrelated middle and leaf.
672 (false, "a", &a, "i", &i),
673 (false, "i", &i, "a", &a),
674 ] {
675 eprintln!("expect that `{sub_name} <: {sup_name}` is `{expected}`");
676 let sub = HeapType::ConcreteFunc(sub.clone());
677 let sup = HeapType::ConcreteFunc(sup.clone());
678 let actual = sub.matches(&sup);
679 assert_eq!(expected, actual);
680 }
681
682 Ok(())
683 }
684
685 #[test]
heap_type_matches_noexn() -> Result<()>686 fn heap_type_matches_noexn() -> Result<()> {
687 // Test that NoExn only matches exception-related types.
688 // NoExn should NOT match unrelated heap types like Func, Extern, Any, etc.
689
690 // NoExn should match exception hierarchy types
691 assert!(HeapType::NoExn.matches(&HeapType::Exn));
692 assert!(HeapType::NoExn.matches(&HeapType::NoExn));
693
694 // NoExn should NOT match types outside the exception hierarchy
695 assert!(!HeapType::NoExn.matches(&HeapType::Func));
696 assert!(!HeapType::NoExn.matches(&HeapType::NoFunc));
697 assert!(!HeapType::NoExn.matches(&HeapType::Extern));
698 assert!(!HeapType::NoExn.matches(&HeapType::NoExtern));
699 assert!(!HeapType::NoExn.matches(&HeapType::Any));
700 assert!(!HeapType::NoExn.matches(&HeapType::Eq));
701 assert!(!HeapType::NoExn.matches(&HeapType::I31));
702 assert!(!HeapType::NoExn.matches(&HeapType::Struct));
703 assert!(!HeapType::NoExn.matches(&HeapType::Array));
704 assert!(!HeapType::NoExn.matches(&HeapType::None));
705 assert!(!HeapType::NoExn.matches(&HeapType::Cont));
706 assert!(!HeapType::NoExn.matches(&HeapType::NoCont));
707
708 Ok(())
709 }
710