distinct type aliasing
This commit is contained in:
@@ -5231,3 +5231,181 @@ checked_division_and_subtraction_emit_guarded_llvm :: proc(t: ^testing.T) {
|
||||
testing.expect(t, strings.contains(llvm_text, "divzero_trap"))
|
||||
testing.expect(t, strings.contains(llvm_text, "divovf_trap"))
|
||||
}
|
||||
|
||||
@(test)
|
||||
distinct_types_preserve_nominal_identity_and_backing_representation :: proc(t: ^testing.T) {
|
||||
text := `Point :: struct {
|
||||
x i32
|
||||
y i32
|
||||
}
|
||||
UserID :: distinct u32
|
||||
OtherID :: distinct u32
|
||||
PointID :: distinct Point
|
||||
Bytes :: distinct [2]u8
|
||||
WrappedID :: distinct UserID
|
||||
static_id UserID :: UserID(42)
|
||||
take :: func(value UserID) UserID {
|
||||
return value
|
||||
}
|
||||
main :: func() i32 {
|
||||
id UserID :: UserID(7)
|
||||
copy UserID = take(id)
|
||||
maybe ?UserID = copy
|
||||
pointer @UserID = ©
|
||||
point PointID :: PointID(Point { x = 1, y = 2 })
|
||||
bytes Bytes :: Bytes([3, 4])
|
||||
wrapped WrappedID :: WrappedID(id)
|
||||
_ = maybe
|
||||
_ = pointer
|
||||
_ = point
|
||||
_ = bytes
|
||||
_ = wrapped
|
||||
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)
|
||||
ir_module := lower.lower(&hir_module)
|
||||
defer ir.destroy_module(&ir_module)
|
||||
llvm_text := llvm.emit(&ir_module, &diagnostics, &symbols)
|
||||
defer delete(llvm_text)
|
||||
|
||||
user_id := types.find_named(&ast_module.type_store, 0, u32(symbol.intern(&symbols, "UserID")))
|
||||
other_id := types.find_named(&ast_module.type_store, 0, u32(symbol.intern(&symbols, "OtherID")))
|
||||
point_id := types.find_named(&ast_module.type_store, 0, u32(symbol.intern(&symbols, "PointID")))
|
||||
point := types.find_named(&ast_module.type_store, 0, u32(symbol.intern(&symbols, "Point")))
|
||||
testing.expect_value(t, len(diagnostics.items), 0)
|
||||
testing.expect(t, user_id != other_id)
|
||||
testing.expect(t, user_id != types.U32)
|
||||
testing.expect(t, types.is_distinct(user_id, &ast_module.type_store))
|
||||
testing.expect_value(t, types.runtime_representation(user_id, &ast_module.type_store), types.U32)
|
||||
testing.expect_value(t, types.runtime_representation(point_id, &ast_module.type_store), point)
|
||||
testing.expect_value(t, types.size(user_id, &ast_module.type_store), types.size(types.U32, &ast_module.type_store))
|
||||
testing.expect(t, hir_module.globals[0].is_static)
|
||||
testing.expect_value(t, hir_module.globals[0].static_value, i64(42))
|
||||
testing.expect(t, strings.contains(llvm_text, "@bro.g.0 = internal constant i32 42"))
|
||||
testing.expect(t, strings.contains(llvm_text, "select i1 true, i32"))
|
||||
|
||||
retype_count := 0
|
||||
for function in ir_module.functions {
|
||||
for instruction in function.instructions {
|
||||
retype_count += 1 if instruction.op == .Retype else 0
|
||||
}
|
||||
}
|
||||
testing.expect_value(t, retype_count, 4)
|
||||
}
|
||||
|
||||
@(test)
|
||||
distinct_types_reject_implicit_conversions_operators_and_invalid_backings :: proc(t: ^testing.T) {
|
||||
text := `Opaque :: c_struct
|
||||
UserID :: distinct u32
|
||||
OtherID :: distinct u32
|
||||
BadInt :: distinct int
|
||||
BadVoid :: distinct void
|
||||
BadFunction :: distinct c_func() void
|
||||
BadOpaque :: distinct Opaque
|
||||
foreign :: c_func(value UserID) void
|
||||
foreign_pointer :: c_func(value @UserID) void
|
||||
main :: func() void {
|
||||
raw u32 = 1
|
||||
id UserID = raw
|
||||
backing u32 = UserID(2)
|
||||
other OtherID = UserID(3)
|
||||
narrow u8 = 4
|
||||
_ = UserID(narrow)
|
||||
_ = UserID()
|
||||
_ = UserID(1, 2)
|
||||
left UserID :: UserID(5)
|
||||
right UserID :: UserID(6)
|
||||
_ = left + right
|
||||
_ = left == right
|
||||
}
|
||||
`
|
||||
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)
|
||||
|
||||
invalid_backing_count := 0
|
||||
implicit_conversion_count := 0
|
||||
found_exact := false
|
||||
found_arity := false
|
||||
found_arithmetic := false
|
||||
found_comparison := false
|
||||
foreign_signature_count := 0
|
||||
for diagnostic in diagnostics.items {
|
||||
invalid_backing_count += 1 if strings.contains(diagnostic.message, "requires a concrete runtime backing type") else 0
|
||||
implicit_conversion_count += 1 if strings.contains(diagnostic.message, "cannot implicitly convert") else 0
|
||||
found_exact = found_exact || strings.contains(diagnostic.message, "requires an exact u32 value, got u8")
|
||||
found_arity = found_arity || strings.contains(diagnostic.message, "expects 1 argument")
|
||||
found_arithmetic = found_arithmetic || strings.contains(diagnostic.message, "arithmetic requires compatible numeric operands")
|
||||
found_comparison = found_comparison || strings.contains(diagnostic.message, "comparison requires compatible numeric operands")
|
||||
foreign_signature_count += 1 if strings.contains(diagnostic.message, "requires concrete parameter types") else 0
|
||||
}
|
||||
testing.expect_value(t, invalid_backing_count, 4)
|
||||
testing.expect(t, implicit_conversion_count >= 3)
|
||||
testing.expect(t, found_exact)
|
||||
testing.expect(t, found_arity)
|
||||
testing.expect(t, found_arithmetic)
|
||||
testing.expect(t, found_comparison)
|
||||
testing.expect_value(t, foreign_signature_count, 2)
|
||||
}
|
||||
|
||||
@(test)
|
||||
distinct_type_construction_defers_to_callable_names :: proc(t: ^testing.T) {
|
||||
text := `Value :: distinct u32
|
||||
Value :: func(value i32) i32 {
|
||||
return value
|
||||
}
|
||||
main :: func() i32 {
|
||||
return Value(42)
|
||||
}
|
||||
`
|
||||
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_call := false
|
||||
found_retype := false
|
||||
for expr in hir_module.exprs {
|
||||
found_call = found_call || expr.kind == .Call
|
||||
found_retype = found_retype || expr.kind == .Retype
|
||||
}
|
||||
testing.expect_value(t, len(diagnostics.items), 0)
|
||||
testing.expect(t, found_call)
|
||||
testing.expect(t, !found_retype)
|
||||
}
|
||||
|
||||
@(test)
|
||||
distinct_types_compile_and_run_across_packages :: proc(t: ^testing.T) {
|
||||
output := "/tmp/brolang-test-distinct-types"
|
||||
defer _ = os.remove(output)
|
||||
status := compiler_core.compile_package("examples/programs/distinct_types", 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