void-payloads, multi-pattern arms, and range patterns (match statements)
This commit is contained in:
@@ -2402,6 +2402,131 @@ main :: func() i32 {
|
||||
testing.expect(t, found_unknown)
|
||||
}
|
||||
|
||||
@(test)
|
||||
match_range_arm_emits_bounds :: proc(t: ^testing.T) {
|
||||
// A scalar range arm `lo..hi:` desugars to `key >= lo and key < hi` (inclusive uses
|
||||
// `<=`), emitted as signed integer comparisons for an i32 subject.
|
||||
text := `main :: func() i32 {
|
||||
n i32 = 5
|
||||
out i32 = 0
|
||||
match n {
|
||||
0..10: out = 1
|
||||
10..=20: out = 2
|
||||
else: out = 3
|
||||
}
|
||||
return out
|
||||
}
|
||||
`
|
||||
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, "icmp sge i32")) // key >= lo
|
||||
testing.expect(t, strings.contains(llvm_text, "icmp slt i32")) // key < hi (exclusive)
|
||||
testing.expect(t, strings.contains(llvm_text, "icmp sle i32")) // key <= hi (inclusive)
|
||||
}
|
||||
|
||||
@(test)
|
||||
match_extended_misuse_is_diagnosed :: proc(t: ^testing.T) {
|
||||
// Five rejected forms from milestone 22.5: (1) a capture on a void variant, (2) a value
|
||||
// given to a void variant in construction, (3) a bare key on a non-void field, (4) a
|
||||
// range pattern on an enum subject, and (5) a multi-pattern capture whose variants have
|
||||
// different payload types. (Pointer capture on an rvalue subject is also rejected, but
|
||||
// match-on-call-result is a separate pre-existing gap so it isn't exercised here.)
|
||||
text := `Animal :: enum {
|
||||
dog
|
||||
cat
|
||||
bird
|
||||
}
|
||||
Point :: struct {
|
||||
x i32
|
||||
y i32
|
||||
}
|
||||
Box :: union(enum) {
|
||||
point Point
|
||||
count i32
|
||||
empty void
|
||||
}
|
||||
void_capture :: func(b Box) i32 {
|
||||
match b {
|
||||
.point |p|: { return p.x }
|
||||
.count |c|: { return c }
|
||||
.empty |x|: { return 0 }
|
||||
}
|
||||
return 0
|
||||
}
|
||||
void_value :: func() i32 {
|
||||
b Box = Box{ empty = 5 }
|
||||
return 0
|
||||
}
|
||||
bare_on_nonvoid :: func() i32 {
|
||||
b Box = Box{ count }
|
||||
return 0
|
||||
}
|
||||
range_on_enum :: func(a Animal) i32 {
|
||||
match a {
|
||||
0..2: { return 1 }
|
||||
else: { return 0 }
|
||||
}
|
||||
return 0
|
||||
}
|
||||
incompatible_capture :: func(b Box) i32 {
|
||||
match b {
|
||||
.point, .count |v|: { return 0 }
|
||||
.empty: { return 0 }
|
||||
}
|
||||
return 0
|
||||
}
|
||||
main :: func() i32 {
|
||||
b Box = Box{ count = 1 }
|
||||
return void_capture(b) + void_value() + bare_on_nonvoid() + range_on_enum(.dog) +
|
||||
incompatible_capture(b)
|
||||
}
|
||||
`
|
||||
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_void_capture := false
|
||||
found_void_value := false
|
||||
found_bare := false
|
||||
found_range_enum := false
|
||||
found_incompatible := false
|
||||
for diagnostic in diagnostics.items {
|
||||
found_void_capture = found_void_capture || strings.contains(diagnostic.message, "void payload")
|
||||
found_void_value = found_void_value || strings.contains(diagnostic.message, "void variant 'empty' takes no value")
|
||||
found_bare = found_bare || strings.contains(diagnostic.message, "field 'count' requires a value")
|
||||
found_range_enum = found_range_enum || strings.contains(diagnostic.message, "range patterns only apply to scalar")
|
||||
found_incompatible = found_incompatible || strings.contains(diagnostic.message, "capture group with incompatible types")
|
||||
}
|
||||
testing.expect(t, found_void_capture)
|
||||
testing.expect(t, found_void_value)
|
||||
testing.expect(t, found_bare)
|
||||
testing.expect(t, found_range_enum)
|
||||
testing.expect(t, found_incompatible)
|
||||
}
|
||||
|
||||
@(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