enums
This commit is contained in:
@@ -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)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user