rename intrinsics

This commit is contained in:
2026-07-14 19:22:20 +02:00
parent 471896b48a
commit 5157cf3bcc
26 changed files with 107640 additions and 106418 deletions
+264 -106
View File
@@ -86,6 +86,32 @@ main func() void { _ = value }
testing.expect_value(t, module.statements[module.functions[0].body[0]].name, sink_symbol)
}
@(test)
parser_marks_only_bang_calls_as_intrinsic :: proc(t: ^testing.T) {
text := `main func() void {
_ = sizeof ! (i32)
_ = sizeof(i32)
}
`
source_file := source.Source{path="test.bro", text=text}
diagnostics := source.init_diagnostics(&source_file)
defer source.destroy_diagnostics(&diagnostics)
symbols := symbol.init_table()
defer symbol.destroy_table(&symbols)
stream := lexer.lex(&source_file, &diagnostics, &symbols)
defer delete(stream.items)
module := parser.parse(&stream, &source_file, &diagnostics)
defer ast.destroy_module(&module)
marked := module.exprs[module.statements[module.functions[0].body[0]].expr]
ordinary := module.exprs[module.statements[module.functions[0].body[1]].expr]
testing.expect_value(t, len(diagnostics.items), 0)
testing.expect_value(t, marked.kind, ast.Expr_Kind.Call)
testing.expect_value(t, ordinary.kind, ast.Expr_Kind.Call)
testing.expect(t, marked.intrinsic)
testing.expect(t, !ordinary.intrinsic)
}
@(test)
compact_ids_reserve_invalid_values_and_preserve_layout :: proc(t: ^testing.T) {
testing.expect_value(t, size_of(source.Span), 12)
@@ -1372,14 +1398,14 @@ Color :: enum {
blue
}
UserID :: distinct u32
SIZE_GLOBAL :: size_of(i32)
SIZE_GLOBAL :: sizeof!(i32)
needs_usize func(value usize) usize {
return value
}
buffer func($T type) [size_of(T)]u8 {
data [size_of(T)]u8 = undefined
buffer func($T type) [sizeof!(T)]u8 {
data [sizeof!(T)]u8 = undefined
return data
}
@@ -1387,18 +1413,18 @@ main func() i32 {
bytes [_]u8 :: buffer(i32)
if (needs_usize(SIZE_GLOBAL) != 4) return 1
if (bytes.len != 4) return 2
if (size_of([3]u8) != 3) return 3
if (size_of([]u8) != 16) return 4
if (align_of([]u8) != 8) return 5
if (size_of(*anyopaque) != 8) return 6
if (size_of(?*i32) != 8) return 7
if (size_of(*Opaque) != 8) return 8
if (size_of(Color) != 2) return 9
if (align_of(Color) != 2) return 10
if (size_of(Point) != 8) return 11
if (align_of(Point) != 4) return 12
if (size_of(UserID) != 4) return 13
if (align_of(UserID) != 4) return 14
if (sizeof!([3]u8) != 3) return 3
if (sizeof!([]u8) != 16) return 4
if (alignof!([]u8) != 8) return 5
if (sizeof!(*anyopaque) != 8) return 6
if (sizeof!(?*i32) != 8) return 7
if (sizeof!(*Opaque) != 8) return 8
if (sizeof!(Color) != 2) return 9
if (alignof!(Color) != 2) return 10
if (sizeof!(Point) != 8) return 11
if (alignof!(Point) != 4) return 12
if (sizeof!(UserID) != 4) return 13
if (alignof!(UserID) != 4) return 14
return 0
}
`
@@ -1418,23 +1444,23 @@ integer_bound_builtins_compile_and_run :: proc(t: ^testing.T) {
directory := "/tmp/brolang-test-integer-bounds"
main_path := "/tmp/brolang-test-integer-bounds/main.bro"
output := "/tmp/brolang-test-integer-bounds-output"
text := `MAX_U64 u64 :: max_value(u64)
text := `MAX_U64 u64 :: maxval!(u64)
maximum func($T type) T {
return max_value(T)
return maxval!(T)
}
main func() i32 {
if (min_value(i8) != -128) return 1
if (max_value(i8) != 127) return 2
if (min_value(u8) != 0) return 3
if (max_value(u8) != 255) return 4
if (min_value(isize) != -9223372036854775808) return 5
if (max_value(usize) != 18446744073709551615) return 6
if (minval!(i8) != -128) return 1
if (maxval!(i8) != 127) return 2
if (minval!(u8) != 0) return 3
if (maxval!(u8) != 255) return 4
if (minval!(isize) != -9223372036854775808) return 5
if (maxval!(usize) != 18446744073709551615) return 6
if (MAX_U64 != 18446744073709551615) return 7
if (maximum(u16) != 65535) return 8
if (min_value(c_int) != -2147483648) return 9
if (max_value(c_ulong) != 18446744073709551615) return 10
if (minval!(c_int) != -2147483648) return 9
if (maxval!(c_ulong) != 18446744073709551615) return 10
return 0
}
`
@@ -1455,14 +1481,14 @@ integer_bound_builtins_reject_invalid_targets :: proc(t: ^testing.T) {
Choice :: enum { one }
main func() void {
_ = min_value()
_ = max_value(u8, u16)
_ = min_value(1)
_ = max_value(int)
_ = max_value(f32)
_ = max_value(bool)
_ = max_value(Named)
_ = max_value(Choice)
_ = minval!()
_ = maxval!(u8, u16)
_ = minval!(1)
_ = maxval!(int)
_ = maxval!(f32)
_ = maxval!(bool)
_ = maxval!(Named)
_ = maxval!(Choice)
}
`
source_file := source.Source{path="test.bro", text=text}
@@ -1496,11 +1522,11 @@ layout_builtins_reject_unsized_targets :: proc(t: ^testing.T) {
Fn :: alias func() void
main func() void {
_ = size_of(void)
_ = align_of(anyopaque)
_ = size_of(Fn)
_ = size_of(Opaque)
_ = align_of(1)
_ = sizeof!(void)
_ = alignof!(anyopaque)
_ = sizeof!(Fn)
_ = sizeof!(Opaque)
_ = alignof!(1)
}
`
source_file := source.Source{path="test.bro", text=text}
@@ -1803,14 +1829,14 @@ main func() void {
}
@(test)
opaque_anyopaque_and_ptr_cast_compile_and_lower :: proc(t: ^testing.T) {
opaque_anyopaque_and_ptrcast_compile_and_lower :: proc(t: ^testing.T) {
text := `Handle :: opaque
take func(_ ?*mut anyopaque) void {}
use_handle func(_ ?@mut Handle) void {}
main func() void {
values [2]mut u8 = [1, 2]
raw ?*mut anyopaque = (&values).ptr
bytes ?*mut u8 = ptr_cast(u8, raw)
bytes ?*mut u8 = ptrcast!(u8, raw)
take(bytes)
if bytes |p| {
p[1] = 5
@@ -1818,7 +1844,7 @@ main func() void {
one u8 = 1
single ?@mut anyopaque = &one
typed ?@mut u8 = ptr_cast(u8, single)
typed ?@mut u8 = ptrcast!(u8, single)
if typed |p| {
p^ = 2
}
@@ -1858,16 +1884,16 @@ main func() void {
}
@(test)
anyopaque_by_value_and_invalid_ptr_casts_are_rejected :: proc(t: ^testing.T) {
anyopaque_by_value_and_invalid_ptrcasts_are_rejected :: proc(t: ^testing.T) {
text := `Callback :: alias c_func() void
main func() void {
raw ?*mut anyopaque = none
value anyopaque = undefined
_ = ptr_cast(void, raw)
_ = ptr_cast(anyopaque, raw)
_ = ptr_cast(Callback, raw)
_ = ptr_cast(u8, 1)
_ = ptr_cast(1, raw)
_ = ptrcast!(void, raw)
_ = ptrcast!(anyopaque, raw)
_ = ptrcast!(Callback, raw)
_ = ptrcast!(u8, 1)
_ = ptrcast!(1, raw)
}
`
source_file := source.Source{path="test.bro", text=text}
@@ -1888,9 +1914,9 @@ main func() void {
found_target_type := false
for diagnostic in diagnostics.items {
found_by_value = found_by_value || strings.contains(diagnostic.message, "could not infer a concrete type")
found_bad_target = found_bad_target || strings.contains(diagnostic.message, "ptr_cast target must be a sized runtime object type")
found_bad_operand = found_bad_operand || strings.contains(diagnostic.message, "ptr_cast operand must be a pointer")
found_target_type = found_target_type || strings.contains(diagnostic.message, "ptr_cast target must be a type")
found_bad_target = found_bad_target || strings.contains(diagnostic.message, "ptrcast! target must be a sized runtime object type")
found_bad_operand = found_bad_operand || strings.contains(diagnostic.message, "ptrcast! operand must be a pointer")
found_target_type = found_target_type || strings.contains(diagnostic.message, "ptrcast! target must be a type")
}
testing.expect(t, found_by_value)
testing.expect(t, found_bad_target)
@@ -1898,6 +1924,138 @@ main func() void {
testing.expect(t, found_target_type)
}
@(test)
old_intrinsic_spellings_are_not_recognized :: proc(t: ^testing.T) {
text := `main func() void {
_ = ptr_cast(1, 1)
_ = size_of(1)
_ = align_of(1)
_ = min_value(1)
_ = max_value(1)
_ = div_trunc(1, 1)
_ = div_floor(1, 1)
_ = div_exact(1, 1)
_ = div_ceil(1, 1)
}
`
source_file := source.Source{path="test.bro", text=text}
diagnostics := source.init_diagnostics(&source_file)
defer source.destroy_diagnostics(&diagnostics)
symbols := symbol.init_table()
defer symbol.destroy_table(&symbols)
stream := lexer.lex(&source_file, &diagnostics, &symbols)
defer delete(stream.items)
ast_module := parser.parse(&stream, &source_file, &diagnostics)
defer ast.destroy_module(&ast_module)
hir_module := checker.check(&ast_module, &diagnostics, &symbols)
defer hir.destroy_module(&hir_module)
names := [?]string{
"ptr_cast", "size_of", "align_of", "min_value", "max_value",
"div_trunc", "div_floor", "div_exact", "div_ceil",
}
found := [len(names)]bool{}
for diagnostic in diagnostics.items {
if !strings.contains(diagnostic.message, "unresolved function") {
continue
}
for name, index in names {
found[index] = found[index] || strings.contains(diagnostic.message, name)
}
}
for value in found {
testing.expect(t, value)
}
}
@(test)
bare_intrinsic_names_are_available_to_user_functions :: proc(t: ^testing.T) {
text := `ptrcast func() i32 { return 1 }
sizeof func() i32 { return 2 }
alignof func() i32 { return 3 }
minval func() i32 { return 4 }
maxval func() i32 { return 5 }
divtrunc func() i32 { return 6 }
divfloor func() i32 { return 7 }
divexact func() i32 { return 8 }
divceil func() i32 { return 9 }
rem func() i32 { return 10 }
mod func() i32 { return 11 }
main func() i32 {
return ptrcast() + sizeof() + alignof() + minval() + maxval() +
divtrunc() + divfloor() + divexact() + divceil() + rem() + mod() - 66
}
`
directory := "/tmp/brolang-test-user-intrinsic-names"
main_path := "/tmp/brolang-test-user-intrinsic-names/main.bro"
output := "/tmp/brolang-test-user-intrinsic-names-output"
_ = os2.remove_all(directory)
defer _ = os2.remove_all(directory)
defer _ = os.remove(output)
testing.expect(t, os.make_directory(directory) == nil)
testing.expect(t, os.write_entire_file(main_path, transmute([]byte)text))
testing.expect_value(t, compiler_core.compile_package(directory, output), 0)
}
@(test)
intrinsic_call_diagnostics_are_precise :: proc(t: ^testing.T) {
text := `main func() void {
_ = mystery!()
_ = math.ptrcast!()
}
`
source_file := source.Source{path="test.bro", text=text}
diagnostics := source.init_diagnostics(&source_file)
defer source.destroy_diagnostics(&diagnostics)
symbols := symbol.init_table()
defer symbol.destroy_table(&symbols)
stream := lexer.lex(&source_file, &diagnostics, &symbols)
defer delete(stream.items)
ast_module := parser.parse(&stream, &source_file, &diagnostics)
defer ast.destroy_module(&ast_module)
hir_module := checker.check(&ast_module, &diagnostics, &symbols)
defer hir.destroy_module(&hir_module)
found_unknown := false
found_qualified := false
for diagnostic in diagnostics.items {
found_unknown = found_unknown || diagnostic.message == "unknown intrinsic 'mystery!'"
found_qualified = found_qualified || diagnostic.message == "intrinsic calls must be unqualified"
}
testing.expect(t, found_unknown)
testing.expect(t, found_qualified)
}
@(test)
malformed_intrinsic_calls_have_targeted_parse_diagnostics :: proc(t: ^testing.T) {
cases := [?]struct {
text, message: string,
}{
{`main func() void { _ = sizeof! }`, "expected '(' after intrinsic name"},
{`callback func() void {}
main func() void { (callback)!() }
`, "intrinsic calls require a direct name"},
}
for test_case in cases {
source_file := source.Source{path="test.bro", text=test_case.text}
diagnostics := source.init_diagnostics(&source_file)
symbols := symbol.init_table()
stream := lexer.lex(&source_file, &diagnostics, &symbols)
module := parser.parse(&stream, &source_file, &diagnostics)
found := false
for diagnostic in diagnostics.items {
found = found || diagnostic.message == test_case.message
}
testing.expect(t, found)
ast.destroy_module(&module)
delete(stream.items)
symbol.destroy_table(&symbols)
source.destroy_diagnostics(&diagnostics)
}
}
@(test)
aarch64_c_record_abi_classifies_fixed_parameters_and_results :: proc(t: ^testing.T) {
text := `Small :: c_struct {
@@ -6541,7 +6699,7 @@ main func() void {}
@(test)
constant_division_by_zero_has_a_precise_diagnostic :: proc(t: ^testing.T) {
text := `value :: div_trunc(5, 0)
text := `value :: divtrunc!(5, 0)
main func() void {}
`
source_file := source.Source{path="test.bro", text=text}
@@ -9669,12 +9827,12 @@ compound_assignment_preserves_checked_numeric_operations :: proc(t: ^testing.T)
signed += 6
signed -= 2
signed *= 3
signed = div_trunc(signed, 4)
signed = divtrunc!(signed, 4)
unsigned u32 = 24
unsigned += 6
unsigned -= 2
unsigned *= 3
unsigned = div_trunc(unsigned, 4)
unsigned = divtrunc!(unsigned, 4)
real f64 = 24.0
real += 6.0
real -= 2.0
@@ -9838,7 +9996,7 @@ checked_division_and_subtraction_emit_guarded_llvm :: proc(t: ^testing.T) {
a i32 = 10
b i32 = 3
c i32 = a - b
return div_trunc(c, b)
return divtrunc!(c, b)
}
`
source_file := source.Source{path="test.bro", text=text}
@@ -9933,20 +10091,20 @@ main func() void { _ = $half(4) }`, want="integer '/' is not allowed"},
@(test)
division_builtins_diagnose_arity_operands_and_comptime_failures :: proc(t: ^testing.T) {
text := `bad_arity :: div_floor(1)
bad_bool :: rem(true, false)
bad_family :: mod(i32(5), f32(3))
zero_trunc :: div_trunc(1, 0)
zero_floor :: div_floor(1.0, 0.0)
zero_exact :: div_exact(1, 0)
zero_ceil :: div_ceil(1.0, 0.0)
zero_rem :: rem(1, 0)
zero_mod :: mod(1.0, 0.0)
inexact :: div_exact(5, 3)
overflow_trunc :: div_trunc(min_value(i32), -1)
overflow_floor :: div_floor(min_value(i32), -1)
overflow_exact :: div_exact(min_value(i32), -1)
overflow_ceil :: div_ceil(min_value(i32), -1)
text := `bad_arity :: divfloor!(1)
bad_bool :: rem!(true, false)
bad_family :: mod!(i32(5), f32(3))
zero_trunc :: divtrunc!(1, 0)
zero_floor :: divfloor!(1.0, 0.0)
zero_exact :: divexact!(1, 0)
zero_ceil :: divceil!(1.0, 0.0)
zero_rem :: rem!(1, 0)
zero_mod :: mod!(1.0, 0.0)
inexact :: divexact!(5, 3)
overflow_trunc :: divtrunc!(minval!(i32), -1)
overflow_floor :: divfloor!(minval!(i32), -1)
overflow_exact :: divexact!(minval!(i32), -1)
overflow_ceil :: divceil!(minval!(i32), -1)
main func() void {}
`
source_file := source.Source{path="test.bro", text=text}
@@ -9986,28 +10144,28 @@ division_family_compiles_and_runs_for_integer_and_float_scalars :: proc(t: ^test
directory := "/tmp/brolang-test-division-family"
main_path := "/tmp/brolang-test-division-family/main.bro"
output := "/tmp/brolang-test-division-family-output"
text := `COUNT :: div_exact(8, 2)
items [div_ceil(10, 3)]u8 :: [0, 0, 0, 0]
text := `COUNT :: divexact!(8, 2)
items [divceil!(10, 3)]u8 :: [0, 0, 0, 0]
OPEN :: 5
open_ceil i32 :: div_ceil(OPEN, 3)
open_ceil i32 :: divceil!(OPEN, 3)
check_i32 func(a, b, qt, qf, qc, r, m i32) bool {
return div_trunc(a, b) == qt and div_floor(a, b) == qf and
div_ceil(a, b) == qc and rem(a, b) == r and mod(a, b) == m
return divtrunc!(a, b) == qt and divfloor!(a, b) == qf and
divceil!(a, b) == qc and rem!(a, b) == r and mod!(a, b) == m
}
check_f32 func(a, b, qt, qf, qc, r, m f32) bool {
return div_trunc(a, b) == qt and div_floor(a, b) == qf and
div_ceil(a, b) == qc and rem(a, b) == r and mod(a, b) == m
return divtrunc!(a, b) == qt and divfloor!(a, b) == qf and
divceil!(a, b) == qc and rem!(a, b) == r and mod!(a, b) == m
}
check_f64 func(a, b, qt, qf, qc, r, m f64) bool {
return div_trunc(a, b) == qt and div_floor(a, b) == qf and
div_ceil(a, b) == qc and rem(a, b) == r and mod(a, b) == m
return divtrunc!(a, b) == qt and divfloor!(a, b) == qf and
divceil!(a, b) == qc and rem!(a, b) == r and mod!(a, b) == m
}
edge_rem func(a, b i32) i32 { return rem(a, b) }
edge_mod func(a, b i32) i32 { return mod(a, b) }
edge_rem func(a, b i32) i32 { return rem!(a, b) }
edge_mod func(a, b i32) i32 { return mod!(a, b) }
main func() i32 {
if COUNT != 4 or items.len != 4 or open_ceil != 2 { return 1 }
@@ -10015,9 +10173,9 @@ main func() i32 {
if !check_i32(5, -3, -1, -2, -1, 2, -1) { return 3 }
if !check_i32(-5, 3, -1, -2, -1, -2, 1) { return 4 }
if !check_i32(-5, -3, 1, 1, 2, -2, -2) { return 5 }
if div_trunc(u32(5), u32(3)) != 1 or div_floor(u32(5), u32(3)) != 1 or
div_ceil(u32(5), u32(3)) != 2 or rem(u32(5), u32(3)) != 2 or mod(u32(5), u32(3)) != 2 { return 6 }
if div_exact(i32(6), i32(3)) != 2 or div_exact(u32(6), u32(3)) != 2 { return 7 }
if divtrunc!(u32(5), u32(3)) != 1 or divfloor!(u32(5), u32(3)) != 1 or
divceil!(u32(5), u32(3)) != 2 or rem!(u32(5), u32(3)) != 2 or mod!(u32(5), u32(3)) != 2 { return 6 }
if divexact!(i32(6), i32(3)) != 2 or divexact!(u32(6), u32(3)) != 2 { return 7 }
if !check_f32(f32(5.0), f32(3.0), f32(1.0), f32(1.0), f32(2.0), f32(2.0), f32(2.0)) or
!check_f32(f32(5.0), f32(-3.0), f32(-1.0), f32(-2.0), f32(-1.0), f32(2.0), f32(-1.0)) or
!check_f32(f32(-5.0), f32(3.0), f32(-1.0), f32(-2.0), f32(-1.0), f32(-2.0), f32(1.0)) or
@@ -10026,7 +10184,7 @@ main func() i32 {
!check_f64(5.0, -3.0, -1.0, -2.0, -1.0, 2.0, -1.0) or
!check_f64(-5.0, 3.0, -1.0, -2.0, -1.0, -2.0, 1.0) or
!check_f64(-5.0, -3.0, 1.0, 1.0, 2.0, -2.0, -2.0) { return 9 }
if div_exact(f32(6.0), f32(3.0)) != 2.0 or div_exact(f64(6.0), f64(3.0)) != 2.0 { return 10 }
if divexact!(f32(6.0), f32(3.0)) != 2.0 or divexact!(f64(6.0), f64(3.0)) != 2.0 { return 10 }
if edge_rem(-2147483648, -1) != 0 or edge_mod(-2147483648, -1) != 0 { return 11 }
return 0
}
@@ -10051,24 +10209,24 @@ division_builtins_trap_for_runtime_zero_overflow_and_inexact_results :: proc(t:
right: string,
}
cases := [?]Case{
{name="div_trunc", type_name="i32", left="1", right="0"},
{name="div_floor", type_name="f32", left="f32(1.0)", right="f32(0.0)"},
{name="div_exact", type_name="f64", left="1.0", right="0.0"},
{name="div_ceil", type_name="i32", left="1", right="0"},
{name="divtrunc", type_name="i32", left="1", right="0"},
{name="divfloor", type_name="f32", left="f32(1.0)", right="f32(0.0)"},
{name="divexact", type_name="f64", left="1.0", right="0.0"},
{name="divceil", type_name="i32", left="1", right="0"},
{name="rem", type_name="f32", left="f32(1.0)", right="f32(0.0)"},
{name="mod", type_name="f64", left="1.0", right="0.0"},
{name="div_exact", type_name="i32", left="5", right="3"},
{name="div_trunc", type_name="i32", left="-2147483648", right="-1"},
{name="div_floor", type_name="i32", left="-2147483648", right="-1"},
{name="div_exact", type_name="i32", left="-2147483648", right="-1"},
{name="div_ceil", type_name="i32", left="-2147483648", right="-1"},
{name="divexact", type_name="i32", left="5", right="3"},
{name="divtrunc", type_name="i32", left="-2147483648", right="-1"},
{name="divfloor", type_name="i32", left="-2147483648", right="-1"},
{name="divexact", type_name="i32", left="-2147483648", right="-1"},
{name="divceil", type_name="i32", left="-2147483648", right="-1"},
}
for test_case, index in cases {
directory := fmt.aprintf("/tmp/brolang-test-division-trap-%d", index)
main_path := fmt.aprintf("%s/main.bro", directory)
output := fmt.aprintf("/tmp/brolang-test-division-trap-output-%d", index)
text := fmt.aprintf(
"invoke func(a, b %s) %s {{ return %s(a, b) }}\nmain func() void {{ _ = invoke(%s, %s) }}\n",
"invoke func(a, b %s) %s {{ return %s!(a, b) }}\nmain func() void {{ _ = invoke(%s, %s) }}\n",
test_case.type_name, test_case.type_name, test_case.name, test_case.left, test_case.right,
)
_ = os2.remove_all(directory)
@@ -10096,10 +10254,10 @@ qualified_division_builtin_names_resolve_as_package_functions :: proc(t: ^testin
math_path := "/tmp/brolang-test-qualified-division/math/math.bro"
main_path := "/tmp/brolang-test-qualified-division/app/main.bro"
output := "/tmp/brolang-test-qualified-division-output"
math_text := `div_floor func(a, b i32) i32 { return a + b }
math_text := `divfloor func(a, b i32) i32 { return a + b }
`
main_text := `math :: import "../math"
main func() i32 { return math.div_floor(20, 22) }
main func() i32 { return math.divfloor(20, 22) }
`
_ = os2.remove_all(directory)
defer _ = os2.remove_all(directory)
@@ -10117,17 +10275,17 @@ main func() i32 { return math.div_floor(20, 22) }
@(test)
division_builtins_emit_guards_rounding_and_single_integer_divisions :: proc(t: ^testing.T) {
text := `floor_i32 func(a, b i32) i32 { return div_floor(a, b) }
ceil_i32 func(a, b i32) i32 { return div_ceil(a, b) }
exact_i32 func(a, b i32) i32 { return div_exact(a, b) }
floor_u32 func(a, b u32) u32 { return div_floor(a, b) }
rem_i16 func(a, b i16) i16 { return rem(a, b) }
mod_i16 func(a, b i16) i16 { return mod(a, b) }
floor_f32 func(a, b f32) f32 { return div_floor(a, b) }
ceil_f64 func(a, b f64) f64 { return div_ceil(a, b) }
exact_f32 func(a, b f32) f32 { return div_exact(a, b) }
rem_f64 func(a, b f64) f64 { return rem(a, b) }
mod_f32 func(a, b f32) f32 { return mod(a, b) }
text := `floor_i32 func(a, b i32) i32 { return divfloor!(a, b) }
ceil_i32 func(a, b i32) i32 { return divceil!(a, b) }
exact_i32 func(a, b i32) i32 { return divexact!(a, b) }
floor_u32 func(a, b u32) u32 { return divfloor!(a, b) }
rem_i16 func(a, b i16) i16 { return rem!(a, b) }
mod_i16 func(a, b i16) i16 { return mod!(a, b) }
floor_f32 func(a, b f32) f32 { return divfloor!(a, b) }
ceil_f64 func(a, b f64) f64 { return divceil!(a, b) }
exact_f32 func(a, b f32) f32 { return divexact!(a, b) }
rem_f64 func(a, b f64) f64 { return rem!(a, b) }
mod_f32 func(a, b f32) f32 { return mod!(a, b) }
main func() void {
_ = floor_i32(5, 3)
_ = ceil_i32(5, 3)