import "@std/mem" import "@std/testing" import "@source/diagnostics" import "@source/strpool" import "@source/lexer" import "@source/parser" import "@source/ast" sema :: import "@source/sema" @hide:file expect_kind proc(result @Result, id ast.NodeId, expected TypeKind) void ! testing.Error { try testing.expect_equal(expected, type_of(result, id).kind) try testing.expect_equal(diagnostics.no_diagnostic, type_of(result, id).diagnostic) } @hide:file expect_error proc( result @Result, diagnostic_store @diagnostics.Store, id ast.NodeId, code diagnostics.Code, span ast.Span, ) diagnostics.DiagnosticId ! testing.Error { actual :: type_of(result, id) try testing.expect_equal(TypeKind.invalid, actual.kind) if (diagnostics.get(diagnostic_store, actual.diagnostic)) |diagnostic| { try testing.expect_equal(code, diagnostic.code) try testing.expect_equal(span.start, diagnostic.span.start) try testing.expect_equal(span.end, diagnostic.span.end) } else { return .expectation_failed } return actual.diagnostic } @hide:file expect_fallthrough proc( result @Result, diagnostic_store @diagnostics.Store, function ast.NodeId, span ast.Span, ) diagnostics.DiagnosticId ! testing.Error { if (fallthrough_diagnostic(result, function)) |id| { if (diagnostics.get(diagnostic_store, id)) |diagnostic| { try testing.expect_equal(diagnostics.Code.missing_return, diagnostic.code) try testing.expect_equal(span.start, diagnostic.span.start) try testing.expect_equal(span.end, diagnostic.span.end) return id } } return .expectation_failed } @hide:file code_count proc(diagnostic_store @diagnostics.Store, code diagnostics.Code) usize { count := usize(0) for 0..diagnostics.count(diagnostic_store) |index| { if (diagnostics.get(diagnostic_store, diagnostics.DiagnosticId(index))) |diagnostic| { if (diagnostic.code == code) count += 1 } } return count } propagates_numeric_context_before_defaulting_forward_and_mutable_aliases test { strings := strpool.init(mem.c_allocator) defer strpool.deinit(&strings) diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) source :: `forward float :: -forwarded * 2 + seed `forwarded :: seed `seed := 3 `backward :: 4 `demand float :: backward `quotient :: -3 / 2 + 1 `mixed :: 3 / 2 `plain :: -3 + 2 * 4 `fraction :: 1.5 `text :: "honey" `fixed int :: 7 `anchored :: fixed + 2 `nested float :: quotient / (forwarded + 1) scan_state := lexer.init(mem.c_allocator) defer lexer.deinit(&scan_state) try lexer.scan(&scan_state, source, &strings, &diagnostic_store) tree := try parser.parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) defer ast.deinit(&tree) resolution := try sema.resolve(mem.c_allocator, &tree, &strings, &diagnostic_store) defer sema.deinit(&resolution) try testing.expect_equal(0, diagnostics.count(&diagnostic_store)) result := try check(mem.c_allocator, &tree, &resolution, &diagnostic_store) defer deinit(&result) expected [13]TypeKind :: [ .float, .float, .float, .float, .float, .float, .float, .int, .float, .string, .int, .int, .float, ] for expected |kind, index| { declaration :: ast.package_decl(&tree, index) view :: ast.statement_decl(&tree, declaration) try expect_kind(&result, declaration, kind) try expect_kind(&result, view.initializer, kind) if (view.type) |annotation| try expect_kind(&result, annotation, kind) } forward :: ast.binary_expr(&tree, ast.statement_decl(&tree, ast.package_decl(&tree, 0)).initializer) multiply :: ast.binary_expr(&tree, forward.left) negate :: ast.unary_expr(&tree, multiply.left) numeric_nodes [6]ast.NodeId :: [ forward.left, forward.right, multiply.left, multiply.right, negate.operand, ast.statement_decl(&tree, ast.package_decl(&tree, 2)).initializer, ] for (numeric_nodes) |id| try expect_kind(&result, id, .float) quotient :: ast.binary_expr(&tree, ast.statement_decl(&tree, ast.package_decl(&tree, 5)).initializer) mixed :: ast.binary_expr(&tree, ast.statement_decl(&tree, ast.package_decl(&tree, 6)).initializer) division_operands [4]ast.NodeId :: [quotient.left, quotient.right, mixed.left, mixed.right] for (division_operands) |id| try expect_kind(&result, id, .float) try testing.expect_equal(0, diagnostics.count(&diagnostic_store)) } rejects_fixed_family_conversion_integer_division_and_conflicting_demands test { strings := strpool.init(mem.c_allocator) defer strpool.deinit(&strings) diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) source :: `fixed int :: 1 `real float :: 2 `widen float :: fixed `narrow int :: 1.5 `cross :: fixed + real `division :: fixed / 2 `negated :: -"x" `strings :: "a" + "b" `first_int int :: conflict_a `first_float float :: conflict_a `conflict_a := 5 `reverse_float float :: conflict_b `reverse_int int :: conflict_b `conflict_b :: 6 `good :: 9 scan_state := lexer.init(mem.c_allocator) defer lexer.deinit(&scan_state) try lexer.scan(&scan_state, source, &strings, &diagnostic_store) tree := try parser.parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) defer ast.deinit(&tree) resolution := try sema.resolve(mem.c_allocator, &tree, &strings, &diagnostic_store) defer sema.deinit(&resolution) try testing.expect_equal(0, diagnostics.count(&diagnostic_store)) result := try check(mem.c_allocator, &tree, &resolution, &diagnostic_store) defer deinit(&result) try expect_kind(&result, ast.package_decl(&tree, 0), .int) try expect_kind(&result, ast.package_decl(&tree, 1), .float) codes [6]diagnostics.Code :: [ .type_mismatch, .type_mismatch, .invalid_operand, .integer_division, .invalid_operand, .invalid_operand, ] for codes |code, index| { declaration :: ast.package_decl(&tree, index + 2) expression :: ast.statement_decl(&tree, declaration).initializer diagnostic :: try expect_error(&result, &diagnostic_store, expression, code, ast.node_span(&tree, expression)) try testing.expect_equal(TypeKind.invalid, type_of(&result, declaration).kind) try testing.expect_equal(diagnostic, type_of(&result, declaration).diagnostic) } conflicts [2]ast.NodeId :: [ast.package_decl(&tree, 10), ast.package_decl(&tree, 13)] for conflicts |declaration| { try testing.expect_equal(TypeKind.invalid, type_of(&result, declaration).kind) diagnostic :: type_of(&result, declaration).diagnostic if (diagnostics.get(&diagnostic_store, diagnostic)) |error| try testing.expect_equal(diagnostics.Code.type_mismatch, error.code) else return .expectation_failed try testing.expect_equal(diagnostic, type_of(&result, ast.statement_decl(&tree, declaration).initializer).diagnostic) } conflict_uses [4]usize :: [8, 9, 11, 12] for conflict_uses |index| { declaration :: ast.package_decl(&tree, index) target :: if (index < 11) conflicts[0] else conflicts[1] try testing.expect_equal(TypeKind.invalid, type_of(&result, declaration).kind) try testing.expect_equal(type_of(&result, target).diagnostic, type_of(&result, declaration).diagnostic) } try expect_kind(&result, ast.package_decl(&tree, 14), .int) try testing.expect_equal(0, code_count(&diagnostic_store, .cannot_infer_type)) } checks_forward_alias_calls_grouped_parameters_and_mutual_recursion test { strings := strpool.init(mem.c_allocator) defer strpool.deinit(&strings) diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) source :: `forwarded :: alpha `alpha proc(a, b int) void { ` (forwarded)(a, b) ` beta(a + b, seed) `} `beta proc(total int, amount float) void { ` alpha(total, 2) `} `seed := 3 `main proc() void { ` beta(1, 2) ` (forwarded)(3, 4) `} scan_state := lexer.init(mem.c_allocator) defer lexer.deinit(&scan_state) try lexer.scan(&scan_state, source, &strings, &diagnostic_store) tree := try parser.parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) defer ast.deinit(&tree) resolution := try sema.resolve(mem.c_allocator, &tree, &strings, &diagnostic_store) defer sema.deinit(&resolution) result := try check(mem.c_allocator, &tree, &resolution, &diagnostic_store) defer deinit(&result) alias_decl :: ast.package_decl(&tree, 0) alpha :: ast.package_decl(&tree, 1) beta :: ast.package_decl(&tree, 2) seed :: ast.package_decl(&tree, 3) main_function :: ast.package_decl(&tree, 4) functions [3]ast.NodeId :: [alpha, beta, main_function] for functions |function| { try expect_kind(&result, function, .function) try testing.expect_equal(function, type_of(&result, function).function) try expect_kind(&result, ast.function_decl(&tree, function).return_type, .void) } try expect_kind(&result, alias_decl, .function) try testing.expect_equal(alpha, type_of(&result, alias_decl).function) try testing.expect_equal(alpha, type_of(&result, ast.statement_decl(&tree, alias_decl).initializer).function) alpha_body :: ast.function_decl(&tree, alpha).body beta_body :: ast.function_decl(&tree, beta).body main_body :: ast.function_decl(&tree, main_function).body calls [5]ast.NodeId :: [ ast.statement_expr(&tree, ast.block_statement(&tree, alpha_body, 0)), ast.statement_expr(&tree, ast.block_statement(&tree, alpha_body, 1)), ast.statement_expr(&tree, ast.block_statement(&tree, beta_body, 0)), ast.statement_expr(&tree, ast.block_statement(&tree, main_body, 0)), ast.statement_expr(&tree, ast.block_statement(&tree, main_body, 1)), ] callees [5]ast.NodeId :: [alpha, beta, alpha, beta, alpha] for calls |call, index| { try expect_kind(&result, call, .void) callee :: ast.call_expr(&tree, call).callee try expect_kind(&result, callee, .function) try testing.expect_equal(callees[index], type_of(&result, callee).function) try expect_kind(&result, ast.call_arg(&tree, call, 0), .int) expected TypeKind :: if (index == 1 or index == 3) .float else .int try expect_kind(&result, ast.call_arg(&tree, call, 1), expected) } try expect_kind(&result, seed, .float) try expect_kind(&result, ast.statement_decl(&tree, seed).initializer, .float) a :: ast.function_param(&tree, alpha, 0) b :: ast.function_param(&tree, alpha, 1) try testing.expect_equal(ast.param(&tree, a).type, ast.param(&tree, b).type) try expect_kind(&result, a, .int) try expect_kind(&result, b, .int) try expect_kind(&result, ast.param(&tree, a).type, .int) try expect_kind(&result, ast.function_param(&tree, beta, 1), .float) try testing.expect_equal(0, diagnostics.count(&diagnostic_store)) } separates_type_annotations_from_values_and_restricts_void_storage test { strings := strpool.init(mem.c_allocator) defer strpool.deinit(&strings) diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) source :: `value :: 1 `bad value :: -"x" `builtin :: int `grouped proc(a, b value) value {} `void_params proc(a, b void) void {} `storage void :: 1 `noop proc() void {} `sink proc(p int) void {} `holder :: noop() `operand :: noop() + 1 `main proc() void { ` noop() ` sink(noop()) `} `good string :: "ok" scan_state := lexer.init(mem.c_allocator) defer lexer.deinit(&scan_state) try lexer.scan(&scan_state, source, &strings, &diagnostic_store) tree := try parser.parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) defer ast.deinit(&tree) resolution := try sema.resolve(mem.c_allocator, &tree, &strings, &diagnostic_store) defer sema.deinit(&resolution) try testing.expect_equal(0, diagnostics.count(&diagnostic_store)) result := try check(mem.c_allocator, &tree, &resolution, &diagnostic_store) defer deinit(&result) bad :: ast.statement_decl(&tree, ast.package_decl(&tree, 1)) if (bad.type) |annotation| _ = try expect_error(&result, &diagnostic_store, annotation, .expected_type, ast.node_span(&tree, annotation)) else return .expectation_failed _ = try expect_error(&result, &diagnostic_store, bad.initializer, .invalid_operand, ast.node_span(&tree, bad.initializer)) builtin :: ast.statement_decl(&tree, ast.package_decl(&tree, 2)).initializer _ = try expect_error(&result, &diagnostic_store, builtin, .type_not_value, ast.node_span(&tree, builtin)) grouped :: ast.package_decl(&tree, 3) a :: ast.function_param(&tree, grouped, 0) b :: ast.function_param(&tree, grouped, 1) shared_type :: ast.param(&tree, a).type try testing.expect_equal(shared_type, ast.param(&tree, b).type) shared_error :: try expect_error(&result, &diagnostic_store, shared_type, .expected_type, ast.node_span(&tree, shared_type)) try testing.expect_equal(shared_error, type_of(&result, a).diagnostic) try testing.expect_equal(shared_error, type_of(&result, b).diagnostic) return_type :: ast.function_decl(&tree, grouped).return_type _ = try expect_error(&result, &diagnostic_store, return_type, .expected_type, ast.node_span(&tree, return_type)) void_function :: ast.package_decl(&tree, 4) void_a :: ast.function_param(&tree, void_function, 0) void_b :: ast.function_param(&tree, void_function, 1) void_annotation :: ast.param(&tree, void_a).type void_error :: try expect_error(&result, &diagnostic_store, void_annotation, .invalid_void_type, ast.node_span(&tree, void_annotation)) try testing.expect_equal(void_annotation, ast.param(&tree, void_b).type) try testing.expect_equal(void_error, type_of(&result, void_a).diagnostic) try testing.expect_equal(void_error, type_of(&result, void_b).diagnostic) try expect_kind(&result, ast.function_decl(&tree, void_function).return_type, .void) storage :: ast.statement_decl(&tree, ast.package_decl(&tree, 5)) if (storage.type) |annotation| _ = try expect_error(&result, &diagnostic_store, annotation, .invalid_void_type, ast.node_span(&tree, annotation)) else return .expectation_failed holder :: ast.statement_decl(&tree, ast.package_decl(&tree, 8)).initializer _ = try expect_error(&result, &diagnostic_store, holder, .type_mismatch, ast.node_span(&tree, holder)) operand :: ast.statement_decl(&tree, ast.package_decl(&tree, 9)).initializer _ = try expect_error(&result, &diagnostic_store, operand, .invalid_operand, ast.node_span(&tree, operand)) main_body :: ast.function_decl(&tree, ast.package_decl(&tree, 10)).body noop_call :: ast.statement_expr(&tree, ast.block_statement(&tree, main_body, 0)) try expect_kind(&result, noop_call, .void) bad_call :: ast.statement_expr(&tree, ast.block_statement(&tree, main_body, 1)) argument :: ast.call_arg(&tree, bad_call, 0) argument_error :: try expect_error(&result, &diagnostic_store, argument, .type_mismatch, ast.node_span(&tree, argument)) try testing.expect_equal(argument_error, type_of(&result, bad_call).diagnostic) rejected [5]usize :: [1, 2, 5, 8, 9] for rejected |index| { try testing.expect_equal(TypeKind.invalid, type_of(&result, ast.package_decl(&tree, index)).kind) } try expect_kind(&result, storage.initializer, .int) try expect_kind(&result, ast.package_decl(&tree, 0), .int) try expect_kind(&result, ast.package_decl(&tree, 11), .string) try testing.expect_equal(3, code_count(&diagnostic_store, .expected_type)) try testing.expect_equal(2, code_count(&diagnostic_store, .invalid_void_type)) try testing.expect_equal(1, code_count(&diagnostic_store, .type_not_value)) try testing.expect_equal(2, code_count(&diagnostic_store, .type_mismatch)) try testing.expect_equal(2, code_count(&diagnostic_store, .invalid_operand)) try testing.expect_equal(10, diagnostics.count(&diagnostic_store)) } reports_nonvoid_fallthrough_without_poisoning_signatures_and_checks_unused_bodies test { strings := strpool.init(mem.c_allocator) defer strpool.deinit(&strings) diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) source :: `answer :: forwarded() `forwarded :: make `make proc() int {} `unused proc() float {} `broken proc() void { ` -"x" `} `main proc() void { ` make() `} scan_state := lexer.init(mem.c_allocator) defer lexer.deinit(&scan_state) try lexer.scan(&scan_state, source, &strings, &diagnostic_store) tree := try parser.parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) defer ast.deinit(&tree) resolution := try sema.resolve(mem.c_allocator, &tree, &strings, &diagnostic_store) defer sema.deinit(&resolution) result := try check(mem.c_allocator, &tree, &resolution, &diagnostic_store) defer deinit(&result) answer :: ast.package_decl(&tree, 0) alias_decl :: ast.package_decl(&tree, 1) make :: ast.package_decl(&tree, 2) unused :: ast.package_decl(&tree, 3) try expect_kind(&result, answer, .int) try expect_kind(&result, ast.statement_decl(&tree, answer).initializer, .int) try expect_kind(&result, alias_decl, .function) try testing.expect_equal(make, type_of(&result, alias_decl).function) functions [2]ast.NodeId :: [make, unused] returns [2]TypeKind :: [.int, .float] for functions |function, index| { view :: ast.function_decl(&tree, function) try expect_kind(&result, function, .function) try testing.expect_equal(function, type_of(&result, function).function) try expect_kind(&result, view.return_type, returns[index]) _ = try expect_fallthrough(&result, &diagnostic_store, function, ast.node_span(&tree, view.body)) } broken :: ast.package_decl(&tree, 4) try expect_kind(&result, broken, .function) try testing.expect_equal(null, fallthrough_diagnostic(&result, broken)) broken_body :: ast.function_decl(&tree, broken).body bad_operand :: ast.statement_expr(&tree, ast.block_statement(&tree, broken_body, 0)) _ = try expect_error(&result, &diagnostic_store, bad_operand, .invalid_operand, ast.node_span(&tree, bad_operand)) main_body :: ast.function_decl(&tree, ast.package_decl(&tree, 5)).body try testing.expect_equal(null, fallthrough_diagnostic(&result, ast.package_decl(&tree, 5))) call :: ast.statement_expr(&tree, ast.block_statement(&tree, main_body, 0)) try expect_kind(&result, call, .int) try testing.expect_equal(2, code_count(&diagnostic_store, .missing_return)) try testing.expect_equal(3, diagnostics.count(&diagnostic_store)) } checks_arguments_even_for_wrong_arity_noncallable_and_chained_callees test { strings := strpool.init(mem.c_allocator) defer strpool.deinit(&strings) diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) source :: `sink proc(a int, b float) void {} `noop proc() void {} `fixed int :: 1 `main proc() void { ` sink(1, 2, -"extra") ` sink() ` fixed(-"arg") ` sink(1, "wrong") ` sink(1, fixed) ` noop()() ` sink(2, 3) `} scan_state := lexer.init(mem.c_allocator) defer lexer.deinit(&scan_state) try lexer.scan(&scan_state, source, &strings, &diagnostic_store) tree := try parser.parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) defer ast.deinit(&tree) resolution := try sema.resolve(mem.c_allocator, &tree, &strings, &diagnostic_store) defer sema.deinit(&resolution) result := try check(mem.c_allocator, &tree, &resolution, &diagnostic_store) defer deinit(&result) body :: ast.function_decl(&tree, ast.package_decl(&tree, 3)).body for usize(0)..usize(2) |index| { call :: ast.statement_expr(&tree, ast.block_statement(&tree, body, index)) _ = try expect_error(&result, &diagnostic_store, call, .argument_count_mismatch, ast.node_span(&tree, call)) } long_call :: ast.statement_expr(&tree, ast.block_statement(&tree, body, 0)) extra :: ast.call_arg(&tree, long_call, 2) _ = try expect_error(&result, &diagnostic_store, extra, .invalid_operand, ast.node_span(&tree, extra)) try expect_kind(&result, ast.call_arg(&tree, long_call, 0), .int) try expect_kind(&result, ast.call_arg(&tree, long_call, 1), .float) noncall :: ast.statement_expr(&tree, ast.block_statement(&tree, body, 2)) noncall_callee :: ast.call_expr(&tree, noncall).callee _ = try expect_error(&result, &diagnostic_store, noncall, .not_callable, ast.node_span(&tree, noncall_callee)) bad_argument :: ast.call_arg(&tree, noncall, 0) _ = try expect_error(&result, &diagnostic_store, bad_argument, .invalid_operand, ast.node_span(&tree, bad_argument)) for usize(3)..usize(5) |index| { call :: ast.statement_expr(&tree, ast.block_statement(&tree, body, index)) argument :: ast.call_arg(&tree, call, 1) diagnostic :: try expect_error(&result, &diagnostic_store, argument, .type_mismatch, ast.node_span(&tree, argument)) try testing.expect_equal(diagnostic, type_of(&result, call).diagnostic) } chained :: ast.statement_expr(&tree, ast.block_statement(&tree, body, 5)) inner :: ast.call_expr(&tree, chained).callee try expect_kind(&result, inner, .void) _ = try expect_error(&result, &diagnostic_store, chained, .not_callable, ast.node_span(&tree, inner)) good_call :: ast.statement_expr(&tree, ast.block_statement(&tree, body, 6)) try expect_kind(&result, good_call, .void) try expect_kind(&result, ast.call_arg(&tree, good_call, 1), .float) try expect_kind(&result, ast.package_decl(&tree, 2), .int) try testing.expect_equal(2, code_count(&diagnostic_store, .argument_count_mismatch)) try testing.expect_equal(2, code_count(&diagnostic_store, .not_callable)) try testing.expect_equal(2, code_count(&diagnostic_store, .invalid_operand)) try testing.expect_equal(2, code_count(&diagnostic_store, .type_mismatch)) try testing.expect_equal(8, diagnostics.count(&diagnostic_store)) } terminates_unanchored_cycles_and_keeps_grounded_cycles_typed test { strings := strpool.init(mem.c_allocator) defer strpool.deinit(&strings) diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) source :: `a :: b `b :: a `dependent :: a `self :: self `anchored float :: partner `partner :: anchored `fixed int :: fixed `good :: 2 `numeric :: numeric + 1 `demand float :: contextual_a `contextual_a :: contextual_b `contextual_b :: contextual_a scan_state := lexer.init(mem.c_allocator) defer lexer.deinit(&scan_state) try lexer.scan(&scan_state, source, &strings, &diagnostic_store) tree := try parser.parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) defer ast.deinit(&tree) resolution := try sema.resolve(mem.c_allocator, &tree, &strings, &diagnostic_store) defer sema.deinit(&resolution) try testing.expect_equal(0, diagnostics.count(&diagnostic_store)) result := try check(mem.c_allocator, &tree, &resolution, &diagnostic_store) defer deinit(&result) a :: ast.package_decl(&tree, 0) b :: ast.package_decl(&tree, 1) dependent :: ast.package_decl(&tree, 2) shared :: type_of(&result, a).diagnostic cycle [3]ast.NodeId :: [a, b, dependent] for cycle |declaration| { try testing.expect_equal(TypeKind.invalid, type_of(&result, declaration).kind) try testing.expect_equal(shared, type_of(&result, declaration).diagnostic) expression :: ast.statement_decl(&tree, declaration).initializer try testing.expect_equal(TypeKind.invalid, type_of(&result, expression).kind) try testing.expect_equal(shared, type_of(&result, expression).diagnostic) } if (diagnostics.get(&diagnostic_store, shared)) |diagnostic| { try testing.expect_equal(diagnostics.Code.cannot_infer_type, diagnostic.code) a_name :: ast.statement_decl(&tree, a).name b_name :: ast.statement_decl(&tree, b).name try testing.expect( (diagnostic.span.start == a_name.start and diagnostic.span.end == a_name.end) or (diagnostic.span.start == b_name.start and diagnostic.span.end == b_name.end), ) } else { return .expectation_failed } self :: ast.package_decl(&tree, 3) self_error :: try expect_error(&result, &diagnostic_store, self, .cannot_infer_type, ast.statement_decl(&tree, self).name) try testing.expect(self_error != shared) try testing.expect_equal(self_error, type_of(&result, ast.statement_decl(&tree, self).initializer).diagnostic) for usize(4)..usize(6) |index| { declaration :: ast.package_decl(&tree, index) try expect_kind(&result, declaration, .float) try expect_kind(&result, ast.statement_decl(&tree, declaration).initializer, .float) } try expect_kind(&result, ast.package_decl(&tree, 6), .int) try expect_kind(&result, ast.statement_decl(&tree, ast.package_decl(&tree, 6)).initializer, .int) try expect_kind(&result, ast.package_decl(&tree, 7), .int) numeric :: ast.package_decl(&tree, 8) try expect_kind(&result, numeric, .int) try expect_kind(&result, ast.statement_decl(&tree, numeric).initializer, .int) for usize(9)..usize(12) |index| { declaration :: ast.package_decl(&tree, index) try expect_kind(&result, declaration, .float) try expect_kind(&result, ast.statement_decl(&tree, declaration).initializer, .float) } try testing.expect_equal(2, diagnostics.count(&diagnostic_store)) } preserves_existing_syntax_name_and_duplicate_ids_while_checking_siblings test { strings := strpool.init(mem.c_allocator) defer strpool.deinit(&strings) diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) source :: `@ broken `main proc(a, b Missing) void { ` empty :: () ` bad :: absent + -"sibling" ` unknown(-"argument") ` valid float :: later ` later := 1 ` discarded(1 2) `} `same :: 1 `same :: -"initializer" scan_state := lexer.init(mem.c_allocator) defer lexer.deinit(&scan_state) try lexer.scan(&scan_state, source, &strings, &diagnostic_store) tree := try parser.parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) defer ast.deinit(&tree) resolution := try sema.resolve(mem.c_allocator, &tree, &strings, &diagnostic_store) defer sema.deinit(&resolution) previous_count :: diagnostics.count(&diagnostic_store) function :: ast.package_decl(&tree, 1) body :: ast.function_decl(&tree, function).body invalid_decl :: ast.package_decl(&tree, 0) empty_decl :: ast.block_statement(&tree, body, 0) empty :: ast.statement_decl(&tree, empty_decl).initializer invalid_statement :: ast.block_statement(&tree, body, 5) bad_decl :: ast.block_statement(&tree, body, 1) bad_expr :: ast.statement_decl(&tree, bad_decl).initializer bad :: ast.binary_expr(&tree, bad_expr) unknown_call :: ast.statement_expr(&tree, ast.block_statement(&tree, body, 2)) unknown_callee :: ast.call_expr(&tree, unknown_call).callee duplicate :: ast.package_decl(&tree, 3) a :: ast.function_param(&tree, function, 0) b :: ast.function_param(&tree, function, 1) shared_type :: ast.param(&tree, a).type try testing.expect_equal(shared_type, ast.param(&tree, b).type) result := try check(mem.c_allocator, &tree, &resolution, &diagnostic_store) defer deinit(&result) syntax_nodes [3]ast.NodeId :: [invalid_decl, empty, invalid_statement] for syntax_nodes |id| { try testing.expect_equal(TypeKind.invalid, type_of(&result, id).kind) try testing.expect_equal(ast.invalid(&tree, id).diagnostic, type_of(&result, id).diagnostic) } try testing.expect_equal(type_of(&result, empty).diagnostic, type_of(&result, empty_decl).diagnostic) resolved_invalid [4]ast.NodeId :: [shared_type, bad.left, unknown_callee, duplicate] for resolved_invalid |id| { try testing.expect_equal(sema.BindingKind.invalid, sema.binding(&resolution, id).kind) try testing.expect_equal(TypeKind.invalid, type_of(&result, id).kind) try testing.expect_equal(sema.binding(&resolution, id).data.diagnostic, type_of(&result, id).diagnostic) } try testing.expect_equal(type_of(&result, shared_type).diagnostic, type_of(&result, a).diagnostic) try testing.expect_equal(type_of(&result, shared_type).diagnostic, type_of(&result, b).diagnostic) try testing.expect_equal(type_of(&result, bad.left).diagnostic, type_of(&result, bad_expr).diagnostic) try testing.expect_equal(type_of(&result, bad.left).diagnostic, type_of(&result, bad_decl).diagnostic) try testing.expect_equal(type_of(&result, unknown_callee).diagnostic, type_of(&result, unknown_call).diagnostic) _ = try expect_error(&result, &diagnostic_store, bad.right, .invalid_operand, ast.node_span(&tree, bad.right)) argument :: ast.call_arg(&tree, unknown_call, 0) _ = try expect_error(&result, &diagnostic_store, argument, .invalid_operand, ast.node_span(&tree, argument)) duplicate_initializer :: ast.statement_decl(&tree, duplicate).initializer _ = try expect_error(&result, &diagnostic_store, duplicate_initializer, .invalid_operand, ast.node_span(&tree, duplicate_initializer)) for usize(3)..usize(5) |index| { declaration :: ast.block_statement(&tree, body, index) try expect_kind(&result, declaration, .float) try expect_kind(&result, ast.statement_decl(&tree, declaration).initializer, .float) } try expect_kind(&result, ast.package_decl(&tree, 2), .int) try testing.expect_equal(3, code_count(&diagnostic_store, .unknown_name)) try testing.expect_equal(1, code_count(&diagnostic_store, .duplicate_name)) try testing.expect_equal(3, code_count(&diagnostic_store, .invalid_operand)) try testing.expect_equal(previous_count + 3, diagnostics.count(&diagnostic_store)) } returns_supply_numeric_context_and_resolve_forward_local_and_parameter_names test { strings := strpool.init(mem.c_allocator) defer strpool.deinit(&strings) diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) source :: `fraction proc() float { ` return -later / 2 ` later := seed `} `identity proc(value int) int { return value } `wrapper proc() void { return noop() } `noop proc() void { return } `text proc() string { return "honey" } `seed :: 3 scan_state := lexer.init(mem.c_allocator) defer lexer.deinit(&scan_state) try lexer.scan(&scan_state, source, &strings, &diagnostic_store) tree := try parser.parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) defer ast.deinit(&tree) resolution := try sema.resolve(mem.c_allocator, &tree, &strings, &diagnostic_store) defer sema.deinit(&resolution) result := try check(mem.c_allocator, &tree, &resolution, &diagnostic_store) defer deinit(&result) expected [5]TypeKind :: [.float, .int, .void, .void, .string] for (expected) |kind, index| { function :: ast.package_decl(&tree, index) view :: ast.function_decl(&tree, function) try expect_kind(&result, function, .function) try expect_kind(&result, view.return_type, kind) statement :: ast.block_statement(&tree, view.body, 0) try expect_kind(&result, statement, .void) if (ast.return_stmt(&tree, statement).value) |value| try expect_kind(&result, value, kind) } fraction :: ast.function_decl(&tree, ast.package_decl(&tree, 0)) local :: ast.block_statement(&tree, fraction.body, 1) try expect_kind(&result, local, .float) try expect_kind(&result, ast.package_decl(&tree, 5), .float) if (ast.return_stmt(&tree, ast.block_statement(&tree, fraction.body, 0)).value) |value| { division :: ast.binary_expr(&tree, value) negate :: ast.unary_expr(&tree, division.left) try expect_kind(&result, division.left, .float) try expect_kind(&result, division.right, .float) try testing.expect_equal(local, sema.binding(&resolution, negate.operand).data.declaration) } else { return .expectation_failed } identity :: ast.package_decl(&tree, 1) identity_body :: ast.function_decl(&tree, identity).body if (ast.return_stmt(&tree, ast.block_statement(&tree, identity_body, 0)).value) |value| try testing.expect_equal(ast.function_param(&tree, identity, 0), sema.binding(&resolution, value).data.declaration) else return .expectation_failed try testing.expect_equal(0, diagnostics.count(&diagnostic_store)) } return_errors_preserve_identity_check_unreachable_code_and_suppress_missing_return_cascades test { strings := strpool.init(mem.c_allocator) defer strpool.deinit(&strings) diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) source :: `bare proc() int { return } `wrong proc() int { return "text" } `void_value proc() void { return 1 } `unknown proc() int { return absent } `syntax proc() int { return (1 } `after_return proc() int { ` return 7 ` -"bad" `} `fallthrough proc() int {} `recovered proc() int { ` return 1 stray ` valid :: 2 `} scan_state := lexer.init(mem.c_allocator) defer lexer.deinit(&scan_state) try lexer.scan(&scan_state, source, &strings, &diagnostic_store) tree := try parser.parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) defer ast.deinit(&tree) resolution := try sema.resolve(mem.c_allocator, &tree, &strings, &diagnostic_store) defer sema.deinit(&resolution) result := try check(mem.c_allocator, &tree, &resolution, &diagnostic_store) defer deinit(&result) for (usize(0)..usize(3)) |index| { function :: ast.package_decl(&tree, index) view :: ast.function_decl(&tree, function) try expect_kind(&result, function, .function) statement :: ast.block_statement(&tree, view.body, 0) returned :: ast.return_stmt(&tree, statement) span :: if (returned.value) |value| ast.node_span(&tree, value) else returned.span diagnostic :: try expect_error(&result, &diagnostic_store, statement, .return_type_mismatch, span) if (returned.value) |value| try testing.expect_equal(diagnostic, type_of(&result, value).diagnostic) } for (usize(3)..usize(5)) |index| { view :: ast.function_decl(&tree, ast.package_decl(&tree, index)) statement :: ast.block_statement(&tree, view.body, 0) if (ast.return_stmt(&tree, statement).value) |value| { original :: type_of(&result, value).diagnostic try testing.expect(original != diagnostics.no_diagnostic) try testing.expect_equal(original, type_of(&result, statement).diagnostic) try testing.expect_equal(original, type_of(&result, view.body).diagnostic) } else { return .expectation_failed } } unreachable_body :: ast.function_decl(&tree, ast.package_decl(&tree, 5)).body try expect_kind(&result, ast.block_statement(&tree, unreachable_body, 0), .void) bad :: ast.statement_expr(&tree, ast.block_statement(&tree, unreachable_body, 1)) _ = try expect_error(&result, &diagnostic_store, bad, .invalid_operand, ast.node_span(&tree, bad)) missing :: ast.package_decl(&tree, 6) missing_body :: ast.function_decl(&tree, missing).body _ = try expect_fallthrough(&result, &diagnostic_store, missing, ast.node_span(&tree, missing_body)) recovered_body :: ast.function_decl(&tree, ast.package_decl(&tree, 7)).body try expect_kind(&result, ast.block_statement(&tree, recovered_body, 1), .int) try testing.expect_equal(3, code_count(&diagnostic_store, .return_type_mismatch)) try testing.expect_equal(1, code_count(&diagnostic_store, .missing_return)) try testing.expect_equal(1, code_count(&diagnostic_store, .unknown_name)) try testing.expect_equal(2, code_count(&diagnostic_store, .unexpected_token)) try testing.expect_equal(8, diagnostics.count(&diagnostic_store)) } checks_bool_comparisons_recursive_returns_and_nested_numeric_context test { strings := strpool.init(mem.c_allocator) defer strpool.deinit(&strings) diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) source :: `flag bool :: true `same :: flag == false `different :: true != flag `less :: forwarded < 4.0 `less_equal :: seed <= 5 `greater :: 6 > seed `greater_equal :: seed >= 1 `equal :: seed == 3 `not_equal :: seed != 2 `forwarded :: seed `seed := 3 `identity proc(value bool) bool { return value } `factorial proc(n int) int { ` if n <= 1 { return 1 } ` return n * factorial(n - 1) `} `fraction proc(value bool) float { ` if value { ` before :: later ` later := 3 ` return -before / 2 ` } else if false { ` later :: 4 ` return later ` } else { ` return 5 ` } `} `main proc() bool { return identity(flag) } scan_state := lexer.init(mem.c_allocator) defer lexer.deinit(&scan_state) try lexer.scan(&scan_state, source, &strings, &diagnostic_store) tree := try parser.parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) defer ast.deinit(&tree) resolution := try sema.resolve(mem.c_allocator, &tree, &strings, &diagnostic_store) defer sema.deinit(&resolution) result := try check(mem.c_allocator, &tree, &resolution, &diagnostic_store) defer deinit(&result) for (0..usize(9)) |i| { declaration :: ast.package_decl(&tree, i) try expect_kind(&result, declaration, .bool) try expect_kind(&result, ast.statement_decl(&tree, declaration).initializer, .bool) if (i < 3) continue comparison_view :: ast.binary_expr(&tree, ast.statement_decl(&tree, declaration).initializer) try expect_kind(&result, comparison_view.left, .float) try expect_kind(&result, comparison_view.right, .float) } for (usize(9)..usize(11)) |i| try expect_kind(&result, ast.package_decl(&tree, i), .float) identity :: ast.package_decl(&tree, 11) try expect_kind(&result, ast.param(&tree, ast.function_param(&tree, identity, 0)).type, .bool) try expect_kind(&result, ast.function_decl(&tree, identity).return_type, .bool) factorial :: ast.package_decl(&tree, 12) factorial_view :: ast.function_decl(&tree, factorial) try expect_kind(&result, factorial, .function) conditional :: ast.if_stmt(&tree, ast.block_statement(&tree, factorial_view.body, 0)) try expect_kind(&result, conditional.condition, .bool) operands :: ast.binary_expr(&tree, conditional.condition) try expect_kind(&result, operands.left, .int) try expect_kind(&result, operands.right, .int) fraction :: ast.function_decl(&tree, ast.package_decl(&tree, 13)) arms :: ast.if_stmt(&tree, ast.block_statement(&tree, fraction.body, 0)) for (0..usize(2)) |i| { declaration :: ast.block_statement(&tree, arms.then_block, i) try expect_kind(&result, declaration, .float) try expect_kind(&result, ast.statement_decl(&tree, declaration).initializer, .float) } if (arms.else_block) |other| { nested :: ast.if_stmt(&tree, ast.block_statement(&tree, other, 0)) try expect_kind(&result, ast.block_statement(&tree, nested.then_block, 0), .float) } else { return .expectation_failed } try testing.expect_equal(0, diagnostics.count(&diagnostic_store)) } rejects_bool_numeric_mixing_and_preserves_invalid_condition_identity test { strings := strpool.init(mem.c_allocator) defer strpool.deinit(&strings) diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) source :: `sum :: true + false `order :: true < false `mixed :: true == 1 `strings :: "x" == "x" `wrong_bool bool :: 1 `wrong_int int :: false `main proc() void { ` if (1) { okay :: true } ` if "text" { okay :: false } ` if absent { okay :: true } ` if true + false { okay :: false } ` if true { return } else { -"bad" } `} scan_state := lexer.init(mem.c_allocator) defer lexer.deinit(&scan_state) try lexer.scan(&scan_state, source, &strings, &diagnostic_store) tree := try parser.parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) defer ast.deinit(&tree) resolution := try sema.resolve(mem.c_allocator, &tree, &strings, &diagnostic_store) defer sema.deinit(&resolution) result := try check(mem.c_allocator, &tree, &resolution, &diagnostic_store) defer deinit(&result) for (0..usize(4)) |i| { expression :: ast.statement_decl(&tree, ast.package_decl(&tree, i)).initializer _ = try expect_error(&result, &diagnostic_store, expression, .invalid_operand, ast.node_span(&tree, expression)) } for (usize(4)..usize(6)) |i| { expression :: ast.statement_decl(&tree, ast.package_decl(&tree, i)).initializer _ = try expect_error(&result, &diagnostic_store, expression, .type_mismatch, ast.node_span(&tree, expression)) } function :: ast.package_decl(&tree, 6) body :: ast.function_decl(&tree, function).body for (0..usize(2)) |i| { statement :: ast.block_statement(&tree, body, i) condition :: ast.if_stmt(&tree, statement).condition diagnostic :: try expect_error(&result, &diagnostic_store, condition, .condition_not_bool, ast.node_span(&tree, condition)) try testing.expect_equal(diagnostic, type_of(&result, statement).diagnostic) } unknown :: ast.if_stmt(&tree, ast.block_statement(&tree, body, 2)).condition original :: sema.binding(&resolution, unknown).data.diagnostic try testing.expect_equal(original, type_of(&result, unknown).diagnostic) failed :: ast.if_stmt(&tree, ast.block_statement(&tree, body, 3)).condition _ = try expect_error(&result, &diagnostic_store, failed, .invalid_operand, ast.node_span(&tree, failed)) final_if :: ast.block_statement(&tree, body, 4) try expect_kind(&result, final_if, .void) final_view :: ast.if_stmt(&tree, final_if) if (final_view.else_block) |other| { error_expr :: ast.statement_expr(&tree, ast.block_statement(&tree, other, 0)) _ = try expect_error(&result, &diagnostic_store, error_expr, .invalid_operand, ast.node_span(&tree, error_expr)) } else { return .expectation_failed } try expect_kind(&result, function, .function) try testing.expect_equal(2, code_count(&diagnostic_store, .condition_not_bool)) try testing.expect_equal(6, code_count(&diagnostic_store, .invalid_operand)) try testing.expect_equal(2, code_count(&diagnostic_store, .type_mismatch)) try testing.expect_equal(1, code_count(&diagnostic_store, .unknown_name)) try testing.expect_equal(11, diagnostics.count(&diagnostic_store)) } analyzes_return_paths_after_trap_propagation_without_poisoning_signatures test { strings := strpool.init(mem.c_allocator) defer strpool.deinit(&strings) diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) source :: `both proc(flag bool) int { ` if flag { return 1 } else { return 2 } `} `missing proc() int { if true { return 1 } } `bad_arm proc(flag bool) int { if flag { -"bad" } } `trap_and_return proc(flag bool) int { ` if flag { return 1 } else { bad int :: false } `} `invalid_condition proc() int { if 1 {} } `invalid_expression proc() int { -"bad" } `invalid_declaration proc() int { bad bool :: 1 } `syntax_trap proc() int { ` return 1 stray `} `after_return proc() int { ` return 1 ` -"bad" `} `nested proc(a bool, b bool) int { ` if a { if b { return 1 } else { return 2 } } else { return 3 } `} `join proc(flag bool) int { ` if flag {} else { return 1 } ` return 2 `} `answer :: bad_arm(false) `wrong_return proc(flag bool) int { ` if flag { return false } else { return 1 } `} scan_state := lexer.init(mem.c_allocator) defer lexer.deinit(&scan_state) try lexer.scan(&scan_state, source, &strings, &diagnostic_store) tree := try parser.parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) defer ast.deinit(&tree) resolution := try sema.resolve(mem.c_allocator, &tree, &strings, &diagnostic_store) defer sema.deinit(&resolution) result := try check(mem.c_allocator, &tree, &resolution, &diagnostic_store) defer deinit(&result) for (0..usize(11)) |i| try expect_kind(&result, ast.package_decl(&tree, i), .function) for (usize(1)..usize(3)) |i| { function :: ast.package_decl(&tree, i) body :: ast.function_decl(&tree, function).body _ = try expect_fallthrough(&result, &diagnostic_store, function, ast.node_span(&tree, body)) } bad_body :: ast.function_decl(&tree, ast.package_decl(&tree, 2)).body conditional :: ast.block_statement(&tree, bad_body, 0) try expect_kind(&result, conditional, .void) arm :: ast.if_stmt(&tree, conditional).then_block operand :: ast.statement_expr(&tree, ast.block_statement(&tree, arm, 0)) original :: try expect_error(&result, &diagnostic_store, operand, .invalid_operand, ast.node_span(&tree, operand)) try testing.expect_equal(original, type_of(&result, arm).diagnostic) try testing.expect(fallthrough_diagnostic(&result, ast.package_decl(&tree, 2))? != original) condition_body :: ast.function_decl(&tree, ast.package_decl(&tree, 4)).body condition :: ast.if_stmt(&tree, ast.block_statement(&tree, condition_body, 0)).condition condition_diag :: try expect_error(&result, &diagnostic_store, condition, .condition_not_bool, ast.node_span(&tree, condition)) try testing.expect_equal(condition_diag, type_of(&result, condition_body).diagnostic) recovered :: ast.function_decl(&tree, ast.package_decl(&tree, 7)).body syntax :: ast.block_statement(&tree, recovered, 0) try testing.expect_equal(ast.invalid(&tree, syntax).diagnostic, type_of(&result, recovered).diagnostic) unreachable_body :: ast.function_decl(&tree, ast.package_decl(&tree, 8)).body checked_after_return :: ast.statement_expr(&tree, ast.block_statement(&tree, unreachable_body, 1)) _ = try expect_error(&result, &diagnostic_store, checked_after_return, .invalid_operand, ast.node_span(&tree, checked_after_return)) try expect_kind(&result, ast.package_decl(&tree, 11), .int) wrong_function :: ast.package_decl(&tree, 12) try expect_kind(&result, wrong_function, .function) wrong_body :: ast.function_decl(&tree, wrong_function).body wrong_if :: ast.block_statement(&tree, wrong_body, 0) try expect_kind(&result, wrong_if, .void) wrong_arm :: ast.if_stmt(&tree, wrong_if).then_block wrong_statement :: ast.block_statement(&tree, wrong_arm, 0) if (ast.return_stmt(&tree, wrong_statement).value) |value| { mismatch :: try expect_error(&result, &diagnostic_store, value, .return_type_mismatch, ast.node_span(&tree, value)) try testing.expect_equal(mismatch, type_of(&result, wrong_statement).diagnostic) try testing.expect_equal(mismatch, type_of(&result, wrong_body).diagnostic) } else { return .expectation_failed } for 0..ast.package_decl_count(&tree) |i| { function :: ast.package_decl(&tree, i) if (i == 1 or i == 2 or ast.decl_kind(&tree, function) != .function) continue try testing.expect_equal(null, fallthrough_diagnostic(&result, function)) } try testing.expect_equal(1, code_count(&diagnostic_store, .return_type_mismatch)) try testing.expect_equal(2, code_count(&diagnostic_store, .missing_return)) try testing.expect_equal(1, code_count(&diagnostic_store, .condition_not_bool)) } assignments_preserve_established_types_and_contextualize_numeric_values test { strings := strpool.init(mem.c_allocator) defer strpool.deinit(&strings) diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) source :: `take_float proc(value float) int { return 1 } `main proc() void { ` number := 0 ` number = 1.5 ` number = number + 1.5 ` number = take_float(number) ` observed :: number ` quotient float := 0 ` quotient = 3 / 2 ` inferred := 0 ` context float :: inferred ` inferred = 5 / 2 ` rigid int := 1 ` quotient = rigid ` flag := true ` flag = false ` text := "before" ` text = "after" `} scan_state := lexer.init(mem.c_allocator) defer lexer.deinit(&scan_state) try lexer.scan(&scan_state, source, &strings, &diagnostic_store) tree := try parser.parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) defer ast.deinit(&tree) resolution := try sema.resolve(mem.c_allocator, &tree, &strings, &diagnostic_store) defer sema.deinit(&resolution) try testing.expect_equal(0, diagnostics.count(&diagnostic_store)) result := try check(mem.c_allocator, &tree, &resolution, &diagnostic_store) defer deinit(&result) body :: ast.function_decl(&tree, ast.package_decl(&tree, 1)).body number :: ast.block_statement(&tree, body, 0) observed :: ast.block_statement(&tree, body, 4) quotient :: ast.block_statement(&tree, body, 5) inferred :: ast.block_statement(&tree, body, 7) rigid :: ast.block_statement(&tree, body, 10) declarations [3]ast.NodeId :: [number, observed, rigid] for declarations |id| { try expect_kind(&result, id, .int) try expect_kind(&result, ast.statement_decl(&tree, id).initializer, .int) } try expect_kind(&result, quotient, .float) try expect_kind(&result, inferred, .float) bad_values [2]usize :: [1, 11] for bad_values |index| { statement :: ast.block_statement(&tree, body, index) assignment :: ast.assignment_stmt(&tree, statement) original :: try expect_error(&result, &diagnostic_store, assignment.value, .type_mismatch, ast.node_span(&tree, assignment.value)) try testing.expect_equal(original, type_of(&result, statement).diagnostic) expected :: if (index == 1) TypeKind.int else TypeKind.float try expect_kind(&result, assignment.target, expected) } arithmetic_statement :: ast.block_statement(&tree, body, 2) arithmetic :: ast.assignment_stmt(&tree, arithmetic_statement) original :: try expect_error(&result, &diagnostic_store, arithmetic.value, .invalid_operand, ast.node_span(&tree, arithmetic.value)) try testing.expect_equal(original, type_of(&result, arithmetic_statement).diagnostic) try expect_kind(&result, arithmetic.target, .int) try expect_kind(&result, ast.binary_expr(&tree, arithmetic.value).left, .int) call_statement :: ast.block_statement(&tree, body, 3) call :: ast.assignment_stmt(&tree, call_statement) argument :: ast.call_arg(&tree, call.value, 0) call_error :: try expect_error(&result, &diagnostic_store, argument, .type_mismatch, ast.node_span(&tree, argument)) try testing.expect_equal(call_error, type_of(&result, call_statement).diagnostic) try expect_kind(&result, call.target, .int) divisions [2]usize :: [6, 9] for divisions |index| { statement :: ast.block_statement(&tree, body, index) assignment :: ast.assignment_stmt(&tree, statement) division :: ast.binary_expr(&tree, assignment.value) try expect_kind(&result, statement, .void) try expect_kind(&result, assignment.target, .float) try expect_kind(&result, assignment.value, .float) try expect_kind(&result, division.left, .float) try expect_kind(&result, division.right, .float) } scalar_assignments [2]usize :: [13, 15] for scalar_assignments |index| { statement :: ast.block_statement(&tree, body, index) assignment :: ast.assignment_stmt(&tree, statement) expected :: if (index == 13) TypeKind.bool else TypeKind.string try expect_kind(&result, statement, .void) try expect_kind(&result, assignment.target, expected) try expect_kind(&result, assignment.value, expected) } try testing.expect_equal(3, code_count(&diagnostic_store, .type_mismatch)) try testing.expect_equal(1, code_count(&diagnostic_store, .invalid_operand)) try testing.expect_equal(0, code_count(&diagnostic_store, .integer_division)) } assignment_target_errors_preserve_bindings_and_independent_rhs_diagnostics test { strings := strpool.init(mem.c_allocator) defer strpool.deinit(&strings) diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) source :: `global := 1 `named proc() void {} `main proc(parameter int) void { ` constant :: 1 ` constant = 2 ` parameter = 2 ` global = 2 ` named = named ` int = 2 ` absent = other_missing ` constant = -false `} scan_state := lexer.init(mem.c_allocator) defer lexer.deinit(&scan_state) try lexer.scan(&scan_state, source, &strings, &diagnostic_store) tree := try parser.parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) defer ast.deinit(&tree) resolution := try sema.resolve(mem.c_allocator, &tree, &strings, &diagnostic_store) defer sema.deinit(&resolution) result := try check(mem.c_allocator, &tree, &resolution, &diagnostic_store) defer deinit(&result) function :: ast.package_decl(&tree, 2) body :: ast.function_decl(&tree, function).body for usize(1)..usize(6) |index| { statement :: ast.block_statement(&tree, body, index) assignment :: ast.assignment_stmt(&tree, statement) _ = try expect_error(&result, &diagnostic_store, statement, .invalid_assignment_target, ast.node_span(&tree, assignment.target)) expected :: if (index == 4) TypeKind.function else TypeKind.int try expect_kind(&result, assignment.value, expected) if (index != 5) try testing.expect(type_of(&result, assignment.target).kind != .invalid) } unknown_statement :: ast.block_statement(&tree, body, 6) unknown :: ast.assignment_stmt(&tree, unknown_statement) target_error :: sema.binding(&resolution, unknown.target).data.diagnostic value_error :: sema.binding(&resolution, unknown.value).data.diagnostic try testing.expect(target_error != value_error) try testing.expect_equal(target_error, type_of(&result, unknown.target).diagnostic) try testing.expect_equal(target_error, type_of(&result, unknown_statement).diagnostic) try testing.expect_equal(value_error, type_of(&result, unknown.value).diagnostic) independent_statement :: ast.block_statement(&tree, body, 7) independent :: ast.assignment_stmt(&tree, independent_statement) _ = try expect_error(&result, &diagnostic_store, independent.value, .invalid_operand, ast.node_span(&tree, independent.value)) _ = try expect_error(&result, &diagnostic_store, independent_statement, .invalid_assignment_target, ast.node_span(&tree, independent.target)) try expect_kind(&result, ast.block_statement(&tree, body, 0), .int) try expect_kind(&result, ast.function_param(&tree, function, 0), .int) try expect_kind(&result, ast.package_decl(&tree, 0), .int) try expect_kind(&result, ast.package_decl(&tree, 1), .function) try testing.expect_equal(6, code_count(&diagnostic_store, .invalid_assignment_target)) try testing.expect_equal(2, code_count(&diagnostic_store, .unknown_name)) try testing.expect_equal(0, code_count(&diagnostic_store, .type_not_value)) } function_assignment_checks_complete_signatures_without_retargeting_calls test { strings := strpool.init(mem.c_allocator) defer strpool.deinit(&strings) diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) source :: `first proc(a int) int { return a } `same proc(renamed int) int { return renamed + 1 } `wrong_parameter proc(a float) int { return 1 } `wrong_result proc(a int) bool { return true } `wrong_count proc() int { return 2 } `main proc() int { ` chosen := first ` chosen = same ` chosen = wrong_parameter ` chosen = wrong_result ` chosen = wrong_count ` answer :: chosen(7) ` return answer `} scan_state := lexer.init(mem.c_allocator) defer lexer.deinit(&scan_state) try lexer.scan(&scan_state, source, &strings, &diagnostic_store) tree := try parser.parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) defer ast.deinit(&tree) resolution := try sema.resolve(mem.c_allocator, &tree, &strings, &diagnostic_store) defer sema.deinit(&resolution) try testing.expect_equal(0, diagnostics.count(&diagnostic_store)) result := try check(mem.c_allocator, &tree, &resolution, &diagnostic_store) defer deinit(&result) first :: ast.package_decl(&tree, 0) body :: ast.function_decl(&tree, ast.package_decl(&tree, 5)).body chosen :: ast.block_statement(&tree, body, 0) try expect_kind(&result, chosen, .function) try testing.expect_equal(first, type_of(&result, chosen).function) try expect_kind(&result, ast.block_statement(&tree, body, 1), .void) for usize(2)..usize(5) |index| { statement :: ast.block_statement(&tree, body, index) assignment :: ast.assignment_stmt(&tree, statement) original :: try expect_error(&result, &diagnostic_store, assignment.value, .type_mismatch, ast.node_span(&tree, assignment.value)) try testing.expect_equal(original, type_of(&result, statement).diagnostic) try expect_kind(&result, assignment.target, .function) try testing.expect_equal(first, type_of(&result, assignment.target).function) } answer :: ast.block_statement(&tree, body, 5) call :: ast.statement_decl(&tree, answer).initializer try expect_kind(&result, answer, .int) try expect_kind(&result, call, .int) try testing.expect_equal(first, type_of(&result, ast.call_expr(&tree, call).callee).function) try testing.expect_equal(3, code_count(&diagnostic_store, .type_mismatch)) try testing.expect_equal(0, code_count(&diagnostic_store, .argument_count_mismatch)) } while_checks_nested_bodies_without_claiming_a_zero_iteration_return test { strings := strpool.init(mem.c_allocator) defer strpool.deinit(&strings) diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) source :: `terminal proc() int { while true { return 1 } } `bad_body proc() int { while false { -"bad" } } `continues proc() int { ` while false { -"bad" } ` return 7 `} `bad_condition proc() int { while 1 { -"bad" } } `unknown_condition proc() int { while absent { missing_body } } `nested proc() void { ` counter := 2 ` while counter > 0 { ` inner := 1 ` while inner > 0 { ` inner = inner - 1 ` counter = counter - 1 ` } ` } `} scan_state := lexer.init(mem.c_allocator) defer lexer.deinit(&scan_state) try lexer.scan(&scan_state, source, &strings, &diagnostic_store) tree := try parser.parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) defer ast.deinit(&tree) resolution := try sema.resolve(mem.c_allocator, &tree, &strings, &diagnostic_store) defer sema.deinit(&resolution) result := try check(mem.c_allocator, &tree, &resolution, &diagnostic_store) defer deinit(&result) terminal :: ast.package_decl(&tree, 0) try testing.expect(fallthrough_diagnostic(&result, terminal) == null) body :: ast.function_decl(&tree, ast.package_decl(&tree, 1)).body _ = try expect_fallthrough(&result, &diagnostic_store, ast.package_decl(&tree, 1), ast.node_span(&tree, body)) try expect_kind(&result, ast.block_statement(&tree, body, 0), .void) continues_body :: ast.function_decl(&tree, ast.package_decl(&tree, 2)).body loop_statement :: ast.block_statement(&tree, continues_body, 0) loop :: ast.while_stmt(&tree, loop_statement) invalid_body :: ast.statement_expr(&tree, ast.block_statement(&tree, loop.body, 0)) original :: try expect_error(&result, &diagnostic_store, invalid_body, .invalid_operand, ast.node_span(&tree, invalid_body)) try testing.expect_equal(original, type_of(&result, loop.body).diagnostic) try testing.expect_equal(original, type_of(&result, continues_body).diagnostic) try expect_kind(&result, loop_statement, .void) try expect_kind(&result, ast.block_statement(&tree, continues_body, 1), .void) bad_condition_body :: ast.function_decl(&tree, ast.package_decl(&tree, 3)).body bad_condition_statement :: ast.block_statement(&tree, bad_condition_body, 0) condition :: ast.while_stmt(&tree, bad_condition_statement).condition condition_error :: try expect_error(&result, &diagnostic_store, condition, .condition_not_bool, ast.node_span(&tree, condition)) try testing.expect_equal(condition_error, type_of(&result, bad_condition_statement).diagnostic) unknown_body :: ast.function_decl(&tree, ast.package_decl(&tree, 4)).body unknown_statement :: ast.block_statement(&tree, unknown_body, 0) unknown :: ast.while_stmt(&tree, unknown_statement).condition try testing.expect_equal(sema.binding(&resolution, unknown).data.diagnostic, type_of(&result, unknown_statement).diagnostic) nested_body :: ast.function_decl(&tree, ast.package_decl(&tree, 5)).body try expect_kind(&result, nested_body, .none) outer_loop :: ast.while_stmt(&tree, ast.block_statement(&tree, nested_body, 1)) inner_loop :: ast.while_stmt(&tree, ast.block_statement(&tree, outer_loop.body, 1)) for usize(0)..usize(2) |index| { statement :: ast.block_statement(&tree, inner_loop.body, index) assignment :: ast.assignment_stmt(&tree, statement) try expect_kind(&result, statement, .void) try expect_kind(&result, assignment.target, .int) try expect_kind(&result, assignment.value, .int) } try testing.expect_equal(1, code_count(&diagnostic_store, .missing_return)) try testing.expect_equal(1, code_count(&diagnostic_store, .condition_not_bool)) try testing.expect_equal(3, code_count(&diagnostic_store, .invalid_operand)) } logical_types_preserve_comparison_context_and_assignment_isolation test { strings := strpool.init(mem.c_allocator) defer strpool.deinit(&strings) diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) source :: `seed :: 3 `forward :: seed `composed :: !(forward < 4.0) and true or false `equal :: !(true and false) == (true or false) `nested :: !!!true `identity proc(value bool) bool { return value } `main proc() bool { ` count := 1 ` flag := true ` flag = !(count < 2) and identity(true) or false ` return flag `} scan_state := lexer.init(mem.c_allocator) defer lexer.deinit(&scan_state) try lexer.scan(&scan_state, source, &strings, &diagnostic_store) tree := try parser.parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) defer ast.deinit(&tree) resolution := try sema.resolve(mem.c_allocator, &tree, &strings, &diagnostic_store) defer sema.deinit(&resolution) result := try check(mem.c_allocator, &tree, &resolution, &diagnostic_store) defer deinit(&result) for (usize(0)..usize(2)) |index| try expect_kind(&result, ast.package_decl(&tree, index), .float) for usize(2)..usize(5) |index| { declaration :: ast.package_decl(&tree, index) try expect_kind(&result, declaration, .bool) try expect_kind(&result, ast.statement_decl(&tree, declaration).initializer, .bool) } composed :: ast.binary_expr(&tree, ast.statement_decl(&tree, ast.package_decl(&tree, 2)).initializer) try testing.expect_equal(ast.BinaryOperator.logical_or, composed.operator) conjunction :: ast.binary_expr(&tree, composed.left) try testing.expect_equal(ast.BinaryOperator.logical_and, conjunction.operator) inverted :: ast.unary_expr(&tree, conjunction.left) try expect_kind(&result, conjunction.left, .bool) comparison_view :: ast.binary_expr(&tree, inverted.operand) try expect_kind(&result, comparison_view.left, .float) try expect_kind(&result, comparison_view.right, .float) nested := ast.statement_decl(&tree, ast.package_decl(&tree, 4)).initializer for 0..3 |_| { try expect_kind(&result, nested, .bool) view :: ast.unary_expr(&tree, nested) try testing.expect_equal(ast.UnaryOperator.logical_not, view.operator) nested = view.operand } try expect_kind(&result, nested, .bool) body :: ast.function_decl(&tree, ast.package_decl(&tree, 6)).body count :: ast.block_statement(&tree, body, 0) try expect_kind(&result, count, .int) assignment :: ast.assignment_stmt(&tree, ast.block_statement(&tree, body, 2)) try expect_kind(&result, assignment.value, .bool) disjunction :: ast.binary_expr(&tree, assignment.value) assignment_conjunction :: ast.binary_expr(&tree, disjunction.left) assignment_not :: ast.unary_expr(&tree, assignment_conjunction.left) assignment_comparison :: ast.binary_expr(&tree, assignment_not.operand) try expect_kind(&result, assignment_comparison.left, .int) try expect_kind(&result, assignment_comparison.right, .int) try testing.expect_equal(0, diagnostics.count(&diagnostic_store)) } logical_operand_errors_are_independent_and_skipped_rhs_preserves_bool test { strings := strpool.init(mem.c_allocator) defer strpool.deinit(&strings) diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) source :: `number :: 1 `number_alias :: number `skipped_unknown :: false and absent `skipped_kind :: true or number `both_bad :: 1 and "wrong" `demanded_unknown :: missing or true `demanded_not :: !number_alias `nested :: !(false and absent_nested) == true `independent :: left_missing and 1 `identity proc(value bool) bool { return value } `skips proc() bool { ` local :: true or 1 ` value := false ` value = false and absent_assignment ` identity(true or absent_argument) ` if true or 1 { value = true } ` while false and 1 { value = false } ` return false and absent_return `} scan_state := lexer.init(mem.c_allocator) defer lexer.deinit(&scan_state) try lexer.scan(&scan_state, source, &strings, &diagnostic_store) tree := try parser.parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) defer ast.deinit(&tree) resolution := try sema.resolve(mem.c_allocator, &tree, &strings, &diagnostic_store) defer sema.deinit(&resolution) result := try check(mem.c_allocator, &tree, &resolution, &diagnostic_store) defer deinit(&result) for usize(0)..usize(2) |index| { declaration :: ast.package_decl(&tree, index) try expect_kind(&result, declaration, .int) try expect_kind(&result, ast.statement_decl(&tree, declaration).initializer, .int) } skipped_indices [3]usize :: [2, 3, 7] for skipped_indices |index| { declaration :: ast.package_decl(&tree, index) try expect_kind(&result, declaration, .bool) try expect_kind(&result, ast.statement_decl(&tree, declaration).initializer, .bool) } unknown :: ast.binary_expr(&tree, ast.statement_decl(&tree, ast.package_decl(&tree, 2)).initializer) try testing.expect_equal(sema.binding(&resolution, unknown.right).data.diagnostic, type_of(&result, unknown.right).diagnostic) _ = try expect_error(&result, &diagnostic_store, unknown.right, .unknown_name, ast.node_span(&tree, unknown.right)) wrong :: ast.binary_expr(&tree, ast.statement_decl(&tree, ast.package_decl(&tree, 3)).initializer) _ = try expect_error(&result, &diagnostic_store, wrong.right, .invalid_operand, ast.node_span(&tree, wrong.right)) both_declaration :: ast.package_decl(&tree, 4) both_id :: ast.statement_decl(&tree, both_declaration).initializer both :: ast.binary_expr(&tree, both_id) left_error :: try expect_error(&result, &diagnostic_store, both.left, .invalid_operand, ast.node_span(&tree, both.left)) right_error :: try expect_error(&result, &diagnostic_store, both.right, .invalid_operand, ast.node_span(&tree, both.right)) try testing.expect(left_error != right_error) try testing.expect_equal(left_error, type_of(&result, both_id).diagnostic) try testing.expect_equal(left_error, type_of(&result, both_declaration).diagnostic) demanded_id :: ast.statement_decl(&tree, ast.package_decl(&tree, 5)).initializer demanded :: ast.binary_expr(&tree, demanded_id) try testing.expect_equal(sema.binding(&resolution, demanded.left).data.diagnostic, type_of(&result, demanded_id).diagnostic) not_id :: ast.statement_decl(&tree, ast.package_decl(&tree, 6)).initializer not_view :: ast.unary_expr(&tree, not_id) not_error :: try expect_error(&result, &diagnostic_store, not_view.operand, .invalid_operand, ast.node_span(&tree, not_view.operand)) try testing.expect_equal(not_error, type_of(&result, not_id).diagnostic) independent_id :: ast.statement_decl(&tree, ast.package_decl(&tree, 8)).initializer independent :: ast.binary_expr(&tree, independent_id) try testing.expect_equal(sema.binding(&resolution, independent.left).data.diagnostic, type_of(&result, independent_id).diagnostic) _ = try expect_error(&result, &diagnostic_store, independent.right, .invalid_operand, ast.node_span(&tree, independent.right)) body :: ast.function_decl(&tree, ast.package_decl(&tree, 10)).body try expect_kind(&result, body, .none) try expect_kind(&result, ast.block_statement(&tree, body, 0), .bool) assignment_id :: ast.block_statement(&tree, body, 2) assignment :: ast.assignment_stmt(&tree, assignment_id) try expect_kind(&result, assignment_id, .void) try expect_kind(&result, assignment.value, .bool) call :: ast.statement_expr(&tree, ast.block_statement(&tree, body, 3)) try expect_kind(&result, call, .bool) try expect_kind(&result, ast.call_arg(&tree, call, 0), .bool) conditional :: ast.if_stmt(&tree, ast.block_statement(&tree, body, 4)) loop :: ast.while_stmt(&tree, ast.block_statement(&tree, body, 5)) try expect_kind(&result, conditional.condition, .bool) try expect_kind(&result, loop.condition, .bool) returned :: ast.block_statement(&tree, body, 6) try expect_kind(&result, returned, .void) if (ast.return_stmt(&tree, returned).value) |value| try expect_kind(&result, value, .bool) else return .expectation_failed try testing.expect_equal(7, code_count(&diagnostic_store, .unknown_name)) try testing.expect_equal(8, code_count(&diagnostic_store, .invalid_operand)) try testing.expect_equal(15, diagnostics.count(&diagnostic_store)) } logical_external_mismatches_and_always_demanded_not_preserve_errors test { strings := strpool.init(mem.c_allocator) defer strpool.deinit(&strings) diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) source :: `accept_bool proc(value bool) bool { return value } `accept_int proc(value int) bool { return true } `noop proc() void {} `wrong_decl int :: true or 1 `wrong_return proc() int { return false and absent_return } `wrong_argument proc() bool { return accept_int(false and absent_argument) } `wrong_assignment proc() void { ` count := 1 ` count = true or 1 `} `unary_bad proc() void { ` !1.0 ` !"text" ` !accept_bool ` !noop() `} scan_state := lexer.init(mem.c_allocator) defer lexer.deinit(&scan_state) try lexer.scan(&scan_state, source, &strings, &diagnostic_store) tree := try parser.parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) defer ast.deinit(&tree) resolution := try sema.resolve(mem.c_allocator, &tree, &strings, &diagnostic_store) defer sema.deinit(&resolution) result := try check(mem.c_allocator, &tree, &resolution, &diagnostic_store) defer deinit(&result) declaration :: ast.package_decl(&tree, 3) initializer :: ast.statement_decl(&tree, declaration).initializer declaration_error :: try expect_error(&result, &diagnostic_store, initializer, .type_mismatch, ast.node_span(&tree, initializer)) try testing.expect_equal(declaration_error, type_of(&result, declaration).diagnostic) return_body :: ast.function_decl(&tree, ast.package_decl(&tree, 4)).body return_id :: ast.block_statement(&tree, return_body, 0) if (ast.return_stmt(&tree, return_id).value) |value| { return_error :: try expect_error(&result, &diagnostic_store, value, .return_type_mismatch, ast.node_span(&tree, value)) try testing.expect_equal(return_error, type_of(&result, return_id).diagnostic) operand :: ast.binary_expr(&tree, value).right try testing.expect_equal(sema.binding(&resolution, operand).data.diagnostic, type_of(&result, operand).diagnostic) try testing.expect(return_error != type_of(&result, operand).diagnostic) } else { return .expectation_failed } argument_body :: ast.function_decl(&tree, ast.package_decl(&tree, 5)).body argument_return :: ast.block_statement(&tree, argument_body, 0) if (ast.return_stmt(&tree, argument_return).value) |call| { argument :: ast.call_arg(&tree, call, 0) argument_error :: try expect_error(&result, &diagnostic_store, argument, .type_mismatch, ast.node_span(&tree, argument)) try testing.expect_equal(argument_error, type_of(&result, call).diagnostic) try testing.expect_equal(argument_error, type_of(&result, argument_return).diagnostic) } else { return .expectation_failed } assignment_body :: ast.function_decl(&tree, ast.package_decl(&tree, 6)).body assignment_id :: ast.block_statement(&tree, assignment_body, 1) assignment :: ast.assignment_stmt(&tree, assignment_id) assignment_error :: try expect_error(&result, &diagnostic_store, assignment.value, .type_mismatch, ast.node_span(&tree, assignment.value)) try testing.expect_equal(assignment_error, type_of(&result, assignment_id).diagnostic) try expect_kind(&result, assignment.target, .int) try expect_kind(&result, ast.block_statement(&tree, assignment_body, 0), .int) unary_body :: ast.function_decl(&tree, ast.package_decl(&tree, 7)).body for usize(0)..usize(4) |index| { expression :: ast.statement_expr(&tree, ast.block_statement(&tree, unary_body, index)) operand :: ast.unary_expr(&tree, expression).operand original :: try expect_error(&result, &diagnostic_store, operand, .invalid_operand, ast.node_span(&tree, operand)) try testing.expect_equal(original, type_of(&result, expression).diagnostic) } for (usize(0)..usize(3)) |index| try expect_kind(&result, ast.package_decl(&tree, index), .function) try testing.expect_equal(6, code_count(&diagnostic_store, .invalid_operand)) try testing.expect_equal(2, code_count(&diagnostic_store, .unknown_name)) try testing.expect_equal(3, code_count(&diagnostic_store, .type_mismatch)) try testing.expect_equal(1, code_count(&diagnostic_store, .return_type_mismatch)) try testing.expect_equal(12, diagnostics.count(&diagnostic_store)) } short_circuit_skip_paths_do_not_guarantee_a_return test { strings := strpool.init(mem.c_allocator) defer strpool.deinit(&strings) diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) source :: `expression proc() int { false and missing_expression } `declaration proc() int { local :: true or 1 } `assignment proc() int { ` value := false ` value = false and missing_assignment `} `call proc() int { take(true or 1) } `conditional proc() int { if false and missing_condition { return 1 } } `loop proc() int { while true or missing_guard { return 1 } } `loop_body proc(flag bool) int { while flag { true or 1 } } `arms proc(flag bool) int { ` if flag { false and missing_arm } else { return 1 } `} `demanded proc() int { missing_left or true } `successful proc() bool { return true or missing_return } `take proc(value bool) void {} scan_state := lexer.init(mem.c_allocator) defer lexer.deinit(&scan_state) try lexer.scan(&scan_state, source, &strings, &diagnostic_store) tree := try parser.parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) defer ast.deinit(&tree) resolution := try sema.resolve(mem.c_allocator, &tree, &strings, &diagnostic_store) defer sema.deinit(&resolution) result := try check(mem.c_allocator, &tree, &resolution, &diagnostic_store) defer deinit(&result) for usize(0)..usize(8) |index| { function :: ast.package_decl(&tree, index) try expect_kind(&result, function, .function) body :: ast.function_decl(&tree, function).body _ = try expect_fallthrough(&result, &diagnostic_store, function, ast.node_span(&tree, body)) } expression_body :: ast.function_decl(&tree, ast.package_decl(&tree, 0)).body expression :: ast.statement_expr(&tree, ast.block_statement(&tree, expression_body, 0)) try expect_kind(&result, expression, .bool) loop_body :: ast.function_decl(&tree, ast.package_decl(&tree, 6)).body loop_id :: ast.block_statement(&tree, loop_body, 0) try expect_kind(&result, loop_id, .void) demanded_body :: ast.function_decl(&tree, ast.package_decl(&tree, 8)).body demanded :: ast.statement_expr(&tree, ast.block_statement(&tree, demanded_body, 0)) left :: ast.binary_expr(&tree, demanded).left original :: sema.binding(&resolution, left).data.diagnostic try testing.expect_equal(original, type_of(&result, demanded).diagnostic) try testing.expect_equal(original, type_of(&result, demanded_body).diagnostic) successful_body :: ast.function_decl(&tree, ast.package_decl(&tree, 9)).body try expect_kind(&result, successful_body, .none) try expect_kind(&result, ast.block_statement(&tree, successful_body, 0), .void) try testing.expect_equal(8, code_count(&diagnostic_store, .missing_return)) try testing.expect_equal(7, code_count(&diagnostic_store, .unknown_name)) try testing.expect_equal(3, code_count(&diagnostic_store, .invalid_operand)) try testing.expect_equal(18, diagnostics.count(&diagnostic_store)) } loop_controls_follow_lexical_bodies_and_check_unreachable_code test { strings := strpool.init(mem.c_allocator) defer strpool.deinit(&strings) diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) source :: `scopes proc(flag bool) void { ` while flag { ` if (flag) break else continue ` while flag { ` if (flag) continue else break ` } ` absent ` } ` break ` continue `} `caller proc() void { ` while true { ` callee() ` break ` } `} `callee proc() void { continue } `dead proc() void { ` return ` break ` continue `} `arms proc() void { ` if (false) break else continue `} scan_state := lexer.init(mem.c_allocator) defer lexer.deinit(&scan_state) try lexer.scan(&scan_state, source, &strings, &diagnostic_store) tree := try parser.parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) defer ast.deinit(&tree) resolution := try sema.resolve(mem.c_allocator, &tree, &strings, &diagnostic_store) defer sema.deinit(&resolution) result := try check(mem.c_allocator, &tree, &resolution, &diagnostic_store) defer deinit(&result) scopes_body :: ast.function_decl(&tree, ast.package_decl(&tree, 0)).body outer_statement :: ast.block_statement(&tree, scopes_body, 0) outer :: ast.while_stmt(&tree, outer_statement) outer_if :: ast.if_stmt(&tree, ast.block_statement(&tree, outer.body, 0)) inner_statement :: ast.block_statement(&tree, outer.body, 1) inner :: ast.while_stmt(&tree, inner_statement) inner_if :: ast.if_stmt(&tree, ast.block_statement(&tree, inner.body, 0)) caller_body :: ast.function_decl(&tree, ast.package_decl(&tree, 1)).body caller_loop :: ast.while_stmt(&tree, ast.block_statement(&tree, caller_body, 0)) valid [5]ast.NodeId :: [ ast.block_statement(&tree, outer_if.then_block, 0), ast.block_statement(&tree, outer_if.else_block?, 0), ast.block_statement(&tree, inner_if.then_block, 0), ast.block_statement(&tree, inner_if.else_block?, 0), ast.block_statement(&tree, caller_loop.body, 1), ] for (valid) |statement| try expect_kind(&result, statement, .void) try expect_kind(&result, outer_statement, .void) try expect_kind(&result, inner_statement, .void) unknown :: ast.statement_expr(&tree, ast.block_statement(&tree, outer.body, 2)) original :: sema.binding(&resolution, unknown).data.diagnostic try testing.expect_equal(original, type_of(&result, unknown).diagnostic) outside_break :: ast.block_statement(&tree, scopes_body, 1) outside_continue :: ast.block_statement(&tree, scopes_body, 2) _ = try expect_error(&result, &diagnostic_store, outside_break, .break_outside_loop, ast.node_span(&tree, outside_break)) _ = try expect_error(&result, &diagnostic_store, outside_continue, .continue_outside_loop, ast.node_span(&tree, outside_continue)) callee_body :: ast.function_decl(&tree, ast.package_decl(&tree, 2)).body callee_control :: ast.block_statement(&tree, callee_body, 0) _ = try expect_error(&result, &diagnostic_store, callee_control, .continue_outside_loop, ast.node_span(&tree, callee_control)) dead_body :: ast.function_decl(&tree, ast.package_decl(&tree, 3)).body dead_break :: ast.block_statement(&tree, dead_body, 1) dead_continue :: ast.block_statement(&tree, dead_body, 2) _ = try expect_error(&result, &diagnostic_store, dead_break, .break_outside_loop, ast.node_span(&tree, dead_break)) _ = try expect_error(&result, &diagnostic_store, dead_continue, .continue_outside_loop, ast.node_span(&tree, dead_continue)) arms_body :: ast.function_decl(&tree, ast.package_decl(&tree, 4)).body arms :: ast.if_stmt(&tree, ast.block_statement(&tree, arms_body, 0)) arm_break :: ast.block_statement(&tree, arms.then_block, 0) arm_continue :: ast.block_statement(&tree, arms.else_block?, 0) _ = try expect_error(&result, &diagnostic_store, arm_break, .break_outside_loop, ast.node_span(&tree, arm_break)) _ = try expect_error(&result, &diagnostic_store, arm_continue, .continue_outside_loop, ast.node_span(&tree, arm_continue)) for (usize(0)..usize(5)) |index| try expect_kind(&result, ast.package_decl(&tree, index), .function) try testing.expect_equal(3, code_count(&diagnostic_store, .break_outside_loop)) try testing.expect_equal(4, code_count(&diagnostic_store, .continue_outside_loop)) try testing.expect_equal(1, code_count(&diagnostic_store, .unknown_name)) try testing.expect_equal(8, diagnostics.count(&diagnostic_store)) } loop_controls_do_not_guarantee_function_returns test { strings := strpool.init(mem.c_allocator) defer strpool.deinit(&strings) diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) source :: `breaking proc() int { while true { break } } `continuing proc() int { while false { continue } } `mixed proc(flag bool) int { ` while flag { ` if (flag) return 42 else break ` } `} `closed proc(flag bool) int { ` while flag { ` if (flag) break else continue ` -"bad" ` } ` return 42 `} `invalid proc() int { break } scan_state := lexer.init(mem.c_allocator) defer lexer.deinit(&scan_state) try lexer.scan(&scan_state, source, &strings, &diagnostic_store) tree := try parser.parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) defer ast.deinit(&tree) resolution := try sema.resolve(mem.c_allocator, &tree, &strings, &diagnostic_store) defer sema.deinit(&resolution) result := try check(mem.c_allocator, &tree, &resolution, &diagnostic_store) defer deinit(&result) for (usize(0)..usize(3)) |index| { function :: ast.package_decl(&tree, index) body :: ast.function_decl(&tree, function).body _ = try expect_fallthrough(&result, &diagnostic_store, function, ast.node_span(&tree, body)) try expect_kind(&result, ast.block_statement(&tree, body, 0), .void) } closed_body :: ast.function_decl(&tree, ast.package_decl(&tree, 3)).body loop_statement :: ast.block_statement(&tree, closed_body, 0) loop :: ast.while_stmt(&tree, loop_statement) dead_expression :: ast.statement_expr(&tree, ast.block_statement(&tree, loop.body, 1)) original :: try expect_error(&result, &diagnostic_store, dead_expression, .invalid_operand, ast.node_span(&tree, dead_expression)) try testing.expect_equal(original, type_of(&result, closed_body).diagnostic) try expect_kind(&result, loop_statement, .void) try expect_kind(&result, ast.block_statement(&tree, closed_body, 1), .void) invalid_body :: ast.function_decl(&tree, ast.package_decl(&tree, 4)).body invalid_control :: ast.block_statement(&tree, invalid_body, 0) control_error :: try expect_error(&result, &diagnostic_store, invalid_control, .break_outside_loop, ast.node_span(&tree, invalid_control)) try testing.expect_equal(control_error, type_of(&result, invalid_body).diagnostic) for (usize(0)..usize(5)) |index| try expect_kind(&result, ast.package_decl(&tree, index), .function) try testing.expect_equal(3, code_count(&diagnostic_store, .missing_return)) try testing.expect_equal(1, code_count(&diagnostic_store, .invalid_operand)) try testing.expect_equal(1, code_count(&diagnostic_store, .break_outside_loop)) try testing.expect_equal(5, diagnostics.count(&diagnostic_store)) } range_bounds_receive_integer_context_without_joining_the_range_or_capture test { strings := strpool.init(mem.c_allocator) defer strpool.deinit(&strings) diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) source :: `limit :: seed `seed := 3 `endpoint proc() int { return 4 } `consume proc(value int) void {} `main proc() void { ` for (-2)..(limit + 1) |item| { ` consume(item) ` copy :: item ` while false { consume(copy) } ` } ` for (0..=endpoint()) |_| consume(1) ` for 0..2 |item| { consume(item) } `} scan_state := lexer.init(mem.c_allocator) defer lexer.deinit(&scan_state) try lexer.scan(&scan_state, source, &strings, &diagnostic_store) tree := try parser.parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) defer ast.deinit(&tree) resolution := try sema.resolve(mem.c_allocator, &tree, &strings, &diagnostic_store) defer sema.deinit(&resolution) result := try check(mem.c_allocator, &tree, &resolution, &diagnostic_store) defer deinit(&result) try expect_kind(&result, ast.package_decl(&tree, 0), .int) try expect_kind(&result, ast.package_decl(&tree, 1), .int) main_body :: ast.function_decl(&tree, ast.package_decl(&tree, 4)).body for (usize(0)..usize(3)) |index| { statement :: ast.block_statement(&tree, main_body, index) loop :: ast.for_stmt(&tree, statement) range_view :: ast.range_expr(&tree, loop.iterable) try expect_kind(&result, statement, .void) try expect_kind(&result, loop.iterable, .range) try expect_kind(&result, range_view.start, .int) try expect_kind(&result, range_view.end, .int) if (loop.capture) |capture| { try expect_kind(&result, capture, .int) try testing.expect_equal(capture, sema.binding(&resolution, capture).data.declaration) call :: ast.statement_expr(&tree, ast.block_statement(&tree, loop.body, 0)) item :: ast.call_arg(&tree, call, 0) try expect_kind(&result, item, .int) try testing.expect_equal(capture, sema.binding(&resolution, item).data.declaration) } } first :: ast.for_stmt(&tree, ast.block_statement(&tree, main_body, 0)) first_range :: ast.range_expr(&tree, first.iterable) try expect_kind(&result, ast.unary_expr(&tree, first_range.start).operand, .int) upper :: ast.binary_expr(&tree, first_range.end) try expect_kind(&result, upper.left, .int) try expect_kind(&result, upper.right, .int) try expect_kind(&result, ast.block_statement(&tree, first.body, 1), .int) try testing.expect_equal(0, diagnostics.count(&diagnostic_store)) } range_checks_both_bounds_and_preserves_fixed_declarations_and_source_errors test { strings := strpool.init(mem.c_allocator) defer strpool.deinit(&strings) diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) source :: `real float :: 2 `flag :: true `forwarded :: flag `nothing proc() void {} `main proc() void { ` for 1.5..2.5 |_| {} ` for forwarded.."text" |_| {} ` for nothing..nothing() |_| {} ` for real..3 |_| {} ` for absent_start..absent_end |_| {} ` for 0..1 |_| { sibling :: 7 } ` for flexible..floating_alias |_| {} `} `floating float :: floating_alias `floating_alias :: flexible `flexible := 3 scan_state := lexer.init(mem.c_allocator) defer lexer.deinit(&scan_state) try lexer.scan(&scan_state, source, &strings, &diagnostic_store) tree := try parser.parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) defer ast.deinit(&tree) resolution := try sema.resolve(mem.c_allocator, &tree, &strings, &diagnostic_store) defer sema.deinit(&resolution) result := try check(mem.c_allocator, &tree, &resolution, &diagnostic_store) defer deinit(&result) try expect_kind(&result, ast.package_decl(&tree, 0), .float) try expect_kind(&result, ast.package_decl(&tree, 1), .bool) try expect_kind(&result, ast.package_decl(&tree, 2), .bool) try expect_kind(&result, ast.package_decl(&tree, 3), .function) main_body :: ast.function_decl(&tree, ast.package_decl(&tree, 4)).body for (usize(0)..usize(3)) |index| { statement :: ast.block_statement(&tree, main_body, index) loop :: ast.for_stmt(&tree, statement) range_view :: ast.range_expr(&tree, loop.iterable) first_error :: try expect_error(&result, &diagnostic_store, range_view.start, .invalid_range_bound, ast.node_span(&tree, range_view.start)) second_error :: try expect_error(&result, &diagnostic_store, range_view.end, .invalid_range_bound, ast.node_span(&tree, range_view.end)) try testing.expect(first_error != second_error) try testing.expect_equal(type_of(&result, loop.iterable).diagnostic, type_of(&result, statement).diagnostic) } mixed :: ast.for_stmt(&tree, ast.block_statement(&tree, main_body, 3)) mixed_range :: ast.range_expr(&tree, mixed.iterable) _ = try expect_error(&result, &diagnostic_store, mixed_range.start, .invalid_range_bound, ast.node_span(&tree, mixed_range.start)) try expect_kind(&result, mixed_range.end, .int) unknown :: ast.for_stmt(&tree, ast.block_statement(&tree, main_body, 4)) unknown_range :: ast.range_expr(&tree, unknown.iterable) unknown_bounds [2]ast.NodeId :: [unknown_range.start, unknown_range.end] for unknown_bounds |bound| { original :: sema.binding(&resolution, bound).data.diagnostic _ = try expect_error(&result, &diagnostic_store, bound, .unknown_name, ast.node_span(&tree, bound)) try testing.expect_equal(original, type_of(&result, bound).diagnostic) } sibling :: ast.for_stmt(&tree, ast.block_statement(&tree, main_body, 5)) try expect_kind(&result, sibling.iterable, .range) try expect_kind(&result, ast.block_statement(&tree, sibling.body, 0), .int) floating_loop :: ast.for_stmt(&tree, ast.block_statement(&tree, main_body, 6)) floating_range :: ast.range_expr(&tree, floating_loop.iterable) floating_start_error :: try expect_error(&result, &diagnostic_store, floating_range.start, .invalid_range_bound, ast.node_span(&tree, floating_range.start)) floating_end_error :: try expect_error(&result, &diagnostic_store, floating_range.end, .invalid_range_bound, ast.node_span(&tree, floating_range.end)) try testing.expect(floating_start_error != floating_end_error) for (usize(5)..usize(8)) |index| try expect_kind(&result, ast.package_decl(&tree, index), .float) try testing.expect_equal(9, code_count(&diagnostic_store, .invalid_range_bound)) try testing.expect_equal(2, code_count(&diagnostic_store, .unknown_name)) try testing.expect_equal(0, code_count(&diagnostic_store, .invalid_for_iterable)) try testing.expect_equal(0, code_count(&diagnostic_store, .cannot_infer_type)) } range_values_nonrange_iterables_and_capture_modes_have_explicit_errors test { strings := strpool.init(mem.c_allocator) defer strpool.deinit(&strings) diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) source :: `stored :: 0..3 `produced proc() int { return 0..3 } `consume proc(value int) void {} `scalar proc() int { return 2 } `main proc() void { ` 0..3 ` consume(0..3) ` local :: 0..3 ` for 3 |_| { absent_scalar } ` for true |_| {} ` for 2.5 |_| {} ` for scalar() |_| {} ` for stored |_| {} ` for 0..3 |@item| { item } ` for 0..3 |item, index| { item + index } ` for 0..3 |_, _| {} ` for 0..3 |@item, _| {} `} scan_state := lexer.init(mem.c_allocator) defer lexer.deinit(&scan_state) try lexer.scan(&scan_state, source, &strings, &diagnostic_store) tree := try parser.parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) defer ast.deinit(&tree) resolution := try sema.resolve(mem.c_allocator, &tree, &strings, &diagnostic_store) defer sema.deinit(&resolution) result := try check(mem.c_allocator, &tree, &resolution, &diagnostic_store) defer deinit(&result) stored :: ast.package_decl(&tree, 0) stored_value :: ast.statement_decl(&tree, stored).initializer stored_error :: try expect_error(&result, &diagnostic_store, stored_value, .range_value_not_supported, ast.node_span(&tree, stored_value)) try testing.expect_equal(stored_error, type_of(&result, stored).diagnostic) produced_body :: ast.function_decl(&tree, ast.package_decl(&tree, 1)).body returned :: ast.return_stmt(&tree, ast.block_statement(&tree, produced_body, 0)).value? _ = try expect_error(&result, &diagnostic_store, returned, .range_value_not_supported, ast.node_span(&tree, returned)) main_body :: ast.function_decl(&tree, ast.package_decl(&tree, 4)).body standalone :: ast.statement_expr(&tree, ast.block_statement(&tree, main_body, 0)) call :: ast.statement_expr(&tree, ast.block_statement(&tree, main_body, 1)) local_value :: ast.statement_decl(&tree, ast.block_statement(&tree, main_body, 2)).initializer values [3]ast.NodeId :: [standalone, ast.call_arg(&tree, call, 0), local_value] for values |value| { _ = try expect_error(&result, &diagnostic_store, value, .range_value_not_supported, ast.node_span(&tree, value)) } for (usize(3)..usize(7)) |index| { statement :: ast.block_statement(&tree, main_body, index) loop :: ast.for_stmt(&tree, statement) original :: try expect_error(&result, &diagnostic_store, loop.iterable, .invalid_for_iterable, ast.node_span(&tree, loop.iterable)) try testing.expect_equal(original, type_of(&result, statement).diagnostic) } stored_loop :: ast.block_statement(&tree, main_body, 7) try testing.expect_equal(stored_error, type_of(&result, stored_loop).diagnostic) modes [4]diagnostics.Code :: [.range_pointer_capture, .range_index_capture, .range_index_capture, .range_pointer_capture] for modes |code, index| { statement :: ast.block_statement(&tree, main_body, index + 8) _ = try expect_error(&result, &diagnostic_store, statement, code, ast.node_span(&tree, statement)) } indexed :: ast.for_stmt(&tree, ast.block_statement(&tree, main_body, 9)) try expect_kind(&result, indexed.capture?, .int) try expect_kind(&result, indexed.index?, .int) try expect_kind(&result, ast.statement_expr(&tree, ast.block_statement(&tree, indexed.body, 0)), .int) try testing.expect_equal(5, code_count(&diagnostic_store, .range_value_not_supported)) try testing.expect_equal(4, code_count(&diagnostic_store, .invalid_for_iterable)) try testing.expect_equal(2, code_count(&diagnostic_store, .range_pointer_capture)) try testing.expect_equal(3, code_count(&diagnostic_store, .range_index_capture)) try testing.expect_equal(1, code_count(&diagnostic_store, .unknown_name)) try testing.expect_equal(0, code_count(&diagnostic_store, .return_type_mismatch)) try testing.expect_equal(0, code_count(&diagnostic_store, .type_mismatch)) } range_captures_are_fixed_readonly_and_assignment_values_do_not_retarget_bounds test { strings := strpool.init(mem.c_allocator) defer strpool.deinit(&strings) diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) source :: `consume proc(value float) void {} `returned proc() float { ` for 0..2 |item| { return item } ` return 1 `} `main proc() void { ` limit := 3 ` real := 1.5 ` for 0..limit |item| { ` item = 2 ` if true { item = 1.5 } ` while false { item = -"bad" } ` real = item ` real = limit ` consume(item) ` copy :: item ` consume(copy) ` } `} scan_state := lexer.init(mem.c_allocator) defer lexer.deinit(&scan_state) try lexer.scan(&scan_state, source, &strings, &diagnostic_store) tree := try parser.parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) defer ast.deinit(&tree) resolution := try sema.resolve(mem.c_allocator, &tree, &strings, &diagnostic_store) defer sema.deinit(&resolution) result := try check(mem.c_allocator, &tree, &resolution, &diagnostic_store) defer deinit(&result) returned_body :: ast.function_decl(&tree, ast.package_decl(&tree, 1)).body returned_loop :: ast.for_stmt(&tree, ast.block_statement(&tree, returned_body, 0)) returned_value :: ast.return_stmt(&tree, ast.block_statement(&tree, returned_loop.body, 0)).value? _ = try expect_error(&result, &diagnostic_store, returned_value, .return_type_mismatch, ast.node_span(&tree, returned_value)) try expect_kind(&result, returned_loop.capture?, .int) main_body :: ast.function_decl(&tree, ast.package_decl(&tree, 2)).body limit :: ast.block_statement(&tree, main_body, 0) real :: ast.block_statement(&tree, main_body, 1) loop_statement :: ast.block_statement(&tree, main_body, 2) loop :: ast.for_stmt(&tree, loop_statement) try expect_kind(&result, limit, .int) try expect_kind(&result, real, .float) try expect_kind(&result, loop_statement, .void) try expect_kind(&result, loop.iterable, .range) try expect_kind(&result, ast.range_expr(&tree, loop.iterable).end, .int) try expect_kind(&result, loop.capture?, .int) conditional :: ast.if_stmt(&tree, ast.block_statement(&tree, loop.body, 1)) inner :: ast.while_stmt(&tree, ast.block_statement(&tree, loop.body, 2)) readonly [3]ast.NodeId :: [ ast.block_statement(&tree, loop.body, 0), ast.block_statement(&tree, conditional.then_block, 0), ast.block_statement(&tree, inner.body, 0), ] for readonly |statement| { assignment :: ast.assignment_stmt(&tree, statement) _ = try expect_error(&result, &diagnostic_store, statement, .invalid_assignment_target, ast.node_span(&tree, assignment.target)) try expect_kind(&result, assignment.target, .int) } dead_value :: ast.assignment_stmt(&tree, readonly[2]).value _ = try expect_error(&result, &diagnostic_store, dead_value, .invalid_operand, ast.node_span(&tree, dead_value)) for (usize(3)..usize(5)) |index| { assignment :: ast.assignment_stmt(&tree, ast.block_statement(&tree, loop.body, index)) _ = try expect_error(&result, &diagnostic_store, assignment.value, .type_mismatch, ast.node_span(&tree, assignment.value)) try expect_kind(&result, assignment.target, .float) } for (usize(5)..usize(8)) |index| { if (index == 6) continue call :: ast.statement_expr(&tree, ast.block_statement(&tree, loop.body, index)) argument :: ast.call_arg(&tree, call, 0) _ = try expect_error(&result, &diagnostic_store, argument, .type_mismatch, ast.node_span(&tree, argument)) } try expect_kind(&result, ast.block_statement(&tree, loop.body, 6), .int) try testing.expect_equal(3, code_count(&diagnostic_store, .invalid_assignment_target)) try testing.expect_equal(4, code_count(&diagnostic_store, .type_mismatch)) try testing.expect_equal(1, code_count(&diagnostic_store, .return_type_mismatch)) try testing.expect_equal(1, code_count(&diagnostic_store, .invalid_operand)) try testing.expect_equal(0, code_count(&diagnostic_store, .invalid_range_bound)) try testing.expect_equal(0, code_count(&diagnostic_store, .missing_return)) } range_loop_return_flow_and_controls_are_lexical_and_body_errors_are_isolated test { strings := strpool.init(mem.c_allocator) defer strpool.deinit(&strings) diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) source :: `missing proc() int { for 0..=0 |_| { return 1 } } `bad_body proc() int { for 2..1 |_| { -"bad" } } `breaking proc() int { for 0..2 |_| { break } } `continuing proc() int { for 2..1 |_| { continue } } `closed proc() int { ` for 2..1 |_| { -"bad" } ` return 7 `} `scopes proc(flag bool) void { ` for 0..2 |item| { ` if (flag) break else continue ` while flag { ` if (flag) continue else break ` for 0..item |_| { continue } ` } ` absent ` } ` while flag { for 0..2 |_| { break } } ` break ` continue ` item `} `callee proc() void { ` break ` continue `} `caller proc() void { ` for 0..2 |_| { ` callee() ` break ` } `} scan_state := lexer.init(mem.c_allocator) defer lexer.deinit(&scan_state) try lexer.scan(&scan_state, source, &strings, &diagnostic_store) tree := try parser.parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) defer ast.deinit(&tree) resolution := try sema.resolve(mem.c_allocator, &tree, &strings, &diagnostic_store) defer sema.deinit(&resolution) result := try check(mem.c_allocator, &tree, &resolution, &diagnostic_store) defer deinit(&result) for (usize(0)..usize(4)) |index| { function :: ast.package_decl(&tree, index) body :: ast.function_decl(&tree, function).body try expect_kind(&result, function, .function) _ = try expect_fallthrough(&result, &diagnostic_store, function, ast.node_span(&tree, body)) try expect_kind(&result, ast.block_statement(&tree, body, 0), .void) } closed_body :: ast.function_decl(&tree, ast.package_decl(&tree, 4)).body closed_statement :: ast.block_statement(&tree, closed_body, 0) closed_loop :: ast.for_stmt(&tree, closed_statement) dead_value :: ast.statement_expr(&tree, ast.block_statement(&tree, closed_loop.body, 0)) original :: try expect_error(&result, &diagnostic_store, dead_value, .invalid_operand, ast.node_span(&tree, dead_value)) try testing.expect_equal(original, type_of(&result, closed_body).diagnostic) try expect_kind(&result, closed_statement, .void) try expect_kind(&result, ast.block_statement(&tree, closed_body, 1), .void) scopes_body :: ast.function_decl(&tree, ast.package_decl(&tree, 5)).body outer_statement :: ast.block_statement(&tree, scopes_body, 0) outer :: ast.for_stmt(&tree, outer_statement) conditional :: ast.if_stmt(&tree, ast.block_statement(&tree, outer.body, 0)) inner_statement :: ast.block_statement(&tree, outer.body, 1) inner :: ast.while_stmt(&tree, inner_statement) inner_if :: ast.if_stmt(&tree, ast.block_statement(&tree, inner.body, 0)) nested_statement :: ast.block_statement(&tree, inner.body, 1) nested :: ast.for_stmt(&tree, nested_statement) controls [5]ast.NodeId :: [ ast.block_statement(&tree, conditional.then_block, 0), ast.block_statement(&tree, conditional.else_block?, 0), ast.block_statement(&tree, inner_if.then_block, 0), ast.block_statement(&tree, inner_if.else_block?, 0), ast.block_statement(&tree, nested.body, 0), ] for (controls) |statement| try expect_kind(&result, statement, .void) try expect_kind(&result, outer_statement, .void) try expect_kind(&result, inner_statement, .void) try expect_kind(&result, nested_statement, .void) absent :: ast.statement_expr(&tree, ast.block_statement(&tree, outer.body, 2)) try testing.expect_equal(sema.binding(&resolution, absent).data.diagnostic, type_of(&result, absent).diagnostic) escaped :: ast.statement_expr(&tree, ast.block_statement(&tree, scopes_body, 4)) try testing.expect_equal(sema.binding(&resolution, escaped).data.diagnostic, type_of(&result, escaped).diagnostic) try testing.expect_equal(4, code_count(&diagnostic_store, .missing_return)) try testing.expect_equal(2, code_count(&diagnostic_store, .invalid_operand)) try testing.expect_equal(2, code_count(&diagnostic_store, .unknown_name)) try testing.expect_equal(2, code_count(&diagnostic_store, .break_outside_loop)) try testing.expect_equal(2, code_count(&diagnostic_store, .continue_outside_loop)) } invalid_range_capture_bindings_keep_resolver_diagnostic_ids_without_erasing_headers test { strings := strpool.init(mem.c_allocator) defer strpool.deinit(&strings) diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) source :: `main proc() void { ` existing :: 7 ` for 0..1 |existing| { sibling :: 1 } ` for 0..1 |int| { sibling :: 2 } ` for 0..1 |item| { item :: 3 } ` for 0..1 |item| { copy :: item } `} scan_state := lexer.init(mem.c_allocator) defer lexer.deinit(&scan_state) try lexer.scan(&scan_state, source, &strings, &diagnostic_store) tree := try parser.parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) defer ast.deinit(&tree) resolution := try sema.resolve(mem.c_allocator, &tree, &strings, &diagnostic_store) defer sema.deinit(&resolution) previous_count :: diagnostics.count(&diagnostic_store) result := try check(mem.c_allocator, &tree, &resolution, &diagnostic_store) defer deinit(&result) function :: ast.package_decl(&tree, 0) body :: ast.function_decl(&tree, function).body try expect_kind(&result, function, .function) try expect_kind(&result, ast.block_statement(&tree, body, 0), .int) for (usize(1)..usize(3)) |index| { statement :: ast.block_statement(&tree, body, index) loop :: ast.for_stmt(&tree, statement) capture :: loop.capture? binding :: sema.binding(&resolution, capture) try testing.expect_equal(sema.BindingKind.invalid, binding.kind) try testing.expect_equal(TypeKind.invalid, type_of(&result, capture).kind) try testing.expect_equal(binding.data.diagnostic, type_of(&result, capture).diagnostic) try testing.expect_equal(binding.data.diagnostic, type_of(&result, loop.body).diagnostic) try expect_kind(&result, statement, .void) try expect_kind(&result, loop.iterable, .range) try expect_kind(&result, ast.block_statement(&tree, loop.body, 0), .int) } duplicate_loop :: ast.for_stmt(&tree, ast.block_statement(&tree, body, 3)) duplicate :: ast.block_statement(&tree, duplicate_loop.body, 0) try expect_kind(&result, duplicate_loop.capture?, .int) try testing.expect_equal(sema.binding(&resolution, duplicate).data.diagnostic, type_of(&result, duplicate).diagnostic) sibling_loop :: ast.for_stmt(&tree, ast.block_statement(&tree, body, 4)) try expect_kind(&result, sibling_loop.capture?, .int) try expect_kind(&result, ast.block_statement(&tree, sibling_loop.body, 0), .int) try testing.expect_equal(previous_count, diagnostics.count(&diagnostic_store)) } @hide:file ArrayFixture :: struct { strings strpool.StringPool tree ast.Tree resolution sema.Resolution diagnostics diagnostics.Store result Result } @hide:file array_fixture proc(source []u8) ArrayFixture ! (mem.AllocError | strpool.InternError | diagnostics.CapacityError | lexer.SourceError) { strings := strpool.init(mem.c_allocator) errdefer strpool.deinit(&strings) store := diagnostics.init(mem.c_allocator) errdefer diagnostics.deinit(&store) scan := lexer.init(mem.c_allocator) defer lexer.deinit(&scan) try lexer.scan(&scan, source, &strings, &store) tree := try parser.parse(mem.c_allocator, scan.tokens.items, source, &store) errdefer ast.deinit(&tree) resolution := try sema.resolve(mem.c_allocator, &tree, &strings, &store) errdefer sema.deinit(&resolution) result :: try check(mem.c_allocator, &tree, &resolution, &store) return ArrayFixture{ strings = strings, tree = tree, resolution = resolution, diagnostics = store, result = result } } @hide:file deinit_array_fixture proc(fixture @mut ArrayFixture) void { deinit(&fixture.result) sema.deinit(&fixture.resolution) ast.deinit(&fixture.tree) diagnostics.deinit(&fixture.diagnostics) strpool.deinit(&fixture.strings) } @hide:file expect_array proc(result @Result, id ast.NodeId, len usize, mutable bool, element TypeKind) ArrayType ! testing.Error { try expect_kind(result, id, .array) actual :: array_type(result, type_of(result, id).array_type) try testing.expect_equal(len, actual.len) try testing.expect_equal(mutable, actual.mutable) try testing.expect_equal(element, actual.element.kind) return actual } fixed_arrays_contextualize_forward_aliases_counts_nested_literals_and_signatures test { source :: `context [2]f64 :: forwarded `forwarded :: seed `seed := [1, 2] `count :: later * 2 - 1 `later :: 2 `nested [_]mut [2]mut i64 := [[1, 2], [3, 4]] `empty [_]bool :: [] `plain :: [] `copy :: nested `index usize :: 0 `child float :: seed[index] `produce proc(value [2]f64) [2]f64 { return value } `called :: produce([5, 6]) `from_result [_]f64 :: called `counted [count]i64 :: [1, 2, 3] fixture := try array_fixture(source) defer deinit_array_fixture(&fixture) tree :: &fixture.tree result :: &fixture.result for (0..usize(3)) |i| _ = try expect_array(result, ast.package_decl(tree, i), 2, false, .float) seed :: ast.statement_decl(tree, ast.package_decl(tree, 2)).initializer for (0..ast.array_element_count(tree, seed)) |i| try expect_kind(result, ast.array_element(tree, seed, i), .float) nested :: ast.package_decl(tree, 5) shape :: try expect_array(result, nested, 2, true, .array) row :: array_type(result, shape.element.array_type) try testing.expect_equal(2, row.len) try testing.expect(row.mutable) try testing.expect_equal(TypeKind.int, row.element.kind) annotation :: ast.statement_decl(tree, nested).type? _ = try expect_array(result, annotation, 2, true, .array) _ = try expect_array(result, ast.package_decl(tree, 6), 0, false, .bool) _ = try expect_array(result, ast.package_decl(tree, 7), 0, false, .int) try testing.expect_equal(type_of(result, nested).array_type, type_of(result, ast.package_decl(tree, 8)).array_type) indexed :: ast.index_expr(tree, ast.statement_decl(tree, ast.package_decl(tree, 10)).initializer) try expect_kind(result, indexed.index, .usize) try expect_kind(result, ast.statement_decl(tree, ast.package_decl(tree, 10)).initializer, .float) function :: ast.package_decl(tree, 11) _ = try expect_array(result, ast.function_param(tree, function, 0), 2, false, .float) _ = try expect_array(result, ast.function_decl(tree, function).return_type, 2, false, .float) call :: ast.statement_decl(tree, ast.package_decl(tree, 12)).initializer argument :: ast.call_arg(tree, call, 0) _ = try expect_array(result, argument, 2, false, .float) try expect_kind(result, ast.array_element(tree, argument, 0), .float) _ = try expect_array(result, ast.package_decl(tree, 13), 2, false, .float) _ = try expect_array(result, ast.package_decl(tree, 14), 3, false, .int) try testing.expect(array_type_count(result) > 0) try testing.expect_equal(0, diagnostics.count(&fixture.diagnostics)) } nested_array_numeric_context_flows_through_index_children_before_defaults test { source :: `first float :: later[0][0] `later := [[1, 2], [3, 4]] `row :: later[0] `mixed :: [[1, 2], [3.5, 4]] `identity proc(value int) int { return value } `another proc(value int) int { return value + 1 } `functions :: [identity, another] fixture := try array_fixture(source) defer deinit_array_fixture(&fixture) tree :: &fixture.tree result :: &fixture.result for usize(1)..usize(4) |i| { element_kind TypeKind :: if (i == 2) .float else .array type :: try expect_array(result, ast.package_decl(tree, i), 2, false, element_kind) if (type.element.kind == .array) try testing.expect_equal(TypeKind.float, array_type(result, type.element.array_type).element.kind) } matrix :: ast.statement_decl(tree, ast.package_decl(tree, 1)).initializer for 0..ast.array_element_count(tree, matrix) |i| { row :: ast.array_element(tree, matrix, i) for (0..ast.array_element_count(tree, row)) |j| try expect_kind(result, ast.array_element(tree, row, j), .float) } outer :: ast.statement_decl(tree, ast.package_decl(tree, 0)).initializer inner :: ast.index_expr(tree, outer).base _ = try expect_array(result, inner, 2, false, .float) try expect_kind(result, outer, .float) try expect_kind(result, ast.index_expr(tree, outer).index, .usize) try expect_kind(result, ast.index_expr(tree, inner).index, .usize) _ = try expect_array(result, ast.package_decl(tree, 6), 2, false, .function) try testing.expect_equal(0, diagnostics.count(&fixture.diagnostics)) } fixed_index_errors_are_occurrence_local_and_preserve_sources_and_siblings test { source :: `fixed i64 :: 0 `inferred := 0 `source [2]i64 :: [10, 20] `bad :: source[fixed] `good :: source[inferred] `boolean :: source[true] `missing :: source[unknown] `tail :: source[1] fixture := try array_fixture(source) defer deinit_array_fixture(&fixture) tree :: &fixture.tree result :: &fixture.result try expect_kind(result, ast.package_decl(tree, 0), .int) try expect_kind(result, ast.package_decl(tree, 1), .usize) _ = try expect_array(result, ast.package_decl(tree, 2), 2, false, .int) bad_id :: ast.statement_decl(tree, ast.package_decl(tree, 3)).initializer bad :: ast.index_expr(tree, bad_id) original :: try expect_error(result, &fixture.diagnostics, bad.index, .invalid_index_type, ast.node_span(tree, bad.index)) try testing.expect_equal(original, type_of(result, bad_id).diagnostic) try testing.expect_equal(original, type_of(result, ast.package_decl(tree, 3)).diagnostic) _ = try expect_array(result, bad.base, 2, false, .int) try expect_kind(result, ast.package_decl(tree, 4), .int) boolean :: ast.index_expr(tree, ast.statement_decl(tree, ast.package_decl(tree, 5)).initializer).index _ = try expect_error(result, &fixture.diagnostics, boolean, .invalid_index_type, ast.node_span(tree, boolean)) missing :: ast.index_expr(tree, ast.statement_decl(tree, ast.package_decl(tree, 6)).initializer).index try testing.expect_equal(sema.binding(&fixture.resolution, missing).data.diagnostic, type_of(result, missing).diagnostic) try expect_kind(result, ast.package_decl(tree, 7), .int) try testing.expect_equal(2, code_count(&fixture.diagnostics, .invalid_index_type)) try testing.expect_equal(1, code_count(&fixture.diagnostics, .unknown_name)) try testing.expect_equal(3, diagnostics.count(&fixture.diagnostics)) } array_lengths_require_supported_constexpr_and_finalize_nested_inferred_counts test { source :: `two :: tail `tail :: 2 `valid [two * 3 - 4]i64 :: [1, 2] `zero [3 - 3]i64 :: [] `nested [_]mut [_]mut i64 := [[1, 2], [3, 4]] `negative [-1]i64 :: [] `fraction [1.5]i64 :: [] `runtime_count := 2 `runtime_array [runtime_count]i64 :: [1, 2] `count proc() int { return 2 } `called [count()]i64 :: [1, 2] `a :: b `b :: a `cycle [a]i64 :: [] `overflow [18446744073709551615 + 1]i64 :: [] `wrong [2]i64 :: [1] `constraints [2]mut int := [1, 2] `floats [0]float :: [] `voids [0]void :: [] `missing [absent]i64 :: [] fixture := try array_fixture(source) defer deinit_array_fixture(&fixture) tree :: &fixture.tree result :: &fixture.result _ = try expect_array(result, ast.package_decl(tree, 2), 2, false, .int) _ = try expect_array(result, ast.package_decl(tree, 3), 0, false, .int) nested :: ast.statement_decl(tree, ast.package_decl(tree, 4)) shape :: try expect_array(result, nested.type?, 2, true, .array) child :: array_type(result, shape.element.array_type) try testing.expect_equal(2, child.len) try testing.expect(child.mutable) _ = try expect_array(result, ast.array_type(tree, nested.type?).element, 2, true, .int) invalid_lengths [6]usize :: [5, 6, 8, 10, 13, 14] for invalid_lengths |i| { annotation :: ast.statement_decl(tree, ast.package_decl(tree, i)).type? count :: ast.array_type(tree, annotation).count? _ = try expect_error(result, &fixture.diagnostics, annotation, .invalid_array_length, ast.node_span(tree, count)) } wrong :: ast.statement_decl(tree, ast.package_decl(tree, 15)).initializer _ = try expect_error(result, &fixture.diagnostics, wrong, .array_length_mismatch, ast.node_span(tree, wrong)) for usize(16)..usize(19) |i| { annotation :: ast.statement_decl(tree, ast.package_decl(tree, i)).type? child_id :: ast.array_type(tree, annotation).element _ = try expect_error(result, &fixture.diagnostics, annotation, .invalid_array_element_type, ast.node_span(tree, child_id)) } missing :: ast.statement_decl(tree, ast.package_decl(tree, 19)).type? count :: ast.array_type(tree, missing).count? try testing.expect_equal(sema.binding(&fixture.resolution, count).data.diagnostic, type_of(result, missing).diagnostic) try testing.expect_equal(6, code_count(&fixture.diagnostics, .invalid_array_length)) try testing.expect_equal(1, code_count(&fixture.diagnostics, .array_length_mismatch)) try testing.expect_equal(3, code_count(&fixture.diagnostics, .invalid_array_element_type)) } indexed_writes_require_mutable_local_root_and_each_array_level_but_copies_are_values test { source :: `global [1]mut i64 := [1] `main proc(parameter [1]mut i64) void { ` mutable [1]mut i64 := [1] ` mutable[0] = 2 ` copied := mutable ` copied[0] = 3 ` inferred := [1] ` inferred[0] = 4 ` inferred = [5] ` constant [1]mut i64 :: [1] ` constant[0] = 6 ` parameter[0] = 7 ` global[0] = 8 ` outer_readonly [1][1]mut i64 := [[1]] ` (outer_readonly[0])[0] = 9 ` inner_readonly [1]mut [1]i64 := [[1]] ` (inner_readonly[0])[0] = 10 ` both [1]mut [1]mut i64 := [[1]] ` (both[0])[0] = 11 ` both[0] = [12] ` mismatch [1]mut i64 := inner_readonly[0] `} fixture := try array_fixture(source) defer deinit_array_fixture(&fixture) tree :: &fixture.tree result :: &fixture.result body :: ast.function_decl(tree, ast.package_decl(tree, 1)).body valid [6]usize :: [1, 3, 6, 16, 17, 2] for valid |i| { id :: ast.block_statement(tree, body, i) kind TypeKind :: if (i == 2) .array else .void try expect_kind(result, id, kind) } invalid [6]usize :: [5, 8, 9, 10, 12, 14] for invalid |i| { id :: ast.block_statement(tree, body, i) target :: ast.assignment_stmt(tree, id).target _ = try expect_error(result, &fixture.diagnostics, id, .invalid_index_assignment, ast.node_span(tree, target)) try expect_kind(result, target, .int) try expect_kind(result, ast.index_expr(tree, target).index, .usize) } mismatch :: ast.statement_decl(tree, ast.block_statement(tree, body, 18)).initializer _ = try expect_error(result, &fixture.diagnostics, mismatch, .type_mismatch, ast.node_span(tree, mismatch)) _ = try expect_array(result, ast.block_statement(tree, body, 4), 1, false, .int) _ = try expect_array(result, ast.function_param(tree, ast.package_decl(tree, 1), 0), 1, true, .int) try testing.expect_equal(6, code_count(&fixture.diagnostics, .invalid_index_assignment)) try testing.expect_equal(1, code_count(&fixture.diagnostics, .type_mismatch)) } usize_arithmetic_ranges_and_readonly_captures_keep_the_bound_family test { source :: `limit usize :: 18446744073709551615 `arithmetic usize :: (limit - 2) * 1 + 1 `ordered :: 0 < limit `equal :: limit == 18446744073709551615 `negated :: -limit `divided :: limit / 2 `fixed i64 :: 1 `mixed :: fixed + limit `main proc() void { ` source [2]i64 :: [1, 2] ` for 0..limit |index| { ` source[index] ` index = 1 ` } ` for 0..2 |index| { source[index] } ` for limit..=limit |_| {} `} fixture := try array_fixture(source) defer deinit_array_fixture(&fixture) tree :: &fixture.tree result :: &fixture.result try expect_kind(result, ast.package_decl(tree, 0), .usize) try expect_kind(result, ast.package_decl(tree, 1), .usize) try expect_kind(result, ast.package_decl(tree, 2), .bool) try expect_kind(result, ast.package_decl(tree, 3), .bool) for usize(4)..usize(8) |i| { if (i == 6) continue value :: ast.statement_decl(tree, ast.package_decl(tree, i)).initializer code diagnostics.Code :: if (i == 5) .integer_division else .invalid_operand _ = try expect_error(result, &fixture.diagnostics, value, code, ast.node_span(tree, value)) } body :: ast.function_decl(tree, ast.package_decl(tree, 8)).body unsigned_loop :: ast.for_stmt(tree, ast.block_statement(tree, body, 1)) try expect_kind(result, unsigned_loop.capture?, .usize) try testing.expect_equal(TypeKind.usize, type_of(result, unsigned_loop.iterable).range_element) interval :: ast.range_expr(tree, unsigned_loop.iterable) try expect_kind(result, interval.start, .usize) try expect_kind(result, interval.end, .usize) read :: ast.statement_expr(tree, ast.block_statement(tree, unsigned_loop.body, 0)) try expect_kind(result, read, .int) try expect_kind(result, ast.index_expr(tree, read).index, .usize) readonly :: ast.block_statement(tree, unsigned_loop.body, 1) target :: ast.assignment_stmt(tree, readonly).target _ = try expect_error(result, &fixture.diagnostics, readonly, .invalid_assignment_target, ast.node_span(tree, target)) try expect_kind(result, target, .usize) signed_loop :: ast.for_stmt(tree, ast.block_statement(tree, body, 2)) try expect_kind(result, signed_loop.capture?, .int) signed_read :: ast.statement_expr(tree, ast.block_statement(tree, signed_loop.body, 0)) bad_index :: ast.index_expr(tree, signed_read).index _ = try expect_error(result, &fixture.diagnostics, bad_index, .invalid_index_type, ast.node_span(tree, bad_index)) try testing.expect_equal(TypeKind.int, type_of(result, signed_loop.iterable).range_element) _ = try expect_array(result, ast.block_statement(tree, body, 0), 2, false, .int) ignored :: ast.for_stmt(tree, ast.block_statement(tree, body, 3)) try testing.expect_equal(TypeKind.usize, type_of(result, ignored.iterable).range_element) try testing.expect_equal(1, code_count(&fixture.diagnostics, .invalid_index_type)) try testing.expect_equal(0, code_count(&fixture.diagnostics, .invalid_range_bound)) } array_assignment_rhs_is_contextual_but_does_not_retarget_established_sources test { source :: `main proc() void { ` target [2]mut f64 := [1, 2] ` seed := 3 ` source [2]i64 :: [4, 5] ` target = [seed, 6] ` target = source ` target = [1] ` matrix [1]mut [2]mut i64 := [[1, 2]] ` matrix[0] = [7, 8] ` matrix[0] = [true, false] ` unknown [1]mut i64 := [absent] ` unknown[0] = 9 ` good := 10 `} fixture := try array_fixture(source) defer deinit_array_fixture(&fixture) tree :: &fixture.tree result :: &fixture.result body :: ast.function_decl(tree, ast.package_decl(tree, 0)).body _ = try expect_array(result, ast.block_statement(tree, body, 0), 2, true, .float) try expect_kind(result, ast.block_statement(tree, body, 1), .int) _ = try expect_array(result, ast.block_statement(tree, body, 2), 2, false, .int) contextual :: ast.assignment_stmt(tree, ast.block_statement(tree, body, 3)).value _ = try expect_error(result, &fixture.diagnostics, ast.array_element(tree, contextual, 0), .type_mismatch, ast.node_span(tree, ast.array_element(tree, contextual, 0))) try expect_kind(result, ast.array_element(tree, contextual, 1), .float) copied :: ast.assignment_stmt(tree, ast.block_statement(tree, body, 4)).value _ = try expect_error(result, &fixture.diagnostics, copied, .type_mismatch, ast.node_span(tree, copied)) wrong_len :: ast.assignment_stmt(tree, ast.block_statement(tree, body, 5)).value _ = try expect_error(result, &fixture.diagnostics, wrong_len, .array_length_mismatch, ast.node_span(tree, wrong_len)) try expect_kind(result, ast.block_statement(tree, body, 7), .void) literal :: ast.statement_decl(tree, ast.block_statement(tree, body, 9)).initializer missing :: ast.array_element(tree, literal, 0) original :: sema.binding(&fixture.resolution, missing).data.diagnostic try testing.expect_equal(original, type_of(result, literal).diagnostic) try testing.expect_equal(original, type_of(result, ast.block_statement(tree, body, 9)).diagnostic) try testing.expect_equal(original, type_of(result, ast.block_statement(tree, body, 10)).diagnostic) try expect_kind(result, ast.block_statement(tree, body, 11), .int) } array_signature_identity_includes_recursive_counts_and_mutability test { source :: `accept proc(value [1]mut i64) [1]mut i64 { return value } `different_count proc(value [2]mut i64) [1]mut i64 { return [1] } `different_mutability proc(value [1]i64) [1]mut i64 { return [1] } `functions :: [accept, different_count, different_mutability] `wrong_return proc() [1]mut i64 { ` value [1]i64 :: [1] ` return value `} `take proc(value [2]i64) void {} `caller proc() void { ` take([1]) ` take([true, false]) `} fixture := try array_fixture(source) defer deinit_array_fixture(&fixture) tree :: &fixture.tree result :: &fixture.result functions :: ast.statement_decl(tree, ast.package_decl(tree, 3)).initializer for usize(1)..usize(3) |i| { value :: ast.array_element(tree, functions, i) _ = try expect_error(result, &fixture.diagnostics, value, .type_mismatch, ast.node_span(tree, value)) try expect_kind(result, ast.package_decl(tree, i), .function) } wrong :: ast.package_decl(tree, 4) wrong_body :: ast.function_decl(tree, wrong).body value :: ast.return_stmt(tree, ast.block_statement(tree, wrong_body, 1)).value? _ = try expect_error(result, &fixture.diagnostics, value, .return_type_mismatch, ast.node_span(tree, value)) _ = try expect_array(result, ast.block_statement(tree, wrong_body, 0), 1, false, .int) try expect_kind(result, wrong, .function) caller :: ast.function_decl(tree, ast.package_decl(tree, 6)).body call :: ast.statement_expr(tree, ast.block_statement(tree, caller, 0)) argument :: ast.call_arg(tree, call, 0) _ = try expect_error(result, &fixture.diagnostics, argument, .array_length_mismatch, ast.node_span(tree, argument)) second :: ast.statement_expr(tree, ast.block_statement(tree, caller, 1)) literal :: ast.call_arg(tree, second, 0) for 0..ast.array_element_count(tree, literal) |i| { child :: ast.array_element(tree, literal, i) _ = try expect_error(result, &fixture.diagnostics, child, .type_mismatch, ast.node_span(tree, child)) } try expect_kind(result, ast.package_decl(tree, 5), .function) } inferred_counts_follow_forward_fixed_aliases_empty_shapes_and_reject_local_counts test { source :: `forward [_]i64 :: later `later [_]i64 :: [1, 2] `zero [_]mut bool := [] `nested [_][_]bool :: [[], []] `unsigned usize :: 5 `main proc() void { ` local_count :: 2 ` unsupported [local_count]i64 :: [1, 2] ` values [2]i64 :: [1, 2] ` for 0..(unsigned + 1) |index| { values[index] } `} fixture := try array_fixture(source) defer deinit_array_fixture(&fixture) tree :: &fixture.tree result :: &fixture.result for 0..usize(2) |i| { declaration :: ast.statement_decl(tree, ast.package_decl(tree, i)) _ = try expect_array(result, declaration.type?, 2, false, .int) _ = try expect_array(result, ast.package_decl(tree, i), 2, false, .int) } _ = try expect_array(result, ast.package_decl(tree, 2), 0, true, .bool) shape :: try expect_array(result, ast.package_decl(tree, 3), 2, false, .array) child :: array_type(result, shape.element.array_type) try testing.expect_equal(0, child.len) try testing.expect_equal(TypeKind.bool, child.element.kind) body :: ast.function_decl(tree, ast.package_decl(tree, 5)).body unsupported :: ast.statement_decl(tree, ast.block_statement(tree, body, 1)).type? count :: ast.array_type(tree, unsupported).count? _ = try expect_error(result, &fixture.diagnostics, unsupported, .invalid_array_length, ast.node_span(tree, count)) try expect_kind(result, ast.block_statement(tree, body, 0), .int) loop :: ast.for_stmt(tree, ast.block_statement(tree, body, 3)) try expect_kind(result, loop.capture?, .usize) try testing.expect_equal(TypeKind.usize, type_of(result, loop.iterable).range_element) read :: ast.statement_expr(tree, ast.block_statement(tree, loop.body, 0)) try expect_kind(result, read, .int) try testing.expect_equal(1, code_count(&fixture.diagnostics, .invalid_array_length)) try testing.expect_equal(1, diagnostics.count(&fixture.diagnostics)) }