anonymous struct payloads

This commit is contained in:
2026-06-30 23:01:18 +02:00
parent eb06b2ee62
commit d6f9c24314
6 changed files with 317 additions and 38 deletions
+155
View File
@@ -2568,6 +2568,90 @@ main func() i32 {
testing.expect(t, strings.contains(llvm_text, "call void @llvm.memcpy.p0.p0.i64"))
}
@(test)
anonymous_struct_payloads_and_keyed_payload_sugar_compile :: proc(t: ^testing.T) {
text := `PayloadError :: union(enum) {
not_found struct {
path i32
line i32
}
wrapped struct {
path i32
line i32
}
scalar i32
empty void
}
OtherError :: union(enum) {
not_found struct {
path i32
line i32
}
other void
}
BothError :: alias PayloadError | OtherError
accept func(value PayloadError) i32 {
match value {
.not_found |info|: return info.path + info.line
.wrapped |info|: return info.path * 10 + info.line
.scalar |n|: return n
.empty: return 0
}
}
with_payload func(value i32) i32 ! PayloadError {
if (value == 0) return .not_found{path = 4, line = 5}
if (value == 1) return .scalar{7}
return value
}
inline_payload func(value i32) i32 ! union(enum) {
inline_bad struct {
code i32
line i32
}
} {
if (value == 0) return .inline_bad{code = 6, line = 7}
return value
}
main func() i32 {
e PayloadError = .not_found{path = 1, line = 2}
a :: accept(e)
b :: accept(.wrapped{line = 4, path = 3})
c :: with_payload(0) catch |err| {
match err {
.not_found |info|: yield info.path + info.line
.wrapped |info|: yield info.path + info.line
.scalar |n|: yield n
.empty: yield 0
}
}
d :: inline_payload(0) catch |err| {
match err {
.inline_bad |info|: yield info.code + info.line
}
}
return a + b + c + d - 59
}
`
source_file := source.Source{path="anonymous_payloads.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, len(llvm_text) > 0)
}
@(test)
inline_fallible_error_types_compile :: proc(t: ^testing.T) {
text := `inline_enum func(value i32) i32 ! enum {
@@ -2689,6 +2773,77 @@ main func() i32 {
testing.expect(t, found_no_context)
}
@(test)
anonymous_struct_payloads_reject_bad_forms :: proc(t: ^testing.T) {
text := `Bad :: union(enum) {
payload struct {
x i32
y i32
}
scalar i32
}
unknown func() Bad {
return .payload{x = 1, z = 2}
}
duplicate func() Bad {
return .payload{x = 1, x = 2, y = 3}
}
missing func() Bad {
return .payload{x = 1}
}
scalar_keyed func() Bad {
return .scalar{value = 1}
}
A :: union(enum) {
dup struct {
x i32
}
}
B :: union(enum) {
dup struct {
x i64
}
}
Conflict :: alias A | B
main func() i32 {
_ = unknown()
_ = duplicate()
_ = missing()
_ = scalar_keyed()
return 0
}
`
source_file := source.Source{path="bad_anonymous_payloads.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_duplicate := false
found_missing := false
found_keyed_non_struct := false
found_conflict := false
for diagnostic in diagnostics.items {
found_unknown = found_unknown || strings.contains(diagnostic.message, "unknown struct field 'z'")
found_duplicate = found_duplicate || strings.contains(diagnostic.message, "duplicate initializer for struct field 'x'")
found_missing = found_missing || strings.contains(diagnostic.message, "missing initializer for struct field 'y'")
found_keyed_non_struct = found_keyed_non_struct || strings.contains(diagnostic.message, "keyed contextual payload requires a struct payload")
found_conflict = found_conflict || strings.contains(diagnostic.message, "same variant name")
}
testing.expect(t, found_unknown)
testing.expect(t, found_duplicate)
testing.expect(t, found_missing)
testing.expect(t, found_keyed_non_struct)
testing.expect(t, found_conflict)
}
@(test)
errors_example_compiles_and_runs :: proc(t: ^testing.T) {
output := "/tmp/brolang-test-errors"