This commit is contained in:
2026-06-23 17:22:41 +02:00
parent f16f352d1e
commit 2f68fc966a
16 changed files with 809 additions and 38 deletions
+241
View File
@@ -2379,6 +2379,42 @@ libclang_import_preserves_external_object_and_final_macro_semantics :: proc(t: ^
testing.expect(t, result.available)
testing.expect_value(t, result.error_message, "")
enum_alias_type := cimport.INVALID_TYPE
for alias in result.aliases {
if alias.name == "Imported_Enum" {
enum_alias_type = alias.type
break
}
}
testing.expect(t, enum_alias_type != cimport.INVALID_TYPE)
if enum_alias_type != cimport.INVALID_TYPE {
testing.expect_value(t, result.types[enum_alias_type].kind, cimport.Type_Kind.C_Int)
}
enum_negative, found_enum_negative := find_cimport_macro(&result, "IMPORTED_ENUM_NEGATIVE")
enum_same, found_enum_same := find_cimport_macro(&result, "IMPORTED_ENUM_SAME")
enum_value, found_enum_value := find_cimport_macro(&result, "IMPORTED_ENUM_VALUE")
enum_back, found_enum_back := find_cimport_macro(&result, "IMPORTED_ENUM_BACK")
enum_anon, found_enum_anon := find_cimport_macro(&result, "IMPORTED_ANON_ENUM")
testing.expect(t, found_enum_negative && found_enum_same && found_enum_value && found_enum_back && found_enum_anon)
if found_enum_negative {
testing.expect(t, enum_negative.value.negative)
testing.expect_value(t, enum_negative.value.integer, u64(2))
}
if found_enum_same {
testing.expect(t, enum_same.value.negative)
testing.expect_value(t, enum_same.value.integer, u64(2))
}
if found_enum_value {
testing.expect(t, !enum_value.value.negative)
testing.expect_value(t, enum_value.value.integer, u64(7))
}
if found_enum_back {
testing.expect_value(t, enum_back.value.integer, u64(3))
}
if found_enum_anon {
testing.expect_value(t, enum_anon.value.integer, u64(9))
}
tls, found_tls := find_cimport_variable(&result, "imported_tls_global")
testing.expect(t, found_tls)
if found_tls {
@@ -5409,3 +5445,208 @@ distinct_types_compile_and_run_across_packages :: proc(t: ^testing.T) {
state := run_executable(output)
testing.expect_value(t, state.exit_code, 0)
}
@(test)
native_enums_preserve_identity_members_and_integer_representation :: proc(t: ^testing.T) {
text := `Animal :: enum {
dog
cat
bird
}
Nat :: enum(u16) {
one = 1
two
five = 5
}
global Animal :: Animal.dog
take :: func(value Animal) Animal {
return value
}
identity :: c_func(value Nat) Nat {
return value
}
variadic :: c_func(marker c_int, ...) c_int
main :: func() i32 {
value Animal = .cat
values [2]Animal :: [.dog, Animal.bird]
number Nat = identity(.two)
_ = variadic(0, number)
if take(value) == Animal.cat and values[0] != values[1] and number == Nat.two {
return 0
}
return 1
}
`
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)
animal := types.find_named(&ast_module.type_store, 0, u32(symbol.intern(&symbols, "Animal")))
nat := types.find_named(&ast_module.type_store, 0, u32(symbol.intern(&symbols, "Nat")))
animal_node, animal_ok := types.node(&ast_module.type_store, animal)
nat_node, nat_ok := types.node(&ast_module.type_store, nat)
animal_members := types.enum_members_for(&ast_module.type_store, animal)
nat_members := types.enum_members_for(&ast_module.type_store, nat)
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(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, 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(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"))
found_promotion := false
for function in ir_module.functions {
for instruction in function.instructions {
found_promotion = found_promotion || instruction.op == .C_Vararg_Promote
}
}
testing.expect(t, found_promotion)
}
@(test)
unbacked_enum_selects_the_smallest_fitting_unsigned_backing :: proc(t: ^testing.T) {
builder := strings.builder_make()
defer strings.builder_destroy(&builder)
strings.write_string(&builder, "Large :: enum {\n")
for index in 0..<257 {
fmt.sbprintf(&builder, "value_%d\n", index)
}
strings.write_string(&builder, "}\nmain :: func() void {}\n")
source_file := source.Source{path="test.bro", text=strings.to_string(builder)}
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)
module := parser.parse(&stream, &source_file, &diagnostics)
defer ast.destroy_module(&module)
large := types.find_named(&module.type_store, 0, u32(symbol.intern(&symbols, "Large")))
item, ok := types.node(&module.type_store, large)
testing.expect_value(t, len(diagnostics.items), 0)
testing.expect(t, ok)
testing.expect_value(t, item.child, types.U16)
testing.expect_value(t, len(types.enum_members_for(&module.type_store, large)), 257)
}
@(test)
native_enum_invalid_declarations_and_operations_are_diagnosed :: proc(t: ^testing.T) {
text := `Empty :: enum {}
Dense :: enum {
zero = 0
one
}
BadBacking :: enum(f32) {
value
}
Duplicate :: enum(u8) {
value
value
}
Jumbled :: enum(i8) {
second = 2
first = 1
}
Overflow :: enum(u8) {
value = 256
}
Other :: enum {
value
}
foreign :: c_func(value Dense) void
allowed :: c_func(value Overflow) Overflow
main :: func() void {
dense Dense = Other.value
_ = Dense.zero + Dense.one
_ = Dense.zero < Dense.one
_ = Dense.missing
_ = .zero
_ = dense
}
`
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_empty := false
found_unbacked_value := false
found_backing := false
found_duplicate := false
found_order := false
found_overflow := false
found_foreign := false
found_conversion := false
found_arithmetic := false
found_comparison := false
found_member := false
found_context := false
for diagnostic in diagnostics.items {
found_empty = found_empty || strings.contains(diagnostic.message, "require at least one member")
found_unbacked_value = found_unbacked_value || strings.contains(diagnostic.message, "explicit enum values require a backing type")
found_backing = found_backing || strings.contains(diagnostic.message, "requires a concrete integer backing type")
found_duplicate = found_duplicate || strings.contains(diagnostic.message, "duplicate enum member")
found_order = found_order || strings.contains(diagnostic.message, "strictly increasing")
found_overflow = found_overflow || strings.contains(diagnostic.message, "does not fit in u8")
found_foreign = found_foreign || strings.contains(diagnostic.message, "requires concrete parameter types")
found_conversion = found_conversion || strings.contains(diagnostic.message, "cannot implicitly convert")
found_arithmetic = found_arithmetic || strings.contains(diagnostic.message, "arithmetic requires compatible numeric operands")
found_comparison = found_comparison || strings.contains(diagnostic.message, "enum values only support")
found_member = found_member || strings.contains(diagnostic.message, "unknown enum member")
found_context = found_context || strings.contains(diagnostic.message, "requires an enum context")
}
testing.expect(t, found_empty)
testing.expect(t, found_unbacked_value)
testing.expect(t, found_backing)
testing.expect(t, found_duplicate)
testing.expect(t, found_order)
testing.expect(t, found_overflow)
testing.expect(t, found_foreign)
testing.expect(t, found_conversion)
testing.expect(t, found_arithmetic)
testing.expect(t, found_comparison)
testing.expect(t, found_member)
testing.expect(t, found_context)
}
@(test)
native_enums_compile_and_run_across_packages :: proc(t: ^testing.T) {
output := "/tmp/brolang-test-enums"
defer _ = os.remove(output)
status := compiler_core.compile_package("examples/programs/enums", output)
testing.expect_value(t, status, 0)
state := run_executable(output)
testing.expect_value(t, state.exit_code, 0)
}