error handling kickoff
This commit is contained in:
+162
-19
@@ -90,7 +90,7 @@ main :: func() void { _ = value }
|
||||
compact_ids_reserve_invalid_values_and_preserve_layout :: proc(t: ^testing.T) {
|
||||
testing.expect_value(t, size_of(source.Span), 12)
|
||||
testing.expect_value(t, size_of(token.Token), 24)
|
||||
testing.expect(t, size_of(ast.Expr) <= 64)
|
||||
testing.expect(t, size_of(ast.Expr) <= 88)
|
||||
testing.expect(t, size_of(hir.Expr) <= 88)
|
||||
testing.expect(t, size_of(ir.Instruction) <= 88)
|
||||
testing.expect_value(t, size_of(types.Type), 4)
|
||||
@@ -2169,9 +2169,9 @@ tagged_union_compiles_and_runs :: proc(t: ^testing.T) {
|
||||
@(test)
|
||||
tagged_union_stores_the_discriminant :: proc(t: ^testing.T) {
|
||||
// The runtime test observes only the payload; this one checks the *tag* is written.
|
||||
// `Animal` is unbacked/dense (dog=0, cat=1, bird=2) in a u8 backing, so `Data{ bird = 99 }`
|
||||
// lays out as `{ i8, [3 x i8], i32 }` (tag at 0, i32 payload at offset 4) and writes the
|
||||
// discriminant `store i8 2` beside the payload `store i32 99`.
|
||||
// Native sum tags are global u16 IDs. `Animal` contributes dog/cat/bird (1..3),
|
||||
// then `Data` contributes dog:i32/bird:i32 (4..5), so `Data{ bird = 99 }`
|
||||
// writes discriminant `store i16 5` beside the payload.
|
||||
text := `Animal :: enum {
|
||||
dog
|
||||
cat
|
||||
@@ -2203,8 +2203,8 @@ main :: func() i32 {
|
||||
defer delete(llvm_text)
|
||||
|
||||
testing.expect_value(t, len(diagnostics.items), 0)
|
||||
testing.expect(t, strings.contains(llvm_text, "{ i8, [3 x i8], i32 }"))
|
||||
testing.expect(t, strings.contains(llvm_text, "store i8 2,"))
|
||||
testing.expect(t, strings.contains(llvm_text, "{ i16, [2 x i8], i32 }"))
|
||||
testing.expect(t, strings.contains(llvm_text, "store i16 5,"))
|
||||
testing.expect(t, strings.contains(llvm_text, "store i32 99,"))
|
||||
}
|
||||
|
||||
@@ -2251,6 +2251,151 @@ main :: func() i32 {
|
||||
testing.expect(t, found_variant)
|
||||
}
|
||||
|
||||
@(test)
|
||||
sum_composition_merges_widens_and_rejects_conflicts :: proc(t: ^testing.T) {
|
||||
text := `A :: enum {
|
||||
same
|
||||
left
|
||||
}
|
||||
B :: enum {
|
||||
same
|
||||
right
|
||||
}
|
||||
Both :: alias A | B
|
||||
UA :: union(enum) {
|
||||
item i32
|
||||
}
|
||||
UB :: union(enum) {
|
||||
empty void
|
||||
}
|
||||
UBoth :: alias UA | UB
|
||||
pick :: func(value Both) i32 {
|
||||
match value {
|
||||
.same: return 1
|
||||
.left: return 2
|
||||
.right: return 3
|
||||
}
|
||||
}
|
||||
payload :: func(value UBoth) i32 {
|
||||
match value {
|
||||
.item |n|: return n
|
||||
.empty: return 5
|
||||
}
|
||||
}
|
||||
main :: func() i32 {
|
||||
a A = .left
|
||||
b B = .right
|
||||
u UA = UA{ item = 4 }
|
||||
v UB = .empty
|
||||
return pick(.same) + pick(a) + pick(b) + payload(u) + payload(v)
|
||||
}
|
||||
`
|
||||
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(t, strings.contains(llvm_text, "call void @llvm.memcpy.p0.p0.i64"))
|
||||
|
||||
conflict := `A :: union(enum) {
|
||||
dup i32
|
||||
}
|
||||
B :: union(enum) {
|
||||
dup i64
|
||||
}
|
||||
Bad :: alias A | B
|
||||
main :: func() void {}
|
||||
`
|
||||
conflict_source := source.Source{path="conflict.bro", text=conflict}
|
||||
conflict_diagnostics := source.init_diagnostics(&conflict_source)
|
||||
defer source.destroy_diagnostics(&conflict_diagnostics)
|
||||
conflict_symbols := symbol.init_table()
|
||||
defer symbol.destroy_table(&conflict_symbols)
|
||||
conflict_stream := lexer.lex(&conflict_source, &conflict_diagnostics, &conflict_symbols)
|
||||
defer delete(conflict_stream.items)
|
||||
conflict_module := parser.parse(&conflict_stream, &conflict_source, &conflict_diagnostics)
|
||||
defer ast.destroy_module(&conflict_module)
|
||||
found_conflict := false
|
||||
for diagnostic in conflict_diagnostics.items {
|
||||
found_conflict = found_conflict || strings.contains(diagnostic.message, "same variant name")
|
||||
}
|
||||
testing.expect(t, found_conflict)
|
||||
|
||||
backed := `A :: enum(u8) {
|
||||
a = 1
|
||||
}
|
||||
B :: enum {
|
||||
b
|
||||
}
|
||||
Bad :: alias A | B
|
||||
main :: func() void {}
|
||||
`
|
||||
backed_source := source.Source{path="backed.bro", text=backed}
|
||||
backed_diagnostics := source.init_diagnostics(&backed_source)
|
||||
defer source.destroy_diagnostics(&backed_diagnostics)
|
||||
backed_symbols := symbol.init_table()
|
||||
defer symbol.destroy_table(&backed_symbols)
|
||||
backed_stream := lexer.lex(&backed_source, &backed_diagnostics, &backed_symbols)
|
||||
defer delete(backed_stream.items)
|
||||
backed_module := parser.parse(&backed_stream, &backed_source, &backed_diagnostics)
|
||||
defer ast.destroy_module(&backed_module)
|
||||
found_backed := false
|
||||
for diagnostic in backed_diagnostics.items {
|
||||
found_backed = found_backed || strings.contains(diagnostic.message, "native unbacked")
|
||||
}
|
||||
testing.expect(t, found_backed)
|
||||
}
|
||||
|
||||
@(test)
|
||||
yield_inference_visits_yielded_calls :: proc(t: ^testing.T) {
|
||||
text := `identity :: func(value int) int {
|
||||
return value
|
||||
}
|
||||
main :: func() i32 {
|
||||
value :: {
|
||||
yield identity(41)
|
||||
}
|
||||
return value - 41
|
||||
}
|
||||
`
|
||||
source_file := source.Source{path="yield_call.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)
|
||||
testing.expect(t, len(hir_module.functions) > 1)
|
||||
}
|
||||
|
||||
@(test)
|
||||
errors_example_compiles_and_runs :: proc(t: ^testing.T) {
|
||||
output := "/tmp/brolang-test-errors"
|
||||
defer _ = os.remove(output)
|
||||
status := compiler_core.compile_package("examples/programs/errors", output)
|
||||
testing.expect_value(t, status, 0)
|
||||
state := run_executable(output)
|
||||
testing.expect_value(t, state.exit_code, 0)
|
||||
}
|
||||
|
||||
@(test)
|
||||
match_compiles_and_runs :: proc(t: ^testing.T) {
|
||||
output := "/tmp/brolang-test-match"
|
||||
@@ -2268,8 +2413,7 @@ match_compiles_and_runs :: proc(t: ^testing.T) {
|
||||
match_dispatches_on_the_tag :: proc(t: ^testing.T) {
|
||||
// A tagged-union match desugars to: read the discriminant once (a load of the tag
|
||||
// enum at the union's offset 0), then compare it against each variant's tag value.
|
||||
// `Animal` is dense in a u8 backing (dog=0, bird=2), so the dog arm compares the
|
||||
// loaded i8 tag against 0.
|
||||
// Data's runtime tag is the hidden global u16 variant ID.
|
||||
text := `Animal :: enum {
|
||||
dog
|
||||
cat
|
||||
@@ -2306,9 +2450,8 @@ main :: func() i32 {
|
||||
defer delete(llvm_text)
|
||||
|
||||
testing.expect_value(t, len(diagnostics.items), 0)
|
||||
// The tag is loaded as an i8 and compared (icmp eq) against the dog variant's tag (0).
|
||||
testing.expect(t, strings.contains(llvm_text, "load i8"))
|
||||
testing.expect(t, strings.contains(llvm_text, "icmp eq i8"))
|
||||
testing.expect(t, strings.contains(llvm_text, "load i16"))
|
||||
testing.expect(t, strings.contains(llvm_text, "icmp eq i16"))
|
||||
}
|
||||
|
||||
@(test)
|
||||
@@ -7038,21 +7181,21 @@ main :: func() i32 {
|
||||
testing.expect_value(t, len(diagnostics.items), 0)
|
||||
testing.expect(t, animal_ok && nat_ok)
|
||||
testing.expect(t, types.is_enum(animal, &ast_module.type_store))
|
||||
testing.expect_value(t, animal_node.child, types.U8)
|
||||
testing.expect_value(t, animal_node.child, types.U16)
|
||||
testing.expect(t, !animal_node.explicit_backing)
|
||||
testing.expect_value(t, nat_node.child, types.U16)
|
||||
testing.expect(t, nat_node.explicit_backing)
|
||||
testing.expect_value(t, len(animal_members), 3)
|
||||
testing.expect_value(t, animal_members[0].value, i128(0))
|
||||
testing.expect_value(t, animal_members[2].value, i128(2))
|
||||
testing.expect_value(t, animal_members[0].value, i128(1))
|
||||
testing.expect_value(t, animal_members[2].value, i128(3))
|
||||
testing.expect_value(t, nat_members[0].value, i128(1))
|
||||
testing.expect_value(t, nat_members[1].value, i128(2))
|
||||
testing.expect_value(t, nat_members[2].value, i128(5))
|
||||
testing.expect_value(t, types.runtime_representation(animal, &ast_module.type_store), types.U8)
|
||||
testing.expect_value(t, types.size(animal, &ast_module.type_store), u64(1))
|
||||
testing.expect_value(t, types.runtime_representation(animal, &ast_module.type_store), types.U16)
|
||||
testing.expect_value(t, types.size(animal, &ast_module.type_store), u64(2))
|
||||
testing.expect(t, hir_module.globals[0].is_static)
|
||||
testing.expect_value(t, hir_module.globals[0].static_value, i64(0))
|
||||
testing.expect(t, strings.contains(llvm_text, "@bro.g.0 = internal constant i8 0"))
|
||||
testing.expect_value(t, hir_module.globals[0].static_value, i64(1))
|
||||
testing.expect(t, strings.contains(llvm_text, "@bro.g.0 = internal constant i16 1"))
|
||||
found_promotion := false
|
||||
for function in ir_module.functions {
|
||||
for instruction in function.instructions {
|
||||
@@ -7063,7 +7206,7 @@ main :: func() i32 {
|
||||
}
|
||||
|
||||
@(test)
|
||||
unbacked_enum_selects_the_smallest_fitting_unsigned_backing :: proc(t: ^testing.T) {
|
||||
unbacked_enum_uses_global_u16_backing :: proc(t: ^testing.T) {
|
||||
builder := strings.builder_make()
|
||||
defer strings.builder_destroy(&builder)
|
||||
strings.write_string(&builder, "Large :: enum {\n")
|
||||
|
||||
Reference in New Issue
Block a user