enforce integer division via explicit builtins
This commit is contained in:
+331
-10
@@ -6246,7 +6246,7 @@ main func() void {}
|
||||
|
||||
@(test)
|
||||
constant_division_by_zero_has_a_precise_diagnostic :: proc(t: ^testing.T) {
|
||||
text := `value :: 5 / 0
|
||||
text := `value :: div_trunc(5, 0)
|
||||
main func() void {}
|
||||
`
|
||||
source_file := source.Source{path="test.bro", text=text}
|
||||
@@ -6265,7 +6265,7 @@ main func() void {}
|
||||
found_overflow := false
|
||||
for diagnostic in diagnostics.items {
|
||||
found_division_by_zero = found_division_by_zero ||
|
||||
strings.contains(diagnostic.message, "division by zero in constant expression")
|
||||
strings.contains(diagnostic.message, "division builtin denominator is zero")
|
||||
found_overflow = found_overflow ||
|
||||
strings.contains(diagnostic.message, "integer constant expression exceeds signed i64 range")
|
||||
}
|
||||
@@ -6404,11 +6404,19 @@ malformed_hir_references_lower_to_valid_trapped_llvm :: proc(t: ^testing.T) {
|
||||
malformed_ir_emits_traps_and_typed_sentinels :: proc(t: ^testing.T) {
|
||||
module := ir.init_module()
|
||||
defer ir.destroy_module(&module)
|
||||
instructions := make([]ir.Instruction, 4)
|
||||
instructions := make([]ir.Instruction, 11)
|
||||
instructions[0] = ir.Instruction{op=.Store, type=types.I8, a=ir.INVALID_INSTRUCTION, b=ir.INVALID_INSTRUCTION, diagnostic=source.INVALID_DIAGNOSTIC}
|
||||
instructions[1] = ir.Instruction{op=.Add_Checked, type=types.I32, a=ir.INVALID_INSTRUCTION, b=ir.INVALID_INSTRUCTION, diagnostic=source.INVALID_DIAGNOSTIC}
|
||||
instructions[2] = ir.Instruction{op=.Neg_Checked, type=types.I16, a=ir.INVALID_INSTRUCTION, b=ir.INVALID_INSTRUCTION, diagnostic=source.INVALID_DIAGNOSTIC}
|
||||
instructions[3] = ir.Instruction{op=.Return, type=types.I32, a=ir.Instruction_Id(1), b=ir.INVALID_INSTRUCTION, diagnostic=source.INVALID_DIAGNOSTIC}
|
||||
division_ops := [?]ir.Opcode{
|
||||
.Div_Checked,
|
||||
.Div_Trunc_Checked, .Div_Floor_Checked, .Div_Exact_Checked, .Div_Ceil_Checked,
|
||||
.Rem_Checked, .Mod_Checked,
|
||||
}
|
||||
for op, index in division_ops {
|
||||
instructions[3+index] = ir.Instruction{op=op, type=types.I32, a=ir.INVALID_INSTRUCTION, b=ir.INVALID_INSTRUCTION, diagnostic=source.INVALID_DIAGNOSTIC}
|
||||
}
|
||||
instructions[10] = ir.Instruction{op=.Return, type=types.I32, a=ir.Instruction_Id(1), b=ir.INVALID_INSTRUCTION, diagnostic=source.INVALID_DIAGNOSTIC}
|
||||
append(&module.functions, ir.Function{
|
||||
link_name=strings.clone("main"),
|
||||
calling_convention=.C,
|
||||
@@ -6430,6 +6438,9 @@ malformed_ir_emits_traps_and_typed_sentinels :: proc(t: ^testing.T) {
|
||||
testing.expect(t, strings.contains(text, "%v0 = add i8 0, -86"))
|
||||
testing.expect(t, strings.contains(text, "%v1 = add i32 0, -1431655766"))
|
||||
testing.expect(t, strings.contains(text, "%v2 = add i16 0, -21846"))
|
||||
for index in 3..=9 {
|
||||
testing.expect(t, strings.contains(text, fmt.tprintf("%%v%d = add i32 0, -1431655766", index)))
|
||||
}
|
||||
testing.expect(t, strings.contains(text, "@bro.trap(ptr %message, i64 %length) noreturn"))
|
||||
testing.expect(t, !strings.contains(text, "%v-1"))
|
||||
llvm_path := "/tmp/brolang-test-malformed-recovery.ll"
|
||||
@@ -9200,12 +9211,12 @@ compound_assignment_preserves_checked_numeric_operations :: proc(t: ^testing.T)
|
||||
signed += 6
|
||||
signed -= 2
|
||||
signed *= 3
|
||||
signed /= 4
|
||||
signed = div_trunc(signed, 4)
|
||||
unsigned u32 = 24
|
||||
unsigned += 6
|
||||
unsigned -= 2
|
||||
unsigned *= 3
|
||||
unsigned /= 4
|
||||
unsigned = div_trunc(unsigned, 4)
|
||||
real f64 = 24.0
|
||||
real += 6.0
|
||||
real -= 2.0
|
||||
@@ -9239,25 +9250,28 @@ compound_assignment_preserves_checked_numeric_operations :: proc(t: ^testing.T)
|
||||
testing.expect_value(t, operation_counts[.Add], 3)
|
||||
testing.expect_value(t, operation_counts[.Sub], 3)
|
||||
testing.expect_value(t, operation_counts[.Mul], 3)
|
||||
testing.expect_value(t, operation_counts[.Div], 3)
|
||||
testing.expect_value(t, operation_counts[.Div], 1)
|
||||
|
||||
add_count := 0
|
||||
sub_count := 0
|
||||
mul_count := 0
|
||||
div_count := 0
|
||||
div_trunc_count := 0
|
||||
for instruction in ir_module.functions[0].instructions {
|
||||
#partial switch instruction.op {
|
||||
case .Add_Checked: add_count += 1
|
||||
case .Sub_Checked: sub_count += 1
|
||||
case .Mul_Checked: mul_count += 1
|
||||
case .Div_Checked: div_count += 1
|
||||
case .Div_Trunc_Checked: div_trunc_count += 1
|
||||
case:
|
||||
}
|
||||
}
|
||||
testing.expect_value(t, add_count, 3)
|
||||
testing.expect_value(t, sub_count, 3)
|
||||
testing.expect_value(t, mul_count, 3)
|
||||
testing.expect_value(t, div_count, 3)
|
||||
testing.expect_value(t, div_count, 1)
|
||||
testing.expect_value(t, div_trunc_count, 2)
|
||||
}
|
||||
|
||||
@(test)
|
||||
@@ -9310,7 +9324,7 @@ binary_arithmetic_rejects_non_numeric_operands :: proc(t: ^testing.T) {
|
||||
text := `main func() i32 {
|
||||
a i32 = 1
|
||||
b u32 = 2
|
||||
_ = a / b
|
||||
_ = a + b
|
||||
return 0
|
||||
}
|
||||
`
|
||||
@@ -9366,7 +9380,7 @@ checked_division_and_subtraction_emit_guarded_llvm :: proc(t: ^testing.T) {
|
||||
a i32 = 10
|
||||
b i32 = 3
|
||||
c i32 = a - b
|
||||
return c / b
|
||||
return div_trunc(c, b)
|
||||
}
|
||||
`
|
||||
source_file := source.Source{path="test.bro", text=text}
|
||||
@@ -9392,6 +9406,313 @@ checked_division_and_subtraction_emit_guarded_llvm :: proc(t: ^testing.T) {
|
||||
testing.expect(t, strings.contains(llvm_text, "divovf_trap"))
|
||||
}
|
||||
|
||||
@(test)
|
||||
integer_slash_is_rejected_and_float_slash_remains_available :: proc(t: ^testing.T) {
|
||||
Case :: struct {text, want: string}
|
||||
invalid := []Case{
|
||||
{text=`main func() void {
|
||||
a i32 = 4
|
||||
b i32 = 2
|
||||
_ = a / b
|
||||
}`, want="integer '/' is not allowed"},
|
||||
{text=`main func() void {
|
||||
a u32 = 4
|
||||
b u32 = 2
|
||||
_ = a / b
|
||||
}`, want="integer '/' is not allowed"},
|
||||
{text=`main func() void {
|
||||
_ = 4 / 2
|
||||
}`, want="integer '/' is not allowed"},
|
||||
{text=`main func() void {
|
||||
values [4 / 2]u8 = undefined
|
||||
_ = &values
|
||||
}`, want="integer '/' is not allowed"},
|
||||
{text=`half func($value i32) i32 { return value / 2 }
|
||||
main func() void { _ = $half(4) }`, want="integer '/' is not allowed"},
|
||||
{text=`main func() void {
|
||||
value i32 = 8
|
||||
value /= 2
|
||||
}`, want="assign through an explicit division builtin"},
|
||||
}
|
||||
for test_case in invalid {
|
||||
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)
|
||||
ast_module := parser.parse(&stream, &source_file, &diagnostics)
|
||||
hir_module := checker.check(&ast_module, &diagnostics, &symbols)
|
||||
found := false
|
||||
for diagnostic in diagnostics.items {
|
||||
found = found || strings.contains(diagnostic.message, test_case.want)
|
||||
}
|
||||
testing.expect(t, found)
|
||||
hir.destroy_module(&hir_module)
|
||||
ast.destroy_module(&ast_module)
|
||||
delete(stream.items)
|
||||
symbol.destroy_table(&symbols)
|
||||
source.destroy_diagnostics(&diagnostics)
|
||||
}
|
||||
|
||||
text := `main func() void {
|
||||
value f32 = 5.0 / 2.0
|
||||
value /= 2.0
|
||||
_ = value
|
||||
}
|
||||
`
|
||||
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)
|
||||
testing.expect_value(t, len(diagnostics.items), 0)
|
||||
}
|
||||
|
||||
@(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)
|
||||
main func() void {}
|
||||
`
|
||||
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)
|
||||
zero_count := 0
|
||||
found_arity, found_operands, found_exact := false, false, false
|
||||
overflow_count := 0
|
||||
for diagnostic in diagnostics.items {
|
||||
found_arity = found_arity || strings.contains(diagnostic.message, "expects 2 arguments")
|
||||
found_operands = found_operands || strings.contains(diagnostic.message, "compatible numeric operands")
|
||||
found_exact = found_exact || strings.contains(diagnostic.message, "exact division has a remainder")
|
||||
if strings.contains(diagnostic.message, "signed integer division overflow") {
|
||||
overflow_count += 1
|
||||
}
|
||||
if strings.contains(diagnostic.message, "division builtin denominator is zero") {
|
||||
zero_count += 1
|
||||
}
|
||||
}
|
||||
testing.expect(t, found_arity)
|
||||
testing.expect(t, found_operands)
|
||||
testing.expect(t, found_exact)
|
||||
testing.expect_value(t, overflow_count, 4)
|
||||
testing.expect_value(t, zero_count, 6)
|
||||
}
|
||||
|
||||
@(test)
|
||||
division_family_compiles_and_runs_for_integer_and_float_scalars :: proc(t: ^testing.T) {
|
||||
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]
|
||||
OPEN :: 5
|
||||
open_ceil i32 :: div_ceil(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
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
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 }
|
||||
if !check_i32(5, 3, 1, 1, 2, 2, 2) { return 2 }
|
||||
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 !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
|
||||
!check_f32(f32(-5.0), f32(-3.0), f32(1.0), f32(1.0), f32(2.0), f32(-2.0), f32(-2.0)) { return 8 }
|
||||
if !check_f64(5.0, 3.0, 1.0, 1.0, 2.0, 2.0, 2.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, -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 edge_rem(-2147483648, -1) != 0 or edge_mod(-2147483648, -1) != 0 { return 11 }
|
||||
return 0
|
||||
}
|
||||
`
|
||||
_ = 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))
|
||||
status := compiler_core.compile_package(directory, output)
|
||||
testing.expect_value(t, status, 0)
|
||||
state := run_executable(output)
|
||||
testing.expect_value(t, state.exit_code, 0)
|
||||
}
|
||||
|
||||
@(test)
|
||||
division_builtins_trap_for_runtime_zero_overflow_and_inexact_results :: proc(t: ^testing.T) {
|
||||
Case :: struct {
|
||||
name: string,
|
||||
type_name: string,
|
||||
left: string,
|
||||
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="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"},
|
||||
}
|
||||
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",
|
||||
test_case.type_name, test_case.type_name, test_case.name, test_case.left, test_case.right,
|
||||
)
|
||||
_ = os2.remove_all(directory)
|
||||
_ = os.remove(output)
|
||||
testing.expect(t, os.make_directory(directory) == nil)
|
||||
testing.expect(t, os.write_entire_file(main_path, transmute([]byte)text))
|
||||
status := compiler_core.compile_package(directory, output)
|
||||
testing.expect_value(t, status, 0)
|
||||
state := run_executable(output)
|
||||
testing.expect(t, !state.success)
|
||||
_ = os.remove(output)
|
||||
_ = os2.remove_all(directory)
|
||||
delete(text)
|
||||
delete(output)
|
||||
delete(main_path)
|
||||
delete(directory)
|
||||
}
|
||||
}
|
||||
|
||||
@(test)
|
||||
qualified_division_builtin_names_resolve_as_package_functions :: proc(t: ^testing.T) {
|
||||
directory := "/tmp/brolang-test-qualified-division"
|
||||
math_directory := "/tmp/brolang-test-qualified-division/math"
|
||||
app_directory := "/tmp/brolang-test-qualified-division/app"
|
||||
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 }
|
||||
`
|
||||
main_text := `math :: import "../math"
|
||||
main func() i32 { return math.div_floor(20, 22) }
|
||||
`
|
||||
_ = 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.make_directory(math_directory) == nil)
|
||||
testing.expect(t, os.make_directory(app_directory) == nil)
|
||||
testing.expect(t, os.write_entire_file(math_path, transmute([]byte)math_text))
|
||||
testing.expect(t, os.write_entire_file(main_path, transmute([]byte)main_text))
|
||||
status := compiler_core.compile_package(app_directory, output)
|
||||
testing.expect_value(t, status, 0)
|
||||
state := run_executable(output)
|
||||
testing.expect_value(t, state.exit_code, 42)
|
||||
}
|
||||
|
||||
@(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) }
|
||||
main func() void {
|
||||
_ = floor_i32(5, 3)
|
||||
_ = ceil_i32(5, 3)
|
||||
_ = exact_i32(6, 3)
|
||||
_ = floor_u32(5, 3)
|
||||
_ = rem_i16(5, 3)
|
||||
_ = mod_i16(5, 3)
|
||||
_ = floor_f32(f32(5.0), f32(3.0))
|
||||
_ = ceil_f64(5.0, 3.0)
|
||||
_ = exact_f32(f32(6.0), f32(3.0))
|
||||
_ = rem_f64(5.0, 3.0)
|
||||
_ = mod_f32(f32(5.0), f32(3.0))
|
||||
}
|
||||
`
|
||||
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)
|
||||
ir_module := lower.lower(&hir_module)
|
||||
defer ir.destroy_module(&ir_module)
|
||||
llvm_text := llvm.emit(&ir_module, &diagnostics, &symbols)
|
||||
defer delete(llvm_text)
|
||||
testing.expect_value(t, len(diagnostics.items), 0)
|
||||
testing.expect_value(t, strings.count(llvm_text, "sdiv i32"), 3)
|
||||
testing.expect(t, !strings.contains(llvm_text, "srem i32"))
|
||||
testing.expect(t, strings.contains(llvm_text, "udiv i32"))
|
||||
testing.expect(t, strings.contains(llvm_text, "srem i16"))
|
||||
testing.expect(t, strings.contains(llvm_text, "remspecial"))
|
||||
testing.expect(t, strings.contains(llvm_text, "divzero_trap"))
|
||||
testing.expect(t, strings.contains(llvm_text, "divovf_trap"))
|
||||
testing.expect(t, strings.contains(llvm_text, "call float @llvm.floor.f32"))
|
||||
testing.expect(t, strings.contains(llvm_text, "call double @llvm.ceil.f64"))
|
||||
testing.expect(t, strings.contains(llvm_text, "call float @llvm.trunc.f32"))
|
||||
testing.expect(t, strings.contains(llvm_text, "frem double"))
|
||||
}
|
||||
|
||||
@(test)
|
||||
distinct_types_preserve_nominal_identity_and_backing_representation :: proc(t: ^testing.T) {
|
||||
text := `Point :: struct {
|
||||
|
||||
Reference in New Issue
Block a user