1use std::fmt;
17
18#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
28pub enum Opcode {
29 IConst,
34 FConst,
36 Splat,
38 GlobalAddr,
40
41 Add,
44 Sub,
46 Mul,
48 SDiv,
50 UDiv,
52 SRem,
54 URem,
56 And,
58 Or,
60 Xor,
62 Shl,
64 LShr,
66 AShr,
68 FAdd,
70 FSub,
72 FMul,
74 FDiv,
76 FRem,
78 FNeg,
80 Fma,
82
83 ICmp,
86 FCmp,
88
89 Trunc,
92 SExt,
94 ZExt,
96 FPTrunc,
98 FPExt,
100 FPToSI,
102 FPToUI,
104 SIToFP,
106 UIToFP,
108 PtrToInt,
110 IntToPtr,
112 Bitcast,
114
115 Alloca,
118 Load,
120 Store,
122 PtrAdd,
124 Memcpy,
126 Memmove,
128 Memset,
130 AtomicLoad,
132 AtomicStore,
134 AtomicRmw,
136 Cmpxchg,
138 Fence,
140
141 Jump,
144 BrIf,
146 Switch,
148 Return,
150 Unreachable,
152
153 Call,
156 CallIndirect,
158 TailCall,
160
161 Ctlz,
164 Cttz,
166 Ctpop,
168 Bswap,
170 Bitreverse,
172 SAddOverflow,
174 UAddOverflow,
176 SSubOverflow,
178 USubOverflow,
180 SMulOverflow,
182 UMulOverflow,
184 Expect,
186 UnreachableHint,
188 Prefetch,
190 FrameAddress,
192 ReturnAddress,
194 VaStart,
196 VaArg,
198 VaEnd,
200 VaCopy,
202 StackSave,
204 StackRestore,
206 SetjmpMarker,
208 LongjmpMarker,
210 TargetIntrinsic,
212
213 InlineAsm,
215}
216
217impl Opcode {
218 #[must_use]
220 pub const fn name(self) -> &'static str {
221 match self {
222 Self::IConst => "iconst",
223 Self::FConst => "fconst",
224 Self::Splat => "splat",
225 Self::GlobalAddr => "global_addr",
226 Self::Add => "add",
227 Self::Sub => "sub",
228 Self::Mul => "mul",
229 Self::SDiv => "sdiv",
230 Self::UDiv => "udiv",
231 Self::SRem => "srem",
232 Self::URem => "urem",
233 Self::And => "and",
234 Self::Or => "or",
235 Self::Xor => "xor",
236 Self::Shl => "shl",
237 Self::LShr => "lshr",
238 Self::AShr => "ashr",
239 Self::FAdd => "fadd",
240 Self::FSub => "fsub",
241 Self::FMul => "fmul",
242 Self::FDiv => "fdiv",
243 Self::FRem => "frem",
244 Self::FNeg => "fneg",
245 Self::Fma => "fma",
246 Self::ICmp => "icmp",
247 Self::FCmp => "fcmp",
248 Self::Trunc => "trunc",
249 Self::SExt => "sext",
250 Self::ZExt => "zext",
251 Self::FPTrunc => "fptrunc",
252 Self::FPExt => "fpext",
253 Self::FPToSI => "fptosi",
254 Self::FPToUI => "fptoui",
255 Self::SIToFP => "sitofp",
256 Self::UIToFP => "uitofp",
257 Self::PtrToInt => "ptrtoint",
258 Self::IntToPtr => "inttoptr",
259 Self::Bitcast => "bitcast",
260 Self::Alloca => "alloca",
261 Self::Load => "load",
262 Self::Store => "store",
263 Self::PtrAdd => "ptr_add",
264 Self::Memcpy => "memcpy",
265 Self::Memmove => "memmove",
266 Self::Memset => "memset",
267 Self::AtomicLoad => "atomic_load",
268 Self::AtomicStore => "atomic_store",
269 Self::AtomicRmw => "atomic_rmw",
270 Self::Cmpxchg => "cmpxchg",
271 Self::Fence => "fence",
272 Self::Jump => "jump",
273 Self::BrIf => "br_if",
274 Self::Switch => "switch",
275 Self::Return => "return",
276 Self::Unreachable => "unreachable",
277 Self::Call => "call",
278 Self::CallIndirect => "call_indirect",
279 Self::TailCall => "tail_call",
280 Self::Ctlz => "ctlz",
281 Self::Cttz => "cttz",
282 Self::Ctpop => "ctpop",
283 Self::Bswap => "bswap",
284 Self::Bitreverse => "bitreverse",
285 Self::SAddOverflow => "sadd_overflow",
286 Self::UAddOverflow => "uadd_overflow",
287 Self::SSubOverflow => "ssub_overflow",
288 Self::USubOverflow => "usub_overflow",
289 Self::SMulOverflow => "smul_overflow",
290 Self::UMulOverflow => "umul_overflow",
291 Self::Expect => "expect",
292 Self::UnreachableHint => "unreachable_hint",
293 Self::Prefetch => "prefetch",
294 Self::FrameAddress => "frame_address",
295 Self::ReturnAddress => "return_address",
296 Self::VaStart => "va_start",
297 Self::VaArg => "va_arg",
298 Self::VaEnd => "va_end",
299 Self::VaCopy => "va_copy",
300 Self::StackSave => "stacksave",
301 Self::StackRestore => "stackrestore",
302 Self::SetjmpMarker => "setjmp_marker",
303 Self::LongjmpMarker => "longjmp_marker",
304 Self::TargetIntrinsic => "target_intrinsic",
305 Self::InlineAsm => "inline_asm",
306 }
307 }
308
309 pub fn all() -> impl Iterator<Item = Self> {
314 ALL.iter().copied()
315 }
316
317 #[must_use]
319 pub fn from_name(name: &str) -> Option<Self> {
320 ALL.iter().copied().find(|op| op.name() == name)
321 }
322
323 #[must_use]
329 pub const fn is_terminator(self) -> bool {
330 matches!(
331 self,
332 Self::Jump
333 | Self::BrIf
334 | Self::Switch
335 | Self::Return
336 | Self::Unreachable
337 | Self::TailCall
338 )
339 }
340
341 #[must_use]
347 pub const fn is_commutative(self) -> bool {
348 matches!(
349 self,
350 Self::Add
351 | Self::Mul
352 | Self::And
353 | Self::Or
354 | Self::Xor
355 | Self::FAdd
356 | Self::FMul
357 | Self::SAddOverflow
358 | Self::UAddOverflow
359 | Self::SMulOverflow
360 | Self::UMulOverflow
361 )
362 }
363
364 #[must_use]
371 pub const fn has_effects(self) -> bool {
372 !matches!(
373 self,
374 Self::IConst
375 | Self::FConst
376 | Self::Splat
377 | Self::GlobalAddr
378 | Self::Add
379 | Self::Sub
380 | Self::Mul
381 | Self::SDiv
382 | Self::UDiv
383 | Self::SRem
384 | Self::URem
385 | Self::And
386 | Self::Or
387 | Self::Xor
388 | Self::Shl
389 | Self::LShr
390 | Self::AShr
391 | Self::FAdd
392 | Self::FSub
393 | Self::FMul
394 | Self::FDiv
395 | Self::FRem
396 | Self::FNeg
397 | Self::Fma
398 | Self::ICmp
399 | Self::FCmp
400 | Self::Trunc
401 | Self::SExt
402 | Self::ZExt
403 | Self::FPTrunc
404 | Self::FPExt
405 | Self::FPToSI
406 | Self::FPToUI
407 | Self::SIToFP
408 | Self::UIToFP
409 | Self::PtrToInt
410 | Self::IntToPtr
411 | Self::Bitcast
412 | Self::PtrAdd
413 | Self::Ctlz
414 | Self::Cttz
415 | Self::Ctpop
416 | Self::Bswap
417 | Self::Bitreverse
418 | Self::SAddOverflow
419 | Self::UAddOverflow
420 | Self::SSubOverflow
421 | Self::USubOverflow
422 | Self::SMulOverflow
423 | Self::UMulOverflow
424 | Self::Expect
425 | Self::FrameAddress
426 | Self::ReturnAddress
427 )
428 }
429
430 #[must_use]
437 pub const fn results(self) -> Option<u8> {
438 match self {
439 Self::Call | Self::CallIndirect | Self::InlineAsm => None,
440 Self::Cmpxchg
441 | Self::SAddOverflow
442 | Self::UAddOverflow
443 | Self::SSubOverflow
444 | Self::USubOverflow
445 | Self::SMulOverflow
446 | Self::UMulOverflow => Some(2),
447 Self::Store
448 | Self::Memcpy
449 | Self::Memmove
450 | Self::Memset
451 | Self::AtomicStore
452 | Self::Fence
453 | Self::Prefetch
454 | Self::VaStart
455 | Self::VaEnd
456 | Self::VaCopy
457 | Self::StackRestore
458 | Self::UnreachableHint
459 | Self::SetjmpMarker
460 | Self::LongjmpMarker => Some(0),
461 _ if self.is_terminator() => Some(0),
462 _ => Some(1),
463 }
464 }
465
466 #[must_use]
474 pub const fn extra_kind(self) -> ExtraKind {
475 match self {
476 Self::IConst | Self::FConst | Self::Splat => ExtraKind::Imm,
477 Self::GlobalAddr | Self::TargetIntrinsic => ExtraKind::Symbol,
478 Self::ICmp => ExtraKind::IntPred,
479 Self::FCmp => ExtraKind::FloatPred,
480 Self::Alloca
481 | Self::Load
482 | Self::Store
483 | Self::Memcpy
484 | Self::Memmove
485 | Self::Memset
486 | Self::AtomicLoad
487 | Self::AtomicStore
488 | Self::Cmpxchg => ExtraKind::Mem,
489 Self::AtomicRmw => ExtraKind::Rmw,
490 Self::Fence => ExtraKind::Order,
491 Self::Jump | Self::BrIf => ExtraKind::Targets,
492 Self::Switch => ExtraKind::Switch,
493 Self::Call | Self::CallIndirect | Self::TailCall => ExtraKind::Call,
494 Self::InlineAsm => ExtraKind::Asm,
495 _ => ExtraKind::None,
496 }
497 }
498}
499
500#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
505pub enum ExtraKind {
506 None,
508 Imm,
510 Symbol,
512 IntPred,
514 FloatPred,
516 Mem,
518 Rmw,
520 Order,
522 Targets,
524 Call,
526 Switch,
528 Asm,
530}
531
532impl ExtraKind {
533 #[must_use]
535 pub const fn name(self) -> &'static str {
536 match self {
537 Self::None => "nothing",
538 Self::Imm => "a constant",
539 Self::Symbol => "a name",
540 Self::IntPred => "an integer comparison",
541 Self::FloatPred => "a floating point comparison",
542 Self::Mem => "an access",
543 Self::Rmw => "a read-modify-write",
544 Self::Order => "an ordering",
545 Self::Targets => "branch targets",
546 Self::Call => "a call",
547 Self::Switch => "a switch",
548 Self::Asm => "inline assembly",
549 }
550 }
551}
552
553impl fmt::Display for Opcode {
554 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
555 f.write_str(self.name())
556 }
557}
558
559static ALL: &[Opcode] = &[
565 Opcode::IConst,
566 Opcode::FConst,
567 Opcode::Splat,
568 Opcode::GlobalAddr,
569 Opcode::Add,
570 Opcode::Sub,
571 Opcode::Mul,
572 Opcode::SDiv,
573 Opcode::UDiv,
574 Opcode::SRem,
575 Opcode::URem,
576 Opcode::And,
577 Opcode::Or,
578 Opcode::Xor,
579 Opcode::Shl,
580 Opcode::LShr,
581 Opcode::AShr,
582 Opcode::FAdd,
583 Opcode::FSub,
584 Opcode::FMul,
585 Opcode::FDiv,
586 Opcode::FRem,
587 Opcode::FNeg,
588 Opcode::Fma,
589 Opcode::ICmp,
590 Opcode::FCmp,
591 Opcode::Trunc,
592 Opcode::SExt,
593 Opcode::ZExt,
594 Opcode::FPTrunc,
595 Opcode::FPExt,
596 Opcode::FPToSI,
597 Opcode::FPToUI,
598 Opcode::SIToFP,
599 Opcode::UIToFP,
600 Opcode::PtrToInt,
601 Opcode::IntToPtr,
602 Opcode::Bitcast,
603 Opcode::Alloca,
604 Opcode::Load,
605 Opcode::Store,
606 Opcode::PtrAdd,
607 Opcode::Memcpy,
608 Opcode::Memmove,
609 Opcode::Memset,
610 Opcode::AtomicLoad,
611 Opcode::AtomicStore,
612 Opcode::AtomicRmw,
613 Opcode::Cmpxchg,
614 Opcode::Fence,
615 Opcode::Jump,
616 Opcode::BrIf,
617 Opcode::Switch,
618 Opcode::Return,
619 Opcode::Unreachable,
620 Opcode::Call,
621 Opcode::CallIndirect,
622 Opcode::TailCall,
623 Opcode::Ctlz,
624 Opcode::Cttz,
625 Opcode::Ctpop,
626 Opcode::Bswap,
627 Opcode::Bitreverse,
628 Opcode::SAddOverflow,
629 Opcode::UAddOverflow,
630 Opcode::SSubOverflow,
631 Opcode::USubOverflow,
632 Opcode::SMulOverflow,
633 Opcode::UMulOverflow,
634 Opcode::Expect,
635 Opcode::UnreachableHint,
636 Opcode::Prefetch,
637 Opcode::FrameAddress,
638 Opcode::ReturnAddress,
639 Opcode::VaStart,
640 Opcode::VaArg,
641 Opcode::VaEnd,
642 Opcode::VaCopy,
643 Opcode::StackSave,
644 Opcode::StackRestore,
645 Opcode::SetjmpMarker,
646 Opcode::LongjmpMarker,
647 Opcode::TargetIntrinsic,
648 Opcode::InlineAsm,
649];
650
651#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
656pub enum IntPred {
657 Eq,
659 Ne,
661 Slt,
663 Sle,
665 Sgt,
667 Sge,
669 Ult,
671 Ule,
673 Ugt,
675 Uge,
677}
678
679impl IntPred {
680 #[must_use]
682 pub const fn name(self) -> &'static str {
683 match self {
684 Self::Eq => "eq",
685 Self::Ne => "ne",
686 Self::Slt => "slt",
687 Self::Sle => "sle",
688 Self::Sgt => "sgt",
689 Self::Sge => "sge",
690 Self::Ult => "ult",
691 Self::Ule => "ule",
692 Self::Ugt => "ugt",
693 Self::Uge => "uge",
694 }
695 }
696
697 #[must_use]
699 pub fn from_name(name: &str) -> Option<Self> {
700 Self::all().find(|pred| pred.name() == name)
701 }
702
703 pub fn all() -> impl Iterator<Item = Self> {
705 [
706 Self::Eq,
707 Self::Ne,
708 Self::Slt,
709 Self::Sle,
710 Self::Sgt,
711 Self::Sge,
712 Self::Ult,
713 Self::Ule,
714 Self::Ugt,
715 Self::Uge,
716 ]
717 .into_iter()
718 }
719
720 #[must_use]
722 pub const fn inverse(self) -> Self {
723 match self {
724 Self::Eq => Self::Ne,
725 Self::Ne => Self::Eq,
726 Self::Slt => Self::Sge,
727 Self::Sge => Self::Slt,
728 Self::Sle => Self::Sgt,
729 Self::Sgt => Self::Sle,
730 Self::Ult => Self::Uge,
731 Self::Uge => Self::Ult,
732 Self::Ule => Self::Ugt,
733 Self::Ugt => Self::Ule,
734 }
735 }
736
737 #[must_use]
739 pub const fn swapped(self) -> Self {
740 match self {
741 Self::Eq => Self::Eq,
742 Self::Ne => Self::Ne,
743 Self::Slt => Self::Sgt,
744 Self::Sgt => Self::Slt,
745 Self::Sle => Self::Sge,
746 Self::Sge => Self::Sle,
747 Self::Ult => Self::Ugt,
748 Self::Ugt => Self::Ult,
749 Self::Ule => Self::Uge,
750 Self::Uge => Self::Ule,
751 }
752 }
753
754 #[must_use]
756 pub const fn is_signed(self) -> bool {
757 matches!(self, Self::Slt | Self::Sle | Self::Sgt | Self::Sge)
758 }
759}
760
761impl fmt::Display for IntPred {
762 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
763 f.write_str(self.name())
764 }
765}
766
767#[derive(Clone, Copy, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
772pub enum FloatPred {
773 False,
775 Oeq,
777 Ogt,
779 Oge,
781 Olt,
783 Ole,
785 One,
787 Ord,
789 Uno,
791 Ueq,
793 Ugt,
795 Uge,
797 Ult,
799 Ule,
801 Une,
803 True,
805}
806
807impl FloatPred {
808 #[must_use]
810 pub const fn name(self) -> &'static str {
811 match self {
812 Self::False => "false",
813 Self::Oeq => "oeq",
814 Self::Ogt => "ogt",
815 Self::Oge => "oge",
816 Self::Olt => "olt",
817 Self::Ole => "ole",
818 Self::One => "one",
819 Self::Ord => "ord",
820 Self::Uno => "uno",
821 Self::Ueq => "ueq",
822 Self::Ugt => "ugt",
823 Self::Uge => "uge",
824 Self::Ult => "ult",
825 Self::Ule => "ule",
826 Self::Une => "une",
827 Self::True => "true",
828 }
829 }
830
831 #[must_use]
833 pub fn from_name(name: &str) -> Option<Self> {
834 Self::all().find(|pred| pred.name() == name)
835 }
836
837 pub fn all() -> impl Iterator<Item = Self> {
839 [
840 Self::False,
841 Self::Oeq,
842 Self::Ogt,
843 Self::Oge,
844 Self::Olt,
845 Self::Ole,
846 Self::One,
847 Self::Ord,
848 Self::Uno,
849 Self::Ueq,
850 Self::Ugt,
851 Self::Uge,
852 Self::Ult,
853 Self::Ule,
854 Self::Une,
855 Self::True,
856 ]
857 .into_iter()
858 }
859
860 #[must_use]
862 pub const fn inverse(self) -> Self {
863 match self {
864 Self::False => Self::True,
865 Self::Oeq => Self::Une,
866 Self::Ogt => Self::Ule,
867 Self::Oge => Self::Ult,
868 Self::Olt => Self::Uge,
869 Self::Ole => Self::Ugt,
870 Self::One => Self::Ueq,
871 Self::Ord => Self::Uno,
872 Self::Uno => Self::Ord,
873 Self::Ueq => Self::One,
874 Self::Ugt => Self::Ole,
875 Self::Uge => Self::Olt,
876 Self::Ult => Self::Oge,
877 Self::Ule => Self::Ogt,
878 Self::Une => Self::Oeq,
879 Self::True => Self::False,
880 }
881 }
882
883 #[must_use]
885 pub const fn swapped(self) -> Self {
886 match self {
887 Self::Ogt => Self::Olt,
888 Self::Olt => Self::Ogt,
889 Self::Oge => Self::Ole,
890 Self::Ole => Self::Oge,
891 Self::Ugt => Self::Ult,
892 Self::Ult => Self::Ugt,
893 Self::Uge => Self::Ule,
894 Self::Ule => Self::Uge,
895 same => same,
896 }
897 }
898
899 #[must_use]
904 pub const fn is_ordered(self) -> bool {
905 matches!(
906 self,
907 Self::Oeq | Self::Ogt | Self::Oge | Self::Olt | Self::Ole | Self::One | Self::Ord
908 )
909 }
910}
911
912impl fmt::Display for FloatPred {
913 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
914 f.write_str(self.name())
915 }
916}
917
918#[cfg(test)]
919mod tests {
920 use super::*;
921
922 #[test]
923 fn every_opcode_is_in_the_table() {
924 assert_eq!(ALL.len(), Opcode::InlineAsm as usize + 1);
928 for (position, &op) in ALL.iter().enumerate() {
929 assert_eq!(op as usize, position, "{op} is out of order in ALL");
930 }
931 }
932
933 #[test]
934 fn every_opcode_has_its_own_name_and_finds_it_again() {
935 let mut names: Vec<&str> = Opcode::all().map(Opcode::name).collect();
936 let total = names.len();
937 names.sort_unstable();
938 names.dedup();
939 assert_eq!(names.len(), total, "two opcodes share a name");
940 for op in Opcode::all() {
941 assert_eq!(Opcode::from_name(op.name()), Some(op));
942 }
943 assert_eq!(Opcode::from_name("phi"), None);
944 assert_eq!(Opcode::from_name("getelementptr"), None);
945 assert_eq!(Opcode::from_name(""), None);
946 }
947
948 #[test]
949 fn the_terminators_are_the_ones_control_leaves_by() {
950 let terminators: Vec<&str> =
951 Opcode::all().filter(|op| op.is_terminator()).map(Opcode::name).collect();
952 assert_eq!(terminators, ["jump", "br_if", "switch", "return", "unreachable", "tail_call"]);
953 }
954
955 #[test]
956 fn a_terminator_produces_nothing() {
957 for op in Opcode::all().filter(|op| op.is_terminator()) {
958 assert_eq!(op.results(), Some(0), "{op}");
959 }
960 }
961
962 #[test]
963 fn the_pair_producing_opcodes_are_the_ones_with_a_flag_beside_the_value() {
964 let pairs: Vec<&str> =
965 Opcode::all().filter(|op| op.results() == Some(2)).map(Opcode::name).collect();
966 assert_eq!(
967 pairs,
968 [
969 "cmpxchg",
970 "sadd_overflow",
971 "uadd_overflow",
972 "ssub_overflow",
973 "usub_overflow",
974 "smul_overflow",
975 "umul_overflow"
976 ]
977 );
978 }
979
980 #[test]
981 fn memory_has_effects_and_arithmetic_does_not() {
982 for op in [Opcode::Load, Opcode::Store, Opcode::Call, Opcode::Alloca, Opcode::Fence] {
983 assert!(op.has_effects(), "{op}");
984 }
985 for op in [Opcode::Add, Opcode::FDiv, Opcode::ICmp, Opcode::PtrAdd, Opcode::IConst] {
986 assert!(!op.has_effects(), "{op}");
987 }
988 }
989
990 #[test]
991 fn commuting_is_only_claimed_where_it_holds() {
992 assert!(Opcode::Add.is_commutative());
993 assert!(Opcode::FAdd.is_commutative());
994 assert!(!Opcode::Sub.is_commutative());
995 assert!(!Opcode::FDiv.is_commutative());
996 assert!(!Opcode::Shl.is_commutative());
997 }
998
999 #[test]
1000 fn an_integer_predicate_inverts_and_swaps_back_to_itself() {
1001 for pred in IntPred::all() {
1002 assert_eq!(pred.inverse().inverse(), pred);
1003 assert_eq!(pred.swapped().swapped(), pred);
1004 assert_eq!(IntPred::from_name(pred.name()), Some(pred));
1005 }
1006 assert_eq!(IntPred::Slt.inverse(), IntPred::Sge);
1007 assert_eq!(IntPred::Slt.swapped(), IntPred::Sgt);
1008 assert_eq!(IntPred::from_name("lt"), None);
1009 }
1010
1011 #[test]
1012 fn a_floating_predicate_inverts_across_the_ordered_line() {
1013 for pred in FloatPred::all() {
1014 assert_eq!(pred.inverse().inverse(), pred);
1015 assert_eq!(pred.swapped().swapped(), pred);
1016 assert_eq!(FloatPred::from_name(pred.name()), Some(pred));
1017 }
1018 for pred in FloatPred::all().filter(|p| !matches!(p, FloatPred::False | FloatPred::True)) {
1022 assert_ne!(pred.is_ordered(), pred.inverse().is_ordered(), "{pred}");
1023 }
1024 assert_eq!(FloatPred::Olt.inverse(), FloatPred::Uge);
1025 assert_eq!(FloatPred::Olt.swapped(), FloatPred::Ogt);
1026 }
1027
1028 #[test]
1029 fn swapping_a_predicate_keeps_it_ordered_or_unordered() {
1030 for pred in FloatPred::all() {
1031 assert_eq!(pred.is_ordered(), pred.swapped().is_ordered(), "{pred}");
1032 }
1033 for pred in IntPred::all() {
1034 assert_eq!(pred.is_signed(), pred.swapped().is_signed(), "{pred}");
1035 }
1036 }
1037
1038 #[test]
1039 fn no_two_predicates_share_a_name_within_their_family() {
1040 for names in [
1041 IntPred::all().map(IntPred::name).collect::<Vec<_>>(),
1042 FloatPred::all().map(FloatPred::name).collect::<Vec<_>>(),
1043 ] {
1044 let total = names.len();
1045 let mut names = names;
1046 names.sort_unstable();
1047 names.dedup();
1048 assert_eq!(names.len(), total);
1049 }
1050 }
1051}