1use crate::compile::ModuleTranslation;
28use crate::component::ExportItem;
29use crate::component::dfg::{
30 AdapterId, AdapterModuleId, ComponentDfg, CoreDef, Export, Instance, InstanceId, SideEffect,
31};
32use crate::prelude::*;
33use crate::union_find::UnionFind;
34use crate::{
35 EntityIndex, EntityRef, FuncIndex, ImportPositions, PrimaryMap, SecondaryMap, StaticModuleIndex,
36};
37use core::mem;
38use std::collections::HashMap;
39use std::collections::hash_map::Entry;
40
41#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
45enum VmctxKey {
46 Component,
51
52 CoreInstance(InstanceId),
54
55 AdapterModule(AdapterModuleId),
60}
61
62#[derive(Clone, Debug, Default)]
65enum SameVmctxPartition {
66 #[default]
70 Top,
71
72 Known(UnionFind<FuncIndex>),
77}
78
79impl SameVmctxPartition {
80 fn bottom() -> Self {
83 SameVmctxPartition::Known(UnionFind::new())
84 }
85
86 fn from_keys(first: &mut HashMap<VmctxKey, FuncIndex>, keys: &[Option<VmctxKey>]) -> Self {
95 first.clear();
96 let mut sets = UnionFind::new();
97 for (i, key) in keys.iter().enumerate() {
98 let Some(key) = *key else { continue };
99 let func = FuncIndex::new(i);
100 match first.entry(key) {
101 Entry::Occupied(e) => {
102 sets.union(*e.get(), func);
103 }
104 Entry::Vacant(e) => {
105 e.insert(func);
106 }
107 }
108 }
109 SameVmctxPartition::Known(sets)
110 }
111
112 fn meet(groups: &mut HashMap<(FuncIndex, FuncIndex), FuncIndex>, a: Self, b: Self) -> Self {
118 use SameVmctxPartition::*;
119 match (a, b) {
120 (Top, p) | (p, Top) => p,
122
123 (Known(ref a), Known(ref b)) => {
124 let (small, large) = if a.len() <= b.len() { (a, b) } else { (b, a) };
132 let mut sets = UnionFind::new();
133
134 groups.clear();
137 for func in small.elems() {
138 if !large.contains(func) {
139 continue;
140 }
141 let group = (
142 a.find_without_path_compression(func),
143 b.find_without_path_compression(func),
144 );
145 match groups.entry(group) {
146 Entry::Occupied(e) => {
147 sets.union(*e.get(), func);
148 }
149 Entry::Vacant(e) => {
150 e.insert(func);
151 }
152 }
153 }
154
155 Known(sets)
156 }
157 }
158 }
159
160 fn representative(&self, func: FuncIndex) -> FuncIndex {
167 match self {
168 SameVmctxPartition::Top => FuncIndex::new(0),
170
171 SameVmctxPartition::Known(sets) => sets.set_min(func),
175 }
176 }
177}
178
179#[derive(Default)]
181struct SameVmctxBuilder {
182 partitions: SecondaryMap<StaticModuleIndex, SameVmctxPartition>,
186
187 scratch_first: HashMap<VmctxKey, FuncIndex>,
190
191 scratch_groups: HashMap<(FuncIndex, FuncIndex), FuncIndex>,
194}
195
196impl SameVmctxBuilder {
197 fn observe_instantiation(&mut self, module: StaticModuleIndex, keys: &[Option<VmctxKey>]) {
200 let partition = SameVmctxPartition::from_keys(&mut self.scratch_first, keys);
201 self.observe(module, partition);
202 }
203
204 fn observe_unknown_instantiation(&mut self, module: StaticModuleIndex) {
207 self.observe(module, SameVmctxPartition::bottom());
208 }
209
210 fn observe(&mut self, module: StaticModuleIndex, partition: SameVmctxPartition) {
211 let current = mem::take(&mut self.partitions[module]);
212 let met = SameVmctxPartition::meet(&mut self.scratch_groups, current, partition);
213 self.partitions[module] = met;
214 }
215
216 fn finish(self) -> SameVmctxImports {
218 SameVmctxImports {
219 partitions: self.partitions,
220 }
221 }
222}
223
224struct SameVmctxImports {
226 partitions: SecondaryMap<StaticModuleIndex, SameVmctxPartition>,
227}
228
229impl SameVmctxImports {
230 fn representative(&self, module: StaticModuleIndex, func: FuncIndex) -> FuncIndex {
233 self.partitions[module].representative(func)
234 }
235}
236
237pub fn analyze_same_vmctx_imports(
240 dfg: &ComponentDfg,
241 static_modules: &mut PrimaryMap<StaticModuleIndex, ModuleTranslation<'_>>,
242) {
243 let mut builder = SameVmctxBuilder::default();
244 let mut keys = VmctxKeys::new(static_modules);
245
246 let mut func_keys = Vec::new();
248 let mut stack = Vec::new();
249
250 for (id, instance) in dfg.instances.iter() {
252 keys.push(dfg, id, instance);
253 }
254
255 for effect in dfg.side_effects.iter() {
258 let SideEffect::Instance(id, _) = effect else {
259 continue;
260 };
261
262 let Instance::Static(module, _) = &dfg.instances[*id] else {
263 continue;
265 };
266
267 observe_instantiation(
268 &mut func_keys,
269 &mut builder,
270 static_modules,
271 *module,
272 keys.args[*id].iter().copied(),
273 );
274 }
275
276 for (_, (module, args)) in dfg.adapter_modules.iter() {
279 observe_instantiation(
280 &mut func_keys,
281 &mut builder,
282 static_modules,
283 *module,
284 args.iter().map(|arg| keys.vmctx_key(dfg, arg)),
285 );
286 }
287
288 for (_, (export, _)) in dfg.exports.iter() {
290 observe_exported_modules(&mut stack, &mut builder, export);
291 }
292
293 let analysis = builder.finish();
294
295 for (module, translation) in static_modules.iter_mut() {
296 for i in 0..translation.module.num_imported_funcs {
297 let func = FuncIndex::new(i);
298 let representative = analysis.representative(module, func);
299 if representative != func {
300 translation.imported_func_vmctx_representative[func] = representative.into();
301 }
302 }
303 }
304}
305
306fn observe_instantiation(
312 func_keys: &mut Vec<Option<VmctxKey>>,
313 builder: &mut SameVmctxBuilder,
314 static_modules: &PrimaryMap<StaticModuleIndex, ModuleTranslation<'_>>,
315 module: StaticModuleIndex,
316 args: impl Iterator<Item = Option<VmctxKey>>,
317) {
318 let translation = &static_modules[module];
319 func_keys.clear();
320 func_keys.resize(translation.module.num_imported_funcs, None);
321
322 for (position, key) in args.enumerate() {
323 let Some(EntityIndex::Function(func)) = translation.module.import_index(position) else {
324 continue;
325 };
326 func_keys[func.index()] = key;
327 }
328
329 builder.observe_instantiation(module, func_keys);
330}
331
332fn observe_exported_modules<'a>(
338 stack: &mut Vec<&'a Export>,
339 builder: &mut SameVmctxBuilder,
340 export: &'a Export,
341) {
342 stack.clear();
343 stack.push(export);
344
345 while let Some(export) = stack.pop() {
346 match export {
347 Export::ModuleStatic { index, .. } => builder.observe_unknown_instantiation(*index),
348
349 Export::Instance { exports, .. } => {
350 stack.extend(exports.iter().map(|(_, (export, _))| export));
351 }
352
353 Export::LiftedFunction { .. } | Export::ModuleImport { .. } | Export::Type(_) => {}
354 }
355 }
356}
357
358struct VmctxKeys<'a> {
362 args: PrimaryMap<InstanceId, Vec<Option<VmctxKey>>>,
367
368 static_modules: &'a PrimaryMap<StaticModuleIndex, ModuleTranslation<'a>>,
370
371 import_positions: PrimaryMap<StaticModuleIndex, ImportPositions>,
373}
374
375impl<'a> VmctxKeys<'a> {
376 fn new(static_modules: &'a PrimaryMap<StaticModuleIndex, ModuleTranslation<'a>>) -> Self {
377 VmctxKeys {
378 args: PrimaryMap::new(),
379 static_modules,
380 import_positions: static_modules
381 .values()
382 .map(|translation| translation.module.import_positions())
383 .collect(),
384 }
385 }
386
387 fn push(&mut self, dfg: &ComponentDfg, id: InstanceId, instance: &Instance) {
389 let keys = match instance {
390 Instance::Static(_, args) => args.iter().map(|arg| self.vmctx_key(dfg, arg)).collect(),
391 Instance::Import(..) => Vec::new(),
392 };
393 let pushed = self.args.push(keys);
394 debug_assert_eq!(pushed, id);
395 }
396
397 fn vmctx_key(&self, dfg: &ComponentDfg, def: &CoreDef) -> Option<VmctxKey> {
400 let export = match def {
403 CoreDef::Trampoline(_) | CoreDef::UnsafeIntrinsic(..) => {
406 return Some(VmctxKey::Component);
407 }
408
409 CoreDef::Adapter(id) => return adapter_vmctx_key(dfg, self.static_modules, *id),
410
411 CoreDef::InstanceFlags(_) => return None,
414
415 CoreDef::Export(export) => export,
416 };
417
418 let Instance::Static(module, _) = &dfg.instances[export.instance] else {
419 return None;
421 };
422
423 let ExportItem::Index(index) = &export.item else {
424 unreachable!()
427 };
428
429 let Some(position) = self.import_positions[*module].get(*index) else {
432 return Some(VmctxKey::CoreInstance(export.instance));
433 };
434
435 *self.args.get(export.instance)?.get(position)?
438 }
439}
440
441fn adapter_vmctx_key(
444 dfg: &ComponentDfg,
445 static_modules: &PrimaryMap<StaticModuleIndex, ModuleTranslation<'_>>,
446 id: AdapterId,
447) -> Option<VmctxKey> {
448 let (adapter_module, index) = *dfg.adapter_partitionings.get(id)?;
449 let (static_module, _) = dfg.adapter_modules[adapter_module];
450
451 debug_assert!(
452 !static_modules[static_module].module.is_imported(index),
453 "adapter modules always define their exported adapters",
454 );
455
456 Some(VmctxKey::AdapterModule(adapter_module))
457}
458
459#[cfg(test)]
460mod tests {
461 use super::*;
462 use crate::property_check;
463 use mutatis::{Mutate, check::CheckResult, mutators as m};
464
465 impl SameVmctxPartition {
466 fn same_block(&self, a: FuncIndex, b: FuncIndex) -> bool {
468 self.representative(a) == self.representative(b)
470 }
471
472 fn blocks(&self, n: u32) -> Vec<Vec<u32>> {
476 let mut blocks = std::collections::BTreeMap::<u32, Vec<u32>>::new();
477 for i in 0..n {
478 let rep = self.representative(FuncIndex::from_u32(i)).as_u32();
479 blocks.entry(rep).or_default().push(i);
480 }
481 blocks.into_values().collect()
482 }
483
484 fn refines(&self, other: &Self, n: u32) -> bool {
487 (0..n).all(|i| {
488 (0..n).all(|j| {
489 let (a, b) = (FuncIndex::from_u32(i), FuncIndex::from_u32(j));
490 !self.same_block(a, b) || other.same_block(a, b)
491 })
492 })
493 }
494 }
495
496 fn key(i: u32) -> Option<VmctxKey> {
498 Some(VmctxKey::CoreInstance(InstanceId::from_u32(i)))
499 }
500
501 fn unknown() -> Option<VmctxKey> {
503 None
504 }
505
506 fn analyze(instantiations: &[&[Option<VmctxKey>]]) -> SameVmctxPartition {
509 let module = StaticModuleIndex::from_u32(0);
510 let mut builder = SameVmctxBuilder::default();
511 for keys in instantiations {
512 builder.observe_instantiation(module, keys);
513 }
514 builder.finish().partitions[module].clone()
515 }
516
517 fn blocks(n: u32, instantiations: &[&[Option<VmctxKey>]]) -> Vec<Vec<u32>> {
520 analyze(instantiations).blocks(n)
521 }
522
523 #[test]
524 fn a_module_that_is_never_instantiated_stays_at_top() {
525 let p = analyze(&[]);
528 assert!(matches!(p, SameVmctxPartition::Top));
529 assert_eq!(p.blocks(4), vec![vec![0, 1, 2, 3]]);
530
531 for i in 0..4 {
532 assert_eq!(
533 p.representative(FuncIndex::from_u32(i)),
534 FuncIndex::from_u32(0),
535 "import {i} should use import 0's `vmctx` slot",
536 );
537 }
538 }
539
540 #[test]
541 fn one_instantiation_with_all_imports_from_one_instance() {
542 assert_eq!(
543 blocks(4, &[&[key(0), key(0), key(0), key(0)]]),
544 vec![vec![0, 1, 2, 3]],
545 );
546 }
547
548 #[test]
549 fn one_instantiation_with_all_imports_from_distinct_instances() {
550 assert_eq!(
551 blocks(4, &[&[key(0), key(1), key(2), key(3)]]),
552 vec![vec![0], vec![1], vec![2], vec![3]],
553 );
554 }
555
556 #[test]
557 fn one_instantiation_split_across_two_instances() {
558 assert_eq!(
559 blocks(4, &[&[key(9), key(9), key(5), key(5)]]),
560 vec![vec![0, 1], vec![2, 3]],
561 );
562 }
563
564 #[test]
565 fn one_instantiation_with_interleaved_instances() {
566 assert_eq!(
568 blocks(4, &[&[key(0), key(1), key(0), key(1)]]),
569 vec![vec![0, 2], vec![1, 3]],
570 );
571 }
572
573 #[test]
574 fn unknown_keys_are_singletons() {
575 assert_eq!(
578 blocks(5, &[&[key(0), unknown(), key(0), unknown(), key(0)]]),
579 vec![vec![0, 2, 4], vec![1], vec![3]],
580 );
581 }
582
583 #[test]
584 fn distinct_key_variants_are_distinct_keys() {
585 let component = Some(VmctxKey::Component);
588 let instance = Some(VmctxKey::CoreInstance(InstanceId::from_u32(0)));
589 let adapter = Some(VmctxKey::AdapterModule(AdapterModuleId::from_u32(0)));
590 assert_eq!(
591 blocks(
592 6,
593 &[&[component, instance, adapter, component, instance, adapter]]
594 ),
595 vec![vec![0, 3], vec![1, 4], vec![2, 5]],
596 );
597 }
598
599 #[test]
600 fn two_agreeing_instantiations_change_nothing() {
601 assert_eq!(
603 blocks(
604 4,
605 &[
606 &[key(0), key(0), key(1), key(1)],
607 &[key(7), key(7), key(8), key(8)],
608 ],
609 ),
610 vec![vec![0, 1], vec![2, 3]],
611 );
612 }
613
614 #[test]
615 fn a_disagreeing_instantiation_breaks_everything_apart() {
616 assert_eq!(
617 blocks(
618 4,
619 &[
620 &[key(0), key(0), key(0), key(0)],
621 &[key(0), key(1), key(2), key(3)],
622 ],
623 ),
624 vec![vec![0], vec![1], vec![2], vec![3]],
625 );
626 }
627
628 #[test]
629 fn a_partially_disagreeing_instantiation_keeps_what_it_agrees_on() {
630 assert_eq!(
631 blocks(
632 4,
633 &[
634 &[key(0), key(0), key(0), key(0)],
635 &[key(0), key(0), key(1), key(1)],
636 ],
637 ),
638 vec![vec![0, 1], vec![2, 3]],
639 );
640 }
641
642 #[test]
643 fn crossing_splits_meet_to_all_singletons() {
644 assert_eq!(
647 blocks(
648 4,
649 &[
650 &[key(0), key(0), key(1), key(1)],
651 &[key(0), key(1), key(0), key(1)],
652 ],
653 ),
654 vec![vec![0], vec![1], vec![2], vec![3]],
655 );
656 }
657
658 #[test]
659 fn partially_crossing_splits_keep_their_common_refinement() {
660 assert_eq!(
662 blocks(
663 6,
664 &[
665 &[key(0), key(0), key(0), key(1), key(1), key(1)],
666 &[key(0), key(0), key(1), key(1), key(2), key(2)],
667 ],
668 ),
669 vec![vec![0, 1], vec![2], vec![3], vec![4, 5]],
670 );
671 }
672
673 #[test]
674 fn progressive_refinement_is_order_independent() {
675 let a: &[Option<VmctxKey>] = &[key(0), key(0), key(0), key(0), key(0), key(0)];
676 let b: &[Option<VmctxKey>] = &[key(0), key(0), key(0), key(1), key(1), key(1)];
677 let c: &[Option<VmctxKey>] = &[key(0), key(0), key(1), key(1), key(2), key(2)];
678
679 let expected = vec![vec![0, 1], vec![2], vec![3], vec![4, 5]];
680 for order in [
681 [a, b, c],
682 [a, c, b],
683 [b, a, c],
684 [b, c, a],
685 [c, a, b],
686 [c, b, a],
687 ] {
688 assert_eq!(blocks(6, &order), expected, "order {order:?} disagreed");
689 }
690 }
691
692 #[test]
693 fn an_unknown_instantiation_forces_bottom_and_cannot_be_undone() {
694 let module = StaticModuleIndex::from_u32(0);
695 let mut builder = SameVmctxBuilder::default();
696
697 builder.observe_instantiation(module, &[key(0), key(0), key(0)]);
698 builder.observe_unknown_instantiation(module);
699 builder.observe_instantiation(module, &[key(0), key(0), key(0)]);
701
702 let p = builder.finish().partitions[module].clone();
703 assert_eq!(p.blocks(3), SameVmctxPartition::bottom().blocks(3));
704 }
705
706 #[test]
707 fn modules_do_not_interfere_with_each_other() {
708 let a = StaticModuleIndex::from_u32(0);
709 let b = StaticModuleIndex::from_u32(3);
710 let mut builder = SameVmctxBuilder::default();
711
712 builder.observe_instantiation(a, &[key(0), key(0)]);
713 builder.observe_instantiation(b, &[key(0), key(1)]);
714
715 let analysis = builder.finish();
716 assert_eq!(analysis.partitions[a].blocks(2), vec![vec![0, 1]]);
717 assert_eq!(analysis.partitions[b].blocks(2), vec![vec![0], vec![1]]);
718 assert_eq!(
720 analysis.partitions[StaticModuleIndex::from_u32(1)].blocks(2),
721 vec![vec![0, 1]],
722 );
723 }
724
725 #[test]
726 fn degenerate_numbers_of_imports() {
727 assert_eq!(blocks(0, &[]), Vec::<Vec<u32>>::new());
730 assert_eq!(blocks(0, &[&[]]), Vec::<Vec<u32>>::new());
731
732 assert_eq!(blocks(1, &[]), vec![vec![0]]);
735 assert_eq!(blocks(1, &[&[key(0)]]), vec![vec![0]]);
736 assert_eq!(blocks(1, &[&[unknown()]]), vec![vec![0]]);
737 assert_eq!(blocks(1, &[&[key(0)], &[key(1)]]), vec![vec![0]]);
738 }
739
740 #[test]
741 fn a_representative_is_always_its_blocks_least_member() {
742 let p = analyze(&[&[key(1), key(0), key(1), key(0), key(1)]]);
743 let rep = |i| p.representative(FuncIndex::from_u32(i)).as_u32();
744 assert_eq!(rep(0), 0);
745 assert_eq!(rep(2), 0);
746 assert_eq!(rep(4), 0);
747 assert_eq!(rep(1), 1);
748 assert_eq!(rep(3), 1);
749
750 let p = analyze(&[&[unknown(), key(0), key(1), key(0), key(1)]]);
752 let rep = |i| p.representative(FuncIndex::from_u32(i)).as_u32();
753 assert_eq!(rep(0), 0);
754 assert_eq!(rep(1), 1);
755 assert_eq!(rep(3), 1);
756 assert_eq!(rep(2), 2);
757 assert_eq!(rep(4), 2);
758 }
759
760 #[test]
761 fn many_imports_in_many_blocks() {
762 let keys = (0..64).map(|i| key(i % 8)).collect::<Vec<_>>();
765 let expected = (0..8)
766 .map(|b| (0..8).map(|i| b + i * 8).collect::<Vec<u32>>())
767 .collect::<Vec<_>>();
768 assert_eq!(blocks(64, &[&keys]), expected);
769
770 let contiguous = (0..64).map(|i| key(i / 8)).collect::<Vec<_>>();
774 assert_eq!(
775 blocks(64, &[&keys, &contiguous]),
776 (0..64).map(|i| vec![i]).collect::<Vec<_>>(),
777 );
778 }
779
780 const N: u32 = 6;
784
785 fn partition(labels: &[Option<u32>]) -> SameVmctxPartition {
788 let keys = labels
789 .iter()
790 .map(|l| match l {
791 Some(l) => key(*l),
792 None => unknown(),
793 })
794 .collect::<Vec<_>>();
795 SameVmctxPartition::from_keys(&mut HashMap::new(), &keys)
796 }
797
798 fn decode(bytes: &[u8]) -> SameVmctxPartition {
805 let byte = |i: usize| bytes.get(i).copied().unwrap_or(0);
806 match byte(0) % 8 {
807 0 => SameVmctxPartition::Top,
808 1 => SameVmctxPartition::bottom(),
809 _ => {
810 let labels = (0..N)
811 .map(|i| match byte(1 + i as usize) % 5 {
812 4 => None,
814 l => Some(u32::from(l)),
815 })
816 .collect::<Vec<_>>();
817 partition(&labels)
818 }
819 }
820 }
821
822 fn decode3(bytes: &[u8]) -> (SameVmctxPartition, SameVmctxPartition, SameVmctxPartition) {
824 let chunk = 1 + N as usize;
825 let at = |i: usize| decode(bytes.get(i * chunk..).unwrap_or(&[]));
826 (at(0), at(1), at(2))
827 }
828
829 fn meet(a: &SameVmctxPartition, b: &SameVmctxPartition) -> SameVmctxPartition {
832 SameVmctxPartition::meet(&mut HashMap::new(), a.clone(), b.clone())
833 }
834
835 fn reference_meet(a: &SameVmctxPartition, b: &SameVmctxPartition) -> Vec<Vec<u32>> {
839 let mut assigned = vec![false; N as usize];
840 let mut blocks = Vec::new();
841 for i in 0..N {
842 if assigned[i as usize] {
843 continue;
844 }
845 let mut block = Vec::new();
846 for j in i..N {
847 let (x, y) = (FuncIndex::from_u32(i), FuncIndex::from_u32(j));
848 if !assigned[j as usize] && a.same_block(x, y) && b.same_block(x, y) {
849 assigned[j as usize] = true;
850 block.push(j);
851 }
852 }
853 blocks.push(block);
854 }
855 blocks
856 }
857
858 #[test]
859 fn top_and_bottom_are_the_lattice_bounds() {
860 let top = SameVmctxPartition::Top;
861 let bottom = SameVmctxPartition::bottom();
862 let p = partition(&[Some(0), Some(0), Some(1), Some(1), None, None]);
863
864 assert_eq!(meet(&top, &p).blocks(N), p.blocks(N));
865 assert_eq!(meet(&p, &top).blocks(N), p.blocks(N));
866 assert_eq!(meet(&bottom, &p).blocks(N), bottom.blocks(N));
867 assert_eq!(meet(&p, &bottom).blocks(N), bottom.blocks(N));
868
869 assert!(p.refines(&top, N));
870 assert!(bottom.refines(&p, N));
871 assert!(!top.refines(&p, N));
872 assert!(!p.refines(&bottom, N));
873 }
874
875 #[test]
876 fn meet_is_idempotent() {
877 for p in [
878 SameVmctxPartition::Top,
879 SameVmctxPartition::bottom(),
880 partition(&[Some(0), Some(0), Some(1), Some(1), Some(2), None]),
881 partition(&[Some(3), Some(3), Some(3), Some(3), Some(3), Some(3)]),
882 ] {
883 assert_eq!(meet(&p, &p).blocks(N), p.blocks(N));
884 }
885 }
886
887 #[test]
888 fn meet_laws_hold_on_random_partitions() -> CheckResult<Vec<u8>> {
889 let mutator = m::default::<Vec<u8>>().map(|_ctx, bytes| {
890 bytes.truncate(3 * (1 + N as usize));
891 Ok(())
892 });
893
894 property_check().run_with(mutator, [Vec::new()], |bytes| {
895 let (a, b, c) = decode3(bytes);
896
897 let ab = meet(&a, &b);
899 let ba = meet(&b, &a);
900 assert_eq!(ab.blocks(N), ba.blocks(N), "meet is not commutative");
901
902 let ab_c = meet(&ab, &c);
904 let a_bc = meet(&a, &meet(&b, &c));
905 assert_eq!(ab_c.blocks(N), a_bc.blocks(N), "meet is not associative");
906
907 assert_eq!(
909 meet(&a, &a).blocks(N),
910 a.blocks(N),
911 "meet is not idempotent"
912 );
913
914 assert_eq!(
916 ab.blocks(N),
917 reference_meet(&a, &b),
918 "meet disagrees with the reference meet",
919 );
920
921 assert!(ab.refines(&a, N), "meet does not refine its left operand");
923 assert!(ab.refines(&b, N), "meet does not refine its right operand");
924
925 if c.refines(&a, N) && c.refines(&b, N) {
927 assert!(
928 c.refines(&ab, N),
929 "meet is not the greatest lower bound of its operands",
930 );
931 }
932
933 assert!(
936 meet(&ab, &c).refines(&meet(&a, &c), N),
937 "meet is not monotone in its left argument",
938 );
939 assert!(
940 meet(&c, &ab).refines(&meet(&c, &a), N),
941 "meet is not monotone in its right argument",
942 );
943
944 assert_eq!(
946 a.refines(&b, N),
947 ab.blocks(N) == a.blocks(N),
948 "`a <= b` and `a /\\ b == a` disagree",
949 );
950
951 Ok::<_, String>(())
952 })
953 }
954
955 #[test]
956 fn representatives_are_least_members_on_random_partitions() -> CheckResult<Vec<u8>> {
957 let mutator = m::default::<Vec<u8>>().map(|_ctx, bytes| {
958 bytes.truncate(1 + N as usize);
959 Ok(())
960 });
961
962 property_check().run_with(mutator, [Vec::new()], |bytes| {
963 let p = decode(bytes);
964 for block in p.blocks(N) {
965 let least = *block.first().unwrap();
966 for i in block {
967 assert_eq!(
968 p.representative(FuncIndex::from_u32(i)).as_u32(),
969 least,
970 "every member of a block must name the same representative",
971 );
972 }
973 }
974 Ok::<_, String>(())
975 })
976 }
977}