13constexpr std::optional<unsigned> idx_shift_amount(
u64 size,
u64 b) {
14 auto width = idx_shift_width(
size);
15 if (b >= width)
return {};
16 return static_cast<unsigned>(b);
19constexpr u64 idx_unsigned_max(
u64 size) {
return size == 0 ? std::numeric_limits<u64>::max() :
size - 1; }
22 return size == 0 ?
static_cast<u64>(std::numeric_limits<s64>::max()) : (
size - 1) / 2;
26 return size == 0 ?
static_cast<u64>(std::numeric_limits<s64>::max()) + 1_u64 :
size / 2;
29constexpr u64 idx_signed_abs(
s64 x) {
return x >= 0 ?
static_cast<u64>(x) : static_cast<
u64>(-(x + 1)) + 1_u64; }
32 if (
abs ==
static_cast<u64>(std::numeric_limits<s64>::max()) + 1_u64)
return std::numeric_limits<s64>::min();
33 return -
static_cast<s64>(
abs);
36constexpr u64 idx_pow2(
unsigned k) {
return 1_u64 << k; }
40 if (
size == 0)
return x >
static_cast<u64>(std::numeric_limits<s64>::max());
43 return x > (
size - 1) / 2;
48 if (
size == 0)
return static_cast<s64>(x);
50 const u64 max_pos = (
size - 1) / 2;
51 if (x <= max_pos)
return static_cast<s64>(x);
54 return -
static_cast<s64>(
size - x);
58 if (
size == 0)
return static_cast<u64>(x);
59 return x >= 0 ?
static_cast<u64>(x) :
size - static_cast<
u64>(-x);
63 if (
size == 0)
return static_cast<u64>(x);
64 if (x >= 0)
return static_cast<u64>(x) %
size;
66 auto rem = idx_signed_abs(x) %
size;
71 if (
size == 0)
return a + b <
a;
72 return a >
size - 1 - b;
75constexpr bool idx_sub_nuw(
u64,
u64 a,
u64 b) {
return a < b; }
78 if (
a == 0 || b == 0)
return false;
80 if (
size == 0)
return b > std::numeric_limits<u64>::max() /
a;
81 return b > (
size - 1) /
a;
85 if (
size == 0)
return a + b;
90 if (
size == 0)
return a - b;
91 return (
a >= b) ? (
a - b) : (
size - (b -
a));
95 if (
size == 0)
return a * b;
100 if (b % 2_u64 != 0)
r = idx_add(
size, r,
a);
102 if (b)
a = idx_add(
size,
a,
a);
108 const bool sa = idx_sign(
size,
a);
109 const bool sb = idx_sign(
size, b);
111 const bool sr = idx_sign(
size, r);
112 return (sa == sb) && (sr != sa);
116 const bool sa = idx_sign(
size,
a);
117 const bool sb = idx_sign(
size, b);
119 const bool sr = idx_sign(
size, r);
120 return (sa != sb) && (sr != sa);
125 const s64 y = idx_sext(
size, b);
127 if (x == 0 || y == 0)
return false;
129 const s64 min_val =
size == 0 ? std::numeric_limits<s64>::min() : -static_cast<
s64>(
size / 2);
130 const s64 max_val =
size == 0 ? std::numeric_limits<s64>::max() : static_cast<
s64>((
size - 1) / 2);
132 if (x == -1)
return size % 2 == 0 && y == min_val;
133 if (y == -1)
return size % 2 == 0 && x == min_val;
137 return x > max_val / y;
139 return y < min_val / x;
141 return x < min_val / y;
143 return x < max_val / y;
146constexpr std::optional<u64> idx_udiv([[maybe_unused]]
u64 size,
u64 a,
u64 b) {
147 if (b == 0)
return {};
151constexpr std::optional<u64> idx_urem([[maybe_unused]]
u64 size,
u64 a,
u64 b) {
152 if (b == 0)
return {};
157 const bool sa = idx_sign(
size,
a);
158 const bool sb = idx_sign(
size, b);
160 if (
a == b)
return false;
161 if (!sa && sb)
return false;
162 if (sa && !sb)
return true;
170 const s64 y = idx_sext(
size, b);
172 if (y == 0)
return true;
174 const s64 min_val = [&] {
175 if (
size == 0)
return std::numeric_limits<s64>::min();
176 return -
static_cast<s64>(
size / 2);
179 return size % 2 == 0 && x == min_val && y == -1;
184 const s64 y = idx_sext(
size, b);
185 return idx_from_signed(
size, x / y);
190 const s64 y = idx_sext(
size, b);
191 return idx_from_signed(
size, x % y);
194constexpr bool idx_shl_nuw(
u64 size,
u64 a,
unsigned k) {
196 u64 max = idx_unsigned_max(
size);
199 if (x > max / 2_u64)
return true;
206constexpr bool idx_shl_nsw(
u64 size,
u64 a,
unsigned k) {
209 u64 y =
static_cast<u64>(x);
210 u64 max = idx_signed_max(
size);
212 if (y > max / 2_u64)
return true;
216 u64 y = idx_signed_abs(x);
217 u64 min = idx_signed_min_abs(
size);
219 if (y > min / 2_u64)
return true;
228 auto k = idx_shift_amount(
size, b);
231 if (
nuw && idx_shl_nuw(
size,
a, *k))
return {};
232 if (
nsw && idx_shl_nsw(
size,
a, *k))
return {};
234 return idx_mul(
size,
a, idx_pow2(*k));
238 auto k = idx_shift_amount(
size, b);
240 return a / idx_pow2(*k);
244 auto k = idx_shift_amount(
size, b);
247 auto divisor = idx_pow2(*k);
248 auto x = idx_sext(
size,
a);
249 if (x >= 0)
return idx_from_signed(
size,
static_cast<s64>(
static_cast<u64>(x) / divisor));
251 auto q = (idx_signed_abs(x) + divisor - 1_u64) / divisor;
252 return idx_from_signed(
size, idx_neg(q));
257 const bool su = idx_sign(
size,
a);
258 const bool sv = idx_sign(
size, b);
269 return (
flags_t(
id) & rel) != 0;
272template<
class Id, Id
id>
273std::optional<u64> fold_idx(
u64 size,
u64 a,
u64 b, [[maybe_unused]]
bool nsw, [[maybe_unused]]
bool nuw) {
276 if constexpr (std::is_same_v<Id, wrap>) {
278 if (
nuw && idx_add_nuw(
size,
a, b))
return {};
279 if (
nsw && idx_add_nsw(
size,
a, b))
return {};
280 return idx_add(
size,
a, b);
283 if (
nuw && idx_sub_nuw(
size,
a, b))
return {};
284 if (
nsw && idx_sub_nsw(
size,
a, b))
return {};
285 return idx_sub(
size,
a, b);
288 if (
nuw && idx_mul_nuw(
size,
a, b))
return {};
289 if (
nsw && idx_mul_nsw(
size,
a, b))
return {};
290 return idx_mul(
size,
a, b);
296 static_assert(
false,
"missing wrap subtag");
299 }
else if constexpr (std::is_same_v<Id, shr>) {
300 if constexpr (
id ==
shr::a)
301 return idx_ashr(
size,
a, b);
302 else if constexpr (
id ==
shr::l)
303 return idx_lshr(
size,
a, b);
305 static_assert(
false,
"missing shr subtag");
307 }
else if constexpr (std::is_same_v<Id, div>) {
309 return idx_udiv(
size,
a, b);
312 return idx_urem(
size,
a, b);
315 if (idx_sdivrem_ub(
size,
a, b))
return {};
316 return idx_sdiv(
size,
a, b);
319 if (idx_sdivrem_ub(
size,
a, b))
return {};
320 return idx_srem(
size,
a, b);
323 static_assert(
false,
"missing div subtag");
326 }
else if constexpr (std::is_same_v<Id, icmp>) {
327 return u64(fold_icmp_idx<id>(
size,
a, b));
329 }
else if constexpr (std::is_same_v<Id, extrema>) {
331 return std::min(
a, b);
334 return std::max(
a, b);
337 return idx_slt(
size,
a, b) ?
a : b;
340 return idx_sgt(
size,
a, b) ?
a : b;
343 static_assert(
false,
"missing extrema subtag");
346 static_assert(
false,
"missing tag");
350template<
class Id, Id
id>
351const Def*
fold(World& world,
const Def* type,
const Def*&
a,
const Def*& b,
const Def*
mode = {}) {
352 if (
a->isa<
Bot>() || b->isa<
Bot>())
return world.bot(type);
356 assert(
a->type() == b->type());
360 bool nsw =
false,
nuw =
false;
361 if constexpr (std::is_same_v<Id, wrap>) {
363 if (!lm)
return nullptr;
369 if constexpr (std::is_same_v<Id, div>) {
370 if (*lb == 0)
return world.bot(type);
372 if constexpr (std::is_same_v<Id, icmp>)
373 return world.lit(type,
u64(fold_icmp_idx<id>(1, 0, 0)));
375 return world.lit(type, 0);
378 auto res = fold_idx<Id, id>(
size, *la, *lb,
nsw,
nuw);
379 return res ? world.lit(type, *res) : world.bot(type);
388const Def*
fold(World& world,
const Def* type,
const Def*&
a) {
389 if (
a->isa<
Bot>())
return world.bot(type);
394 if constexpr (std::is_same_v<Id, abs>) {
395 auto x = idx_sext(
size, *la);
396 if (x >= 0)
return world.lit(type,
static_cast<u64>(x));
398 auto y = idx_signed_abs(x);
399 if (
size % 2_u64 == 0 && y == idx_signed_min_abs(
size))
return world.lit(type, *la);
401 return world.lit(type, y);
403 static_assert(
false,
"missing tag");
422const Def* reassociate(Id
id, World& world,
const Def*
a,
const Def* b) {
427 auto la =
a->isa<
Lit>();
428 auto [x, y] = xy ? xy->template args<2>() : std::array<const Def*, 2>{
nullptr,
nullptr};
429 auto [z,
w] = zw ? zw->template args<2>() : std::array<const Def*, 2>{
nullptr,
nullptr};
434 auto make_op = [&world,
id](
const Def*
a,
const Def* b) {
435 if constexpr (std::is_same_v<Id, nat>)
436 return world.call(
id,
Defs{a, b});
441 if (la && lz)
return make_op(make_op(
a, z), w);
442 if (lx && lz)
return make_op(make_op(x, z), make_op(y, w));
443 if (lz)
return make_op(z, make_op(
a, w));
444 if (lx)
return make_op(x, make_op(y, b));
449const Def* merge_cmps(std::array<std::array<u64, 2>, 2> tab,
const Def*
a,
const Def* b) {
450 static_assert(
sizeof(
sub_t) == 1,
"if this ever changes, please adjust the logic below");
451 static constexpr size_t num_bits = std::bit_width(
Annex::num<Id>() - 1_u64);
453 auto& world =
a->world();
457 if (a_cmp && b_cmp && a_cmp->arg() == b_cmp->arg()) {
460 sub_t a_sub = a_cmp.sub();
461 sub_t b_sub = b_cmp.sub();
462 for (
size_t i = 0; i != num_bits; ++i, res >>= 1, a_sub >>= 1, b_sub >>= 1)
463 res |= tab[a_sub & 1][b_sub & 1] << 7_u8;
464 res >>= (7_u8 -
u8(num_bits));
466 if constexpr (std::is_same_v<Id, math::cmp>)
467 return world.call(
math::cmp(res), a_cmp->decurry()->decurry()->arg(), a_cmp->arg());
479 auto& world = type->world();
480 auto [
a, b] = arg->
projs<2>();
488 case nat::add:
return world.lit_nat(*la + *lb);
489 case nat::sub:
return *la < *lb ? world.lit_nat_0() : world.lit_nat(*la - *lb);
490 case nat::mul:
return world.lit_nat(*la * *lb);
491 case nat::div:
return *lb == 0 ? world.lit_nat_0() : world.lit_nat(*la / *lb);
492 case nat::rem:
return *lb == 0 ?
a : world.lit_nat(*la % *lb);
501 case nat::div:
return world.lit_nat_0();
502 case nat::rem:
return world.lit_nat_0();
506 if (*la == 1 &&
id ==
nat::mul)
return b;
513 case nat::div:
return world.lit_nat_0();
521 case nat::rem:
return world.lit_nat_0();
530 const Def* marg = m->arg();
531 auto [c, x] = marg->
projs<2>();
532 if (
auto lc =
Lit::isa(c); lc && *lc != 0 && *lc % *lb == 0) {
533 if (
id ==
nat::rem)
return world.lit_nat_0();
534 return world.call(
nat::mul,
Defs{world.lit_nat(*lc / *lb), x});
542 case nat::sub:
return world.lit_nat(0);
545 case nat::rem:
return world.lit_nat_0();
549 if (
auto res = reassociate<nat>(
id, world,
a, b))
return res;
551 return world.raw_app(type, callee, {
a, b});
556 auto& world = type->world();
558 if (
id ==
ncmp::t)
return world.lit_tt();
559 if (
id ==
ncmp::f)
return world.lit_ff();
561 auto [
a, b] = arg->
projs<2>();
565 constexpr auto eq_mask = fe::to_underlying(
ncmp::e) & 0xff;
566 if ((fe::to_underlying(
id) & eq_mask) != 0)
return world.lit_tt();
567 if (
id ==
ncmp::ne)
return world.lit_ff();
574 case ncmp:: e:
return world.lit_bool(*la == *lb);
575 case ncmp::ne:
return world.lit_bool(*la != *lb);
576 case ncmp::l :
return world.lit_bool(*la < *lb);
577 case ncmp::le:
return world.lit_bool(*la <= *lb);
578 case ncmp::g :
return world.lit_bool(*la > *lb);
579 case ncmp::ge:
return world.lit_bool(*la >= *lb);
580 default: std::unreachable();
586 return world.raw_app(type, callee, {
a, b});
591 auto& world = type->world();
592 auto callee = c->as<
App>();
593 auto [
a, b] = arg->
projs<2>();
595 if (
auto result = fold<icmp, id>(world, type,
a, b))
return result;
596 if (
id ==
icmp::f)
return world.lit_ff();
597 if (
id ==
icmp::t)
return world.lit_tt();
599 constexpr auto eq_mask = fe::to_underlying(
icmp::e) & 0xff;
600 if ((fe::to_underlying(
id) & eq_mask) != 0)
return world.lit_tt();
601 if (
id ==
icmp::ne)
return world.lit_ff();
604 return world.raw_app(type, callee, {
a, b});
609 auto& world = type->world();
610 auto callee = c->as<
App>();
611 auto [
a, b] = arg->
projs<2>();
612 if (
auto result = fold<extrema, id>(world, type,
a, b))
return result;
613 return world.raw_app(type, callee, {
a, b});
617 auto& world = type->world();
619 auto [_, actual_type] = type->projs<2>();
620 auto make_res = [&,
mem =
mem](
const Def* res) {
return world.tuple({
mem, res}); };
622 if (
auto result = fold<abs>(world, actual_type,
a))
return make_res(result);
628 auto& world = type->world();
629 auto callee = c->as<
App>();
630 auto s = callee->arg();
637 case bit1::f:
return world.lit_idx(*ls, 0);
638 case bit1::t:
return world.lit_idx(*ls, *ls - 1_u64);
644 if (
auto la =
Lit::isa(
a))
return world.lit_idx_mod(*ls, ~*la);
652 auto& world = type->world();
653 auto callee = c->as<
App>();
654 auto [
a, b] = arg->
projs<2>();
655 auto mode = callee->decurry()->arg();
656 auto s = callee->arg();
663 if (
auto res = merge_cmps<icmp>(tab,
a, b))
return res;
664 if (
auto res = merge_cmps<math::cmp>(tab,
a, b))
return res;
671 case bit2:: f:
return world.lit(type, 0);
672 case bit2:: t:
if (ls)
return world.lit(type, *ls-1_u64);
break;
673 case bit2:: fst:
return a;
674 case bit2:: snd:
return b;
682 if (la && lb && ls) {
684 case bit2::and_:
return world.lit_idx (*ls, *la & *lb);
685 case bit2:: or_:
return world.lit_idx (*ls, *la | *lb);
686 case bit2::xor_:
return world.lit_idx (*ls, *la ^ *lb);
687 case bit2::nand:
return world.lit_idx_mod(*ls, ~(*la & *lb));
688 case bit2:: nor:
return world.lit_idx_mod(*ls, ~(*la | *lb));
689 case bit2::nxor:
return world.lit_idx_mod(*ls, ~(*la ^ *lb));
690 case bit2:: iff:
return world.lit_idx_mod(*ls, ~ *la | *lb);
691 case bit2::niff:
return world.lit_idx (*ls, *la & ~*lb);
692 default: std::unreachable();
697 auto unary = [&](
bool x,
bool y,
const Def*
a) ->
const Def* {
698 if (!x && !y)
return world.lit(type, 0);
699 if ( x && y)
return ls ? world.lit(type, *ls-1_u64) :
nullptr;
700 if (!x && y)
return a;
707 if (
auto res = unary(tab[0][0], tab[1][1],
a))
return res;
712 if (
auto res = unary(tab[0][0], tab[0][1], b))
return res;
713 }
else if (ls && *la == *ls - 1_u64) {
714 if (
auto res = unary(tab[1][0], tab[1][1], b))
return res;
720 if (
auto res = unary(tab[0][0], tab[1][0],
a))
return res;
721 }
else if (ls && *lb == *ls - 1_u64) {
722 if (
auto res = unary(tab[0][1], tab[1][1],
a))
return res;
726 if (
auto res = reassociate<bit2>(
id, world,
a, b))
return res;
728 return world.raw_app(type, callee, {
a, b});
732 auto& world = type->world();
733 auto callee = c->as<
App>();
735 if (*i == 0)
return world.lit(type, 0);
737 if (*
s == 0 || *i < *
s)
return world.lit_idx(*
s, *i);
738 if (
auto m =
Lit::isa(callee->decurry()->arg()))
return *m ? world.bot(type) : world.lit_idx_mod(*
s, *i);
747 auto& world = type->world();
748 auto callee = c->as<
App>();
749 auto [
a, b] = arg->
projs<2>();
752 auto width = ls.transform(idx_shift_width);
754 if (
auto result = fold<shr, id>(world, type,
a, b))
return result;
756 if (
auto la =
Lit::isa(
a); la && *la == 0)
return a;
759 if (width && *lb >= *width)
return world.bot(type);
761 if (*lb == 0)
return a;
764 return world.raw_app(type, callee, {
a, b});
769 auto& world = type->world();
770 auto callee = c->as<
App>();
771 auto [
a, b] = arg->
projs<2>();
772 auto mode = callee->decurry()->arg();
775 auto width = ls.transform(idx_shift_width);
777 if (
auto result = fold<wrap, id>(world, type,
a, b,
mode))
return result;
788 }
else if (*la == 1) {
809 return world.call(
wrap::add,
mode,
Defs{a, world.lit_idx(*ls, idx_sub(*ls, 0, *lb))});
810 else if (
id ==
wrap::shl && width && *lb >= *width)
811 return world.bot(type);
817 if (ls && (*ls == 0 || *ls > 2))
return world.call(
wrap::mul,
mode,
Defs{world.lit(type, 2),
a});
819 case wrap::sub:
return world.lit(type, 0);
826 if (
auto res = reassociate<wrap>(
id, world,
a, b))
return res;
828 return world.raw_app(type, callee, {
a, b});
833 auto& world = full_type->
world();
835 auto [
a, b] = ab->projs<2>();
836 auto [_, type] = full_type->
projs<2>();
837 auto make_res = [&,
mem =
mem](
const Def* res) {
return world.tuple({
mem, res}); };
839 if (
auto result = fold<div, id>(world, type,
a, b))
return make_res(result);
842 if (*la == 0)
return make_res(
a);
846 if (*lb == 0)
return make_res(world.bot(type));
852 case div::srem:
return make_res(world.lit(type, 0));
853 case div::urem:
return make_res(world.lit(type, 0));
863 return make_res(world.lit(type, 1));
865 case div::srem:
return make_res(world.lit(type, 0));
866 case div::urem:
return make_res(world.lit(type, 0));
875 auto& world = dst_t->
world();
876 auto s_t = x->
type()->as<
App>();
877 auto d_t = dst_t->as<
App>();
883 if (s_t == d_t)
return x;
884 if (x->isa<
Bot>())
return world.bot(d_t);
888 if (ld && *ld == 1)
return world.lit(d_t, 0);
889 if (ls && *ls == 1)
return world.lit(d_t, 0);
893 if (*ld == 0)
return world.lit(d_t, *
l);
894 return world.lit(d_t, *
l % *ld);
897 return world.lit(d_t, idx_from_signed_mod(*ld, idx_sext(*ls, *
l)));
904 if (*ls == 0 || (*ls1 != 0 && *ls > *ls1))
905 if (*ld == *ls1)
return x1;
912 auto& world = type->world();
914 if (x->isa<
Bot>())
return world.bot(type);
915 if (
auto l =
Lit::isa(x))
return world.lit_nat(*
l);
922 auto& world = dst_t->
world();
923 auto src_t = src->
type();
925 if (src->isa<
Bot>())
return world.bot(dst_t);
926 if (src_t == dst_t)
return src;
929 return other->arg()->
type() == dst_t ? other->arg() : world.call<
bitcast>(dst_t, other->arg());
932 return i2n->arg()->type() == dst_t ? i2n->arg() : world.call<
bitcast>(dst_t, i2n->arg());
935 if (dst_t->isa<
Nat>())
return world.lit(dst_t, *
l);
937 if (
auto s =
Lit::isa(size))
return world.lit_idx_mod(*
s, *
l);
938 return world.lit(dst_t, *
l);
951 auto& world = type->world();
953 return world.lit_nat(8);
954 }
else if (type->isa<
Pi>()) {
955 return world.lit_nat(8);
956 }
else if (
auto size =
Idx::isa(type)) {
957 if (
auto w =
Idx::size2bitwidth(size))
return world.lit_nat(std::max(1_n, std::bit_ceil(*w) / 8_n));
960 case 16:
return world.lit_nat(2);
961 case 32:
return world.lit_nat(4);
962 case 64:
return world.lit_nat(8);
963 default: std::unreachable();
965 }
else if (type->isa<
Sigma>()) {
968 for (
auto t : type->ops()) {
971 if (!
a || !
s)
return {};
974 offset = fe::pad(offset, *
a) + *
s;
977 offset = fe::pad(offset,
align);
978 u64 size = std::max(1_u64, offset);
984 }
else if (
auto arr = type->isa_imm<
Arr>()) {
986 auto elem = arr->elem();
987 if (!elem)
return {};
999 auto& world = type->world();
1003 if (arg->
is_closed())
return world.lit_tt();
A (possibly paramterized) Array.
static auto isa(const Def *def)
World & world() const noexcept
auto projs(Projector auto f) const
Splits this Def via Def::projections into an Array (if A == std::dynamic_extent) or std::array (other...
const Def * type() const noexcept
Yields the "raw" type of this Def (maybe nullptr).
static bool greater(const Def *a, const Def *b)
bool is_closed() const
Same as !has_free_vars().
static constexpr nat_t size2bitwidth(nat_t n)
static const Def * isa(const Def *def)
Checks if def is a Idx s and returns s or nullptr otherwise.
static std::optional< nat_t > isa_lit(const Def *def)
static std::optional< T > isa(const Def *def)
static T as(const Def *def)
A dependent function type.
#define MIM_core_NORMALIZER_IMPL
const Def * normalize_nat(const Def *type, const Def *callee, const Def *arg)
const Def * normalize_div(const Def *full_type, const Def *, const Def *arg)
const Def * normalize_pe(const Def *type, const Def *, const Def *arg)
const Def * normalize_idx2nat(const Def *type, const Def *, const Def *x)
const Def * normalize_extrema(const Def *type, const Def *c, const Def *arg)
const Def * normalize_icmp(const Def *type, const Def *c, const Def *arg)
const Def * normalize_bit1(const Def *type, const Def *c, const Def *a)
const Def * normalize_conv(const Def *dst_t, const Def *, const Def *x)
const Def * normalize_bit2(const Def *type, const Def *c, const Def *arg)
const Def * normalize_wrap(const Def *type, const Def *c, const Def *arg)
const Def * normalize_trait(const Def *, const Def *, const Def *type)
const Def * op(trait o, const Def *type)
const Def * normalize_abs(const Def *type, const Def *, const Def *arg)
const Def * normalize_idx(const Def *type, const Def *c, const Def *arg)
constexpr std::array< std::array< u64, 2 >, 2 > make_truth_table(bit2 id)
const Def * normalize_bitcast(const Def *dst_t, const Def *, const Def *src)
const Def * normalize_ncmp(const Def *type, const Def *callee, const Def *arg)
constexpr flags_t icmp_mask
@ nuw
No Unsigned Wrap around.
@ nsw
No Signed Wrap around.
const Def * normalize_shr(const Def *type, const Def *c, const Def *arg)
std::optional< nat_t > isa_f(const Def *def)
fe::View< const Def * > Defs
constexpr bool is_commutative(Id)
constexpr bool is_associative(Id id)
static consteval size_t num()
static consteval flags_t base()