fix comptime specialization, implement io.print
This commit is contained in:
+204
-1
@@ -2425,6 +2425,7 @@ milestone_37_tuples_reflection_inline_for_and_debug_print_compile_and_run :: pro
|
||||
testing.expect_value(t, len(diagnostics.items), 0)
|
||||
testing.expect(t, !strings.contains(llvm_text, "FormatToken"))
|
||||
testing.expect(t, !strings.contains(llvm_text, "parse_format"))
|
||||
testing.expect(t, !strings.contains(llvm_text, "format_field_name"))
|
||||
testing.expect(t, !strings.contains(llvm_text, "FieldInfo"))
|
||||
testing.expect(t, !strings.contains(llvm_text, "RecordInfo"))
|
||||
|
||||
@@ -2441,7 +2442,7 @@ milestone_37_tuples_reflection_inline_for_and_debug_print_compile_and_run :: pro
|
||||
defer delete(stderr)
|
||||
testing.expect_value(t, state.exit_code, 0)
|
||||
testing.expect_value(t, string(stdout), "")
|
||||
testing.expect_value(t, string(stderr), "tuple=40/bro, limits=-9223372036854775808/18446744073709551615")
|
||||
testing.expect_value(t, string(stderr), "hello!\ntuple=40/bro, limits=-9223372036854775808/18446744073709551615")
|
||||
}
|
||||
|
||||
@(test)
|
||||
@@ -2531,6 +2532,171 @@ milestone_37_inline_loop_control_must_be_statically_resolvable :: proc(t: ^testi
|
||||
testing.expect(t, found)
|
||||
}
|
||||
|
||||
@(test)
|
||||
milestone_37_comptime_undefined_aggregates_support_full_initialization :: proc(t: ^testing.T) {
|
||||
text := `Token :: struct {
|
||||
text []u8
|
||||
count usize
|
||||
}
|
||||
Partial :: struct { initialized i32, text []u8 }
|
||||
make_tokens func() [2]mut Token {
|
||||
tokens [2]mut Token = undefined
|
||||
tokens[0] = Token {text = "a", count = 1}
|
||||
tokens[1].text = "bro"
|
||||
tokens[1].count = 3
|
||||
return tokens
|
||||
}
|
||||
read_initialized_sibling func() i32 {
|
||||
value Partial = undefined
|
||||
value.initialized = 42
|
||||
return value.initialized
|
||||
}
|
||||
answer :: $read_initialized_sibling()
|
||||
main func() i32 {
|
||||
total usize = 0
|
||||
inline for make_tokens() |token| {
|
||||
total += token.text.len + token.count
|
||||
}
|
||||
if answer != 42 or total != 8 {
|
||||
return 1
|
||||
}
|
||||
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)
|
||||
|
||||
testing.expect_value(t, len(diagnostics.items), 0)
|
||||
}
|
||||
|
||||
@(test)
|
||||
milestone_37_comptime_undefined_values_cannot_be_observed :: proc(t: ^testing.T) {
|
||||
text := `Bad :: struct { value i32, text []u8 }
|
||||
read_scalar func() i32 {
|
||||
value i32 = undefined
|
||||
return value
|
||||
}
|
||||
return_partial func() Bad {
|
||||
value Bad = undefined
|
||||
value.value = 1
|
||||
return value
|
||||
}
|
||||
take_bad func(value Bad) i32 {
|
||||
return value.value
|
||||
}
|
||||
pass_partial func() i32 {
|
||||
value Bad = undefined
|
||||
value.value = 1
|
||||
return take_bad(value)
|
||||
}
|
||||
bad_scalar :: $read_scalar()
|
||||
bad_record :: $return_partial()
|
||||
bad_argument :: $pass_partial()
|
||||
main func() void {}
|
||||
`
|
||||
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_read := false
|
||||
found_return := false
|
||||
found_pass := false
|
||||
for diagnostic in diagnostics.items {
|
||||
found_read = found_read || strings.contains(diagnostic.message, "cannot read an undefined value at comptime")
|
||||
found_return = found_return || strings.contains(diagnostic.message, "comptime function returned an undefined value")
|
||||
found_pass = found_pass || strings.contains(diagnostic.message, "cannot pass an undefined value at comptime")
|
||||
}
|
||||
testing.expect(t, found_read)
|
||||
testing.expect(t, found_return)
|
||||
testing.expect(t, found_pass)
|
||||
}
|
||||
|
||||
@(test)
|
||||
milestone_37_inline_expansions_keep_distinct_call_resolutions :: proc(t: ^testing.T) {
|
||||
text := `identity func($T type, value T) T {
|
||||
return value
|
||||
}
|
||||
main func() i32 {
|
||||
total i64 = 0
|
||||
inline for {{i8(1), i16(2)}, {i32(3), i64(4)}} |row| {
|
||||
inline for row |value| {
|
||||
total += i64(identity(value))
|
||||
}
|
||||
}
|
||||
return i32(total - 10)
|
||||
}
|
||||
`
|
||||
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)
|
||||
|
||||
identity_symbol := symbol.intern(&symbols, "identity")
|
||||
specializations := 0
|
||||
for function in hir_module.functions {
|
||||
specializations += 1 if function.name == identity_symbol else 0
|
||||
}
|
||||
testing.expect_value(t, len(diagnostics.items), 0)
|
||||
testing.expect_value(t, specializations, 4)
|
||||
}
|
||||
|
||||
@(test)
|
||||
milestone_37_inline_control_prunes_inference_after_static_exit :: proc(t: ^testing.T) {
|
||||
text := `take_i8 func(value i8) void { _ = value }
|
||||
main func() void {
|
||||
inline for {i8(1), "skip"} |value, index| {
|
||||
if index == 1 {
|
||||
continue
|
||||
}
|
||||
take_i8(value)
|
||||
}
|
||||
inline for {i8(1), "stop"} |value, index| {
|
||||
if index == 1 {
|
||||
break
|
||||
}
|
||||
take_i8(value)
|
||||
}
|
||||
}
|
||||
`
|
||||
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)
|
||||
|
||||
testing.expect_value(t, len(diagnostics.items), 0)
|
||||
}
|
||||
|
||||
@(test)
|
||||
milestone_33_rejects_an_incompatible_runtime_write_declaration :: proc(t: ^testing.T) {
|
||||
text := `write c_func(_ c_int, _ c_int, _ c_ulong) c_long
|
||||
@@ -2877,6 +3043,36 @@ main func() void {
|
||||
testing.expect(t, found_function)
|
||||
}
|
||||
|
||||
@(test)
|
||||
generic_parameter_usage_is_source_based :: proc(t: ^testing.T) {
|
||||
text := `choose func($N usize, used, unused i32) i32 {
|
||||
if N > 0 {
|
||||
return used
|
||||
}
|
||||
return 0
|
||||
}
|
||||
main func() void {
|
||||
_ = choose(0, 1, 2)
|
||||
}
|
||||
`
|
||||
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)
|
||||
|
||||
testing.expect_value(t, len(diagnostics.items), 1)
|
||||
testing.expect_value(t, diagnostics.items[0].severity, source.Severity.Warning)
|
||||
testing.expect(t, strings.contains(diagnostics.items[0].message, "unused parameter 'unused'"))
|
||||
testing.expect(t, !strings.contains(diagnostics.items[0].message, "unused parameter 'used'"))
|
||||
}
|
||||
|
||||
@(test)
|
||||
recursive_specialization_reaches_a_fixed_point :: proc(t: ^testing.T) {
|
||||
text := `a func(value int) i32 {
|
||||
@@ -3204,6 +3400,7 @@ unknown func($T type) T { value T = undefined; return value }
|
||||
partial func($T type, $N usize, value T) T { return value }
|
||||
use_ignored func($T type, value Ignored(T)) i32 { return value }
|
||||
use_alias func($T type, value BoxAlias(T)) T { return value.value }
|
||||
mapping_fail func($A usize, value i32, $B usize) void {}
|
||||
main func() void {
|
||||
a i32 :: 1
|
||||
b u32 :: 2
|
||||
@@ -3213,6 +3410,7 @@ main func() void {
|
||||
_ = use_ignored(a)
|
||||
box Box(i32) :: Box(i32) { value = 1 }
|
||||
_ = use_alias(box)
|
||||
mapping_fail(true, false)
|
||||
}
|
||||
`
|
||||
source_file := source.Source{path="test.bro", text=text}
|
||||
@@ -3230,12 +3428,16 @@ main func() void {
|
||||
found_conflict := false
|
||||
found_unknown := false
|
||||
found_partial := false
|
||||
found_candidate_failures := false
|
||||
unrecoverable := 0
|
||||
for diagnostic in diagnostics.items {
|
||||
message := diagnostic.message
|
||||
found_conflict = found_conflict || strings.contains(message, "conflicting inference for comptime parameter 'T': i32 and u32")
|
||||
found_unknown = found_unknown || strings.contains(message, "cannot infer comptime parameter 'T'")
|
||||
found_partial = found_partial || strings.contains(message, "cannot infer comptime parameter 'N'")
|
||||
found_candidate_failures = found_candidate_failures ||
|
||||
strings.contains(message, "candidate 1: cannot infer comptime parameter 'B'") &&
|
||||
strings.contains(message, "candidate 2: cannot infer comptime parameter 'A'")
|
||||
if strings.contains(message, "cannot infer comptime parameter 'T'") {
|
||||
unrecoverable += 1
|
||||
}
|
||||
@@ -3243,6 +3445,7 @@ main func() void {
|
||||
testing.expect(t, found_conflict)
|
||||
testing.expect(t, found_unknown)
|
||||
testing.expect(t, found_partial)
|
||||
testing.expect(t, found_candidate_failures)
|
||||
testing.expect(t, unrecoverable >= 3)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user