tagged unions
This commit is contained in:
@@ -2152,6 +2152,105 @@ native_union_compiles_and_runs :: proc(t: ^testing.T) {
|
||||
testing.expect_value(t, state.exit_code, 42)
|
||||
}
|
||||
|
||||
@(test)
|
||||
tagged_union_compiles_and_runs :: proc(t: ^testing.T) {
|
||||
output := "/tmp/brolang-test-tagged-union"
|
||||
defer _ = os.remove(output)
|
||||
status := compiler_core.compile_package("examples/programs/tagged_union", output)
|
||||
testing.expect_value(t, status, 0)
|
||||
state := run_executable(output)
|
||||
// Both tagged forms — `union(Animal)` (existing enum tag) and `union(enum)` (synthesized
|
||||
// tag) — constructed via keyed literals, stored as `{tag, payload}`, with the active
|
||||
// payload read back at its post-tag offset: 37 + 5 = 42. Non-zero payloads make a wrong
|
||||
// payload offset (e.g. overlapping the tag) fail the exit code.
|
||||
testing.expect_value(t, state.exit_code, 42)
|
||||
}
|
||||
|
||||
@(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`.
|
||||
text := `Animal :: enum {
|
||||
dog
|
||||
cat
|
||||
bird
|
||||
}
|
||||
Data :: union(Animal) {
|
||||
dog i32
|
||||
bird i32
|
||||
}
|
||||
main :: func() i32 {
|
||||
x Data = Data{ bird = 99 }
|
||||
return x.bird
|
||||
}
|
||||
`
|
||||
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, "{ i8, [3 x i8], i32 }"))
|
||||
testing.expect(t, strings.contains(llvm_text, "store i8 2,"))
|
||||
testing.expect(t, strings.contains(llvm_text, "store i32 99,"))
|
||||
}
|
||||
|
||||
@(test)
|
||||
tagged_union_validation_is_diagnosed :: proc(t: ^testing.T) {
|
||||
// A `union(T)` tag must be an enum, and every variant of a `union(Enum)` must name a
|
||||
// member of that enum.
|
||||
text := `Color :: struct {
|
||||
r u8
|
||||
}
|
||||
Animal :: enum {
|
||||
dog
|
||||
cat
|
||||
}
|
||||
BadTag :: union(Color) {
|
||||
dog i32
|
||||
}
|
||||
BadVariant :: union(Animal) {
|
||||
snake i32
|
||||
}
|
||||
main :: func() i32 {
|
||||
return 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)
|
||||
|
||||
found_tag := false
|
||||
found_variant := false
|
||||
for diagnostic in diagnostics.items {
|
||||
found_tag = found_tag || strings.contains(diagnostic.message, "tagged union's tag must be an enum")
|
||||
found_variant = found_variant || strings.contains(diagnostic.message, "'snake' is not a member of the tag enum")
|
||||
}
|
||||
testing.expect(t, found_tag)
|
||||
testing.expect(t, found_variant)
|
||||
}
|
||||
|
||||
@(test)
|
||||
yield_misuse_is_diagnosed :: proc(t: ^testing.T) {
|
||||
// A value block that does not end in `yield`, and a `yield` nested inside an
|
||||
|
||||
Reference in New Issue
Block a user