import "@std/mem" import "@std/testing" import "@source/diagnostics" import "@source/strpool" import "@source/lexer" import "@source/parser" import "@source/ast" resolves_forward_references_recursion_and_builtin_signatures test { pool := strpool.init(mem.c_allocator) defer strpool.deinit(&pool) diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) source :: `first :: later `alpha proc(p int, q float, s string) void { ` before :: -after * p + beta(after) ` after :: later `} `beta proc(p int) void { ` after :: p ` alpha(after, 1.0, "x") `} `later int :: 1 scan_state := lexer.init(mem.c_allocator) defer lexer.deinit(&scan_state) try lexer.scan(&scan_state, source, &pool, &diagnostic_store) tree := try parser.parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) defer ast.deinit(&tree) resolution := try resolve(mem.c_allocator, &tree, &pool, &diagnostic_store) defer deinit(&resolution) first :: ast.package_decl(&tree, 0) alpha :: ast.package_decl(&tree, 1) beta :: ast.package_decl(&tree, 2) later :: ast.package_decl(&tree, 3) alpha_view :: ast.function_decl(&tree, alpha) beta_view :: ast.function_decl(&tree, beta) before :: ast.block_statement(&tree, alpha_view.body, 0) after :: ast.block_statement(&tree, alpha_view.body, 1) p :: ast.function_param(&tree, alpha, 0) q :: ast.function_param(&tree, alpha, 1) s :: ast.function_param(&tree, alpha, 2) sibling_p :: ast.function_param(&tree, beta, 0) sibling_after :: ast.block_statement(&tree, beta_view.body, 0) first_ref :: ast.statement_decl(&tree, first).initializer after_ref :: ast.statement_decl(&tree, after).initializer references [2]ast.NodeId :: [first_ref, after_ref] for references |id| { try testing.expect_equal(BindingKind.declaration, binding(&resolution, id).kind) try testing.expect_equal(later, binding(&resolution, id).data.declaration) } add :: ast.binary_expr(&tree, ast.statement_decl(&tree, before).initializer) multiply :: ast.binary_expr(&tree, add.left) forward_ref :: ast.unary_expr(&tree, multiply.left).operand try testing.expect_equal(BindingKind.declaration, binding(&resolution, forward_ref).kind) try testing.expect_equal(after, binding(&resolution, forward_ref).data.declaration) try testing.expect_equal(BindingKind.declaration, binding(&resolution, multiply.right).kind) try testing.expect_equal(p, binding(&resolution, multiply.right).data.declaration) beta_ref :: ast.call_expr(&tree, add.right).callee try testing.expect_equal(BindingKind.declaration, binding(&resolution, beta_ref).kind) try testing.expect_equal(beta, binding(&resolution, beta_ref).data.declaration) argument :: ast.call_arg(&tree, add.right, 0) try testing.expect_equal(BindingKind.declaration, binding(&resolution, argument).kind) try testing.expect_equal(after, binding(&resolution, argument).data.declaration) recursive_call :: ast.statement_expr(&tree, ast.block_statement(&tree, beta_view.body, 1)) alpha_ref :: ast.call_expr(&tree, recursive_call).callee try testing.expect_equal(BindingKind.declaration, binding(&resolution, alpha_ref).kind) try testing.expect_equal(alpha, binding(&resolution, alpha_ref).data.declaration) sibling_ref :: ast.call_arg(&tree, recursive_call, 0) try testing.expect_equal(BindingKind.declaration, binding(&resolution, sibling_ref).kind) try testing.expect_equal(sibling_after, binding(&resolution, sibling_ref).data.declaration) sibling_param_ref :: ast.statement_decl(&tree, sibling_after).initializer try testing.expect_equal(BindingKind.declaration, binding(&resolution, sibling_param_ref).kind) try testing.expect_equal(sibling_p, binding(&resolution, sibling_param_ref).data.declaration) parameter_types [2]ast.NodeId :: [ast.param(&tree, p).type, ast.param(&tree, sibling_p).type] for parameter_types |id| { try testing.expect_equal(BindingKind.builtin, binding(&resolution, id).kind) try testing.expect_equal(BuiltinType.int, binding(&resolution, id).data.builtin) } if (ast.statement_decl(&tree, later).type) |type| { try testing.expect_equal(BindingKind.builtin, binding(&resolution, type).kind) try testing.expect_equal(BuiltinType.int, binding(&resolution, type).data.builtin) } else { return .expectation_failed } try testing.expect_equal( BindingKind.builtin, binding(&resolution, ast.param(&tree, q).type).kind, ) try testing.expect_equal( BuiltinType.float, binding(&resolution, ast.param(&tree, q).type).data.builtin, ) try testing.expect_equal( BindingKind.builtin, binding(&resolution, ast.param(&tree, s).type).kind, ) try testing.expect_equal( BuiltinType.string, binding(&resolution, ast.param(&tree, s).type).data.builtin, ) return_types [2]ast.NodeId :: [alpha_view.return_type, beta_view.return_type] for return_types |id| { try testing.expect_equal(BindingKind.builtin, binding(&resolution, id).kind) try testing.expect_equal(BuiltinType.void, binding(&resolution, id).data.builtin) } try testing.expect_equal(0, diagnostics.count(&diagnostic_store)) } rejects_shadowing_and_duplicates_without_cascades test { pool := strpool.init(mem.c_allocator) defer strpool.deinit(&pool) diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) source :: `collide proc(outer int, p int, p int) outer { ` outer :: missing ` outer :: 1 ` p :: 1 ` int :: 1 ` pkg :: 1 ` outer ` p ` int ` pkg `} `outer :: 1 `pkg :: 1 `same :: 1 `same :: same scan_state := lexer.init(mem.c_allocator) defer lexer.deinit(&scan_state) try lexer.scan(&scan_state, source, &pool, &diagnostic_store) tree := try parser.parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) defer ast.deinit(&tree) try testing.expect_equal(0, diagnostics.count(&diagnostic_store)) resolution := try resolve(mem.c_allocator, &tree, &pool, &diagnostic_store) defer deinit(&resolution) function :: ast.package_decl(&tree, 0) view :: ast.function_decl(&tree, function) outer :: ast.package_decl(&tree, 1) first_same :: ast.package_decl(&tree, 3) second_same :: ast.package_decl(&tree, 4) outer_param :: ast.function_param(&tree, function, 0) p :: ast.function_param(&tree, function, 1) duplicate_p :: ast.function_param(&tree, function, 2) first_outer :: ast.block_statement(&tree, view.body, 0) second_outer :: ast.block_statement(&tree, view.body, 1) local_p :: ast.block_statement(&tree, view.body, 2) local_int :: ast.block_statement(&tree, view.body, 3) local_pkg :: ast.block_statement(&tree, view.body, 4) rejected [8]ast.NodeId :: [ outer_param, duplicate_p, first_outer, second_outer, local_p, local_int, local_pkg, second_same, ] for rejected |id| { invalid_binding :: binding(&resolution, id) try testing.expect_equal(BindingKind.invalid, invalid_binding.kind) if (diagnostics.get(&diagnostic_store, invalid_binding.data.diagnostic)) |diagnostic| { try testing.expect_equal(diagnostics.Code.duplicate_name, diagnostic.code) name :: if (id == outer_param or id == duplicate_p) ast.param(&tree, id).name else ast.statement_decl(&tree, id).name try testing.expect_equal(name.start, diagnostic.span.start) try testing.expect_equal(name.end, diagnostic.span.end) } else { return .expectation_failed } } try testing.expect( binding(&resolution, first_outer).data.diagnostic != binding(&resolution, second_outer).data.diagnostic, ) try testing.expect( binding(&resolution, outer_param).data.diagnostic != binding(&resolution, first_outer).data.diagnostic, ) try testing.expect( binding(&resolution, duplicate_p).data.diagnostic != binding(&resolution, local_p).data.diagnostic, ) outer_ref :: ast.statement_expr(&tree, ast.block_statement(&tree, view.body, 5)) try testing.expect_equal(BindingKind.invalid, binding(&resolution, outer_ref).kind) try testing.expect_equal( binding(&resolution, outer_param).data.diagnostic, binding(&resolution, outer_ref).data.diagnostic, ) p_ref :: ast.statement_expr(&tree, ast.block_statement(&tree, view.body, 6)) try testing.expect_equal(BindingKind.declaration, binding(&resolution, p_ref).kind) try testing.expect_equal(p, binding(&resolution, p_ref).data.declaration) int_ref :: ast.statement_expr(&tree, ast.block_statement(&tree, view.body, 7)) try testing.expect_equal(BindingKind.invalid, binding(&resolution, int_ref).kind) try testing.expect_equal( binding(&resolution, local_int).data.diagnostic, binding(&resolution, int_ref).data.diagnostic, ) pkg_ref :: ast.statement_expr(&tree, ast.block_statement(&tree, view.body, 8)) try testing.expect_equal(BindingKind.invalid, binding(&resolution, pkg_ref).kind) try testing.expect_equal( binding(&resolution, local_pkg).data.diagnostic, binding(&resolution, pkg_ref).data.diagnostic, ) try testing.expect_equal(BindingKind.declaration, binding(&resolution, view.return_type).kind) try testing.expect_equal(outer, binding(&resolution, view.return_type).data.declaration) parameter_type :: ast.param(&tree, outer_param).type try testing.expect_equal(BindingKind.builtin, binding(&resolution, parameter_type).kind) try testing.expect_equal(BuiltinType.int, binding(&resolution, parameter_type).data.builtin) same_ref :: ast.statement_decl(&tree, second_same).initializer try testing.expect_equal(BindingKind.declaration, binding(&resolution, same_ref).kind) try testing.expect_equal(first_same, binding(&resolution, same_ref).data.declaration) missing :: ast.statement_decl(&tree, first_outer).initializer try testing.expect_equal(BindingKind.invalid, binding(&resolution, missing).kind) if (diagnostics.get(&diagnostic_store, binding(&resolution, missing).data.diagnostic)) |diagnostic| { try testing.expect_equal(diagnostics.Code.unknown_name, diagnostic.code) try testing.expect_equal(ast.node_span(&tree, missing).start, diagnostic.span.start) try testing.expect_equal(ast.node_span(&tree, missing).end, diagnostic.span.end) } else { return .expectation_failed } try testing.expect_equal(9, diagnostics.count(&diagnostic_store)) } preserves_syntax_diagnostics_and_resolves_shared_unknown_types_once test { pool := strpool.init(mem.c_allocator) defer strpool.deinit(&pool) diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) source :: `@ broken `main proc(a, b Missing) void { ` empty :: () ` bad :: absent + absent ` valid :: later ` later :: 1 ` discarded(1 2) `} scan_state := lexer.init(mem.c_allocator) defer lexer.deinit(&scan_state) try lexer.scan(&scan_state, source, &pool, &diagnostic_store) tree := try parser.parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) defer ast.deinit(&tree) syntax_count :: diagnostics.count(&diagnostic_store) invalid_decl :: ast.package_decl(&tree, 0) try testing.expect_equal(ast.DeclKind.invalid, ast.decl_kind(&tree, invalid_decl)) function :: ast.package_decl(&tree, 1) view :: ast.function_decl(&tree, function) empty :: ast.statement_decl(&tree, ast.block_statement(&tree, view.body, 0)).initializer try testing.expect_equal(ast.ExprKind.invalid, ast.expr_kind(&tree, empty)) invalid_statement :: ast.block_statement(&tree, view.body, 4) try testing.expect_equal(ast.StmtKind.invalid, ast.stmt_kind(&tree, invalid_statement)) resolution := try resolve(mem.c_allocator, &tree, &pool, &diagnostic_store) defer deinit(&resolution) invalid_nodes [3]ast.NodeId :: [invalid_decl, empty, invalid_statement] for invalid_nodes |id| { invalid_view :: ast.invalid(&tree, id) invalid_binding :: binding(&resolution, id) try testing.expect_equal(BindingKind.invalid, invalid_binding.kind) try testing.expect_equal(invalid_view.diagnostic, invalid_binding.data.diagnostic) } 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( binding(&resolution, shared_type).data.diagnostic, binding(&resolution, ast.param(&tree, b).type).data.diagnostic, ) bad :: ast.binary_expr( &tree, ast.statement_decl(&tree, ast.block_statement(&tree, view.body, 1)).initializer, ) unknown_nodes [3]ast.NodeId :: [shared_type, bad.left, bad.right] for unknown_nodes |id| { invalid_binding :: binding(&resolution, id) try testing.expect_equal(BindingKind.invalid, invalid_binding.kind) if (diagnostics.get(&diagnostic_store, invalid_binding.data.diagnostic)) |diagnostic| { try testing.expect_equal(diagnostics.Code.unknown_name, diagnostic.code) try testing.expect_equal(ast.node_span(&tree, id).start, diagnostic.span.start) try testing.expect_equal(ast.node_span(&tree, id).end, diagnostic.span.end) } else { return .expectation_failed } } try testing.expect( binding(&resolution, bad.left).data.diagnostic != binding(&resolution, bad.right).data.diagnostic, ) valid :: ast.block_statement(&tree, view.body, 2) later :: ast.block_statement(&tree, view.body, 3) valid_ref :: ast.statement_decl(&tree, valid).initializer try testing.expect_equal(BindingKind.declaration, binding(&resolution, valid_ref).kind) try testing.expect_equal(later, binding(&resolution, valid_ref).data.declaration) try testing.expect_equal(syntax_count + 3, diagnostics.count(&diagnostic_store)) } resolves_conditional_arm_scopes_without_leaks_or_shadowing test { pool := strpool.init(mem.c_allocator) defer strpool.deinit(&pool) diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) source :: `choose proc(flag bool, value int) void { ` if flag { ` before :: later ` later :: value ` if before == value { nested :: later } ` } else { ` later :: value ` } ` later ` if later { later :: 1 } ` if flag { ` value :: 2 ` bool :: false ` if flag { value } ` } `} scan_state := lexer.init(mem.c_allocator) defer lexer.deinit(&scan_state) try lexer.scan(&scan_state, source, &pool, &diagnostic_store) tree := try parser.parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) defer ast.deinit(&tree) try testing.expect_equal(0, diagnostics.count(&diagnostic_store)) resolution := try resolve(mem.c_allocator, &tree, &pool, &diagnostic_store) defer deinit(&resolution) function :: ast.package_decl(&tree, 0) view :: ast.function_decl(&tree, function) flag :: ast.function_param(&tree, function, 0) value :: ast.function_param(&tree, function, 1) try testing.expect_equal(BuiltinType.bool, binding(&resolution, ast.param(&tree, flag).type).data.builtin) first :: ast.if_stmt(&tree, ast.block_statement(&tree, view.body, 0)) try testing.expect_equal(flag, binding(&resolution, first.condition).data.declaration) before :: ast.block_statement(&tree, first.then_block, 0) later :: ast.block_statement(&tree, first.then_block, 1) try testing.expect_equal(later, binding(&resolution, ast.statement_decl(&tree, before).initializer).data.declaration) try testing.expect_equal(value, binding(&resolution, ast.statement_decl(&tree, later).initializer).data.declaration) nested :: ast.if_stmt(&tree, ast.block_statement(&tree, first.then_block, 2)) nested_decl :: ast.block_statement(&tree, nested.then_block, 0) try testing.expect_equal(later, binding(&resolution, ast.statement_decl(&tree, nested_decl).initializer).data.declaration) if (first.else_block) |other| { sibling :: ast.block_statement(&tree, other, 0) try testing.expect_equal(BindingKind.declaration, binding(&resolution, sibling).kind) try testing.expect_equal(sibling, binding(&resolution, sibling).data.declaration) } else { return .expectation_failed } outside :: ast.statement_expr(&tree, ast.block_statement(&tree, view.body, 1)) try testing.expect_equal(BindingKind.invalid, binding(&resolution, outside).kind) independent :: ast.if_stmt(&tree, ast.block_statement(&tree, view.body, 2)) try testing.expect_equal(BindingKind.invalid, binding(&resolution, independent.condition).kind) try testing.expect_equal(BindingKind.declaration, binding(&resolution, ast.block_statement(&tree, independent.then_block, 0)).kind) forbidden :: ast.if_stmt(&tree, ast.block_statement(&tree, view.body, 3)) shadow :: ast.block_statement(&tree, forbidden.then_block, 0) builtin :: ast.block_statement(&tree, forbidden.then_block, 1) try testing.expect_equal(BindingKind.invalid, binding(&resolution, shadow).kind) try testing.expect_equal(BindingKind.invalid, binding(&resolution, builtin).kind) poisoned :: ast.if_stmt(&tree, ast.block_statement(&tree, forbidden.then_block, 2)) use :: ast.statement_expr(&tree, ast.block_statement(&tree, poisoned.then_block, 0)) try testing.expect_equal(binding(&resolution, shadow).data.diagnostic, binding(&resolution, use).data.diagnostic) try testing.expect_equal(4, diagnostics.count(&diagnostic_store)) } resolves_loop_assignments_in_lexical_scopes_with_forward_local_bindings test { pool := strpool.init(mem.c_allocator) defer strpool.deinit(&pool) diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) source :: `main proc(flag bool) void { ` outer := 2 ` while flag { ` later = outer ` later := 1 ` while later > 0 { ` outer = later ` inner := later ` } ` inner ` } ` while flag { later := 3 } ` later = unknown_rhs ` while flag { ` outer := 1 ` outer = 2 ` bool := false ` } `} scan_state := lexer.init(mem.c_allocator) defer lexer.deinit(&scan_state) try lexer.scan(&scan_state, source, &pool, &diagnostic_store) tree := try parser.parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) defer ast.deinit(&tree) try testing.expect_equal(0, diagnostics.count(&diagnostic_store)) resolution := try resolve(mem.c_allocator, &tree, &pool, &diagnostic_store) defer deinit(&resolution) function :: ast.package_decl(&tree, 0) body :: ast.function_decl(&tree, function).body flag :: ast.function_param(&tree, function, 0) outer :: ast.block_statement(&tree, body, 0) first :: ast.while_stmt(&tree, ast.block_statement(&tree, body, 1)) try testing.expect_equal(flag, binding(&resolution, first.condition).data.declaration) forward :: ast.assignment_stmt(&tree, ast.block_statement(&tree, first.body, 0)) later :: ast.block_statement(&tree, first.body, 1) try testing.expect_equal(BindingKind.declaration, binding(&resolution, forward.target).kind) try testing.expect_equal(later, binding(&resolution, forward.target).data.declaration) try testing.expect_equal(outer, binding(&resolution, forward.value).data.declaration) nested :: ast.while_stmt(&tree, ast.block_statement(&tree, first.body, 2)) guard :: ast.binary_expr(&tree, nested.condition) try testing.expect_equal(later, binding(&resolution, guard.left).data.declaration) update :: ast.assignment_stmt(&tree, ast.block_statement(&tree, nested.body, 0)) try testing.expect_equal(outer, binding(&resolution, update.target).data.declaration) try testing.expect_equal(later, binding(&resolution, update.value).data.declaration) inner :: ast.block_statement(&tree, nested.body, 1) try testing.expect_equal(later, binding(&resolution, ast.statement_decl(&tree, inner).initializer).data.declaration) escaped_inner :: ast.statement_expr(&tree, ast.block_statement(&tree, first.body, 3)) try testing.expect_equal(BindingKind.invalid, binding(&resolution, escaped_inner).kind) sibling :: ast.while_stmt(&tree, ast.block_statement(&tree, body, 2)) sibling_local :: ast.block_statement(&tree, sibling.body, 0) try testing.expect_equal(BindingKind.declaration, binding(&resolution, sibling_local).kind) try testing.expect(sibling_local != later) escaped :: ast.assignment_stmt(&tree, ast.block_statement(&tree, body, 3)) try testing.expect_equal(BindingKind.invalid, binding(&resolution, escaped.target).kind) try testing.expect_equal(BindingKind.invalid, binding(&resolution, escaped.value).kind) try testing.expect(binding(&resolution, escaped.target).data.diagnostic != binding(&resolution, escaped.value).data.diagnostic) forbidden :: ast.while_stmt(&tree, ast.block_statement(&tree, body, 4)) shadow :: ast.block_statement(&tree, forbidden.body, 0) poisoned :: ast.assignment_stmt(&tree, ast.block_statement(&tree, forbidden.body, 1)) builtin :: ast.block_statement(&tree, forbidden.body, 2) try testing.expect_equal(BindingKind.invalid, binding(&resolution, shadow).kind) try testing.expect_equal(binding(&resolution, shadow).data.diagnostic, binding(&resolution, poisoned.target).data.diagnostic) try testing.expect_equal(BindingKind.invalid, binding(&resolution, builtin).kind) try testing.expect_equal(5, diagnostics.count(&diagnostic_store)) } resolves_all_code_and_preserves_scopes_after_loop_controls test { pool := strpool.init(mem.c_allocator) defer strpool.deinit(&pool) diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) source :: `main proc() void { ` while (true) { ` break ` before :: later ` continue ` later :: 1 ` missing_after_break ` if (true) { ` continue ` private :: later ` int :: 2 ` } ` private ` } ` later `} scan_state := lexer.init(mem.c_allocator) defer lexer.deinit(&scan_state) try lexer.scan(&scan_state, source, &pool, &diagnostic_store) tree := try parser.parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) defer ast.deinit(&tree) try testing.expect_equal(0, diagnostics.count(&diagnostic_store)) resolution := try resolve(mem.c_allocator, &tree, &pool, &diagnostic_store) defer deinit(&resolution) try testing.expect_equal(4, diagnostics.count(&diagnostic_store)) body :: ast.function_decl(&tree, ast.package_decl(&tree, 0)).body loop :: ast.while_stmt(&tree, ast.block_statement(&tree, body, 0)) before :: ast.block_statement(&tree, loop.body, 1) later :: ast.block_statement(&tree, loop.body, 3) reference :: ast.statement_decl(&tree, before).initializer try testing.expect_equal(BindingKind.declaration, binding(&resolution, reference).kind) try testing.expect_equal(later, binding(&resolution, reference).data.declaration) conditional :: ast.if_stmt(&tree, ast.block_statement(&tree, loop.body, 5)) private_decl :: ast.block_statement(&tree, conditional.then_block, 1) private_ref :: ast.statement_decl(&tree, private_decl).initializer try testing.expect_equal(later, binding(&resolution, private_ref).data.declaration) forbidden :: ast.block_statement(&tree, conditional.then_block, 2) try testing.expect_equal(BindingKind.invalid, binding(&resolution, forbidden).kind) } range_bounds_use_outer_scope_and_captures_are_body_declarations test { pool := strpool.init(mem.c_allocator) defer strpool.deinit(&pool) diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) source :: `run proc() void { ` for lower..upper |item| { ` before :: later ` later :: item ` continue ` item ` } ` for lower..upper |item| { item } ` lower :: 0 ` upper :: 2 ` item `} scan_state := lexer.init(mem.c_allocator) defer lexer.deinit(&scan_state) try lexer.scan(&scan_state, source, &pool, &diagnostic_store) tree := try parser.parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) defer ast.deinit(&tree) resolution := try resolve(mem.c_allocator, &tree, &pool, &diagnostic_store) defer deinit(&resolution) body :: ast.function_decl(&tree, ast.package_decl(&tree, 0)).body loop :: ast.for_stmt(&tree, ast.block_statement(&tree, body, 0)) sibling :: ast.for_stmt(&tree, ast.block_statement(&tree, body, 1)) lower :: ast.block_statement(&tree, body, 2) upper :: ast.block_statement(&tree, body, 3) bounds :: ast.range_expr(&tree, loop.iterable) try testing.expect_equal(lower, binding(&resolution, bounds.start).data.declaration) try testing.expect_equal(upper, binding(&resolution, bounds.end).data.declaration) if (loop.capture) |capture| { try testing.expect_equal(capture, binding(&resolution, capture).data.declaration) later :: ast.block_statement(&tree, loop.body, 1) before_ref :: ast.statement_decl(&tree, ast.block_statement(&tree, loop.body, 0)).initializer try testing.expect_equal(later, binding(&resolution, before_ref).data.declaration) capture_ref :: ast.statement_decl(&tree, later).initializer try testing.expect_equal(capture, binding(&resolution, capture_ref).data.declaration) dead_ref :: ast.statement_expr(&tree, ast.block_statement(&tree, loop.body, 3)) try testing.expect_equal(capture, binding(&resolution, dead_ref).data.declaration) } else return .expectation_failed if (sibling.capture) |capture| { try testing.expect_equal(BindingKind.declaration, binding(&resolution, capture).kind) sibling_ref :: ast.statement_expr(&tree, ast.block_statement(&tree, sibling.body, 0)) try testing.expect_equal(capture, binding(&resolution, sibling_ref).data.declaration) } else return .expectation_failed escaped :: ast.statement_expr(&tree, ast.block_statement(&tree, body, 4)) try testing.expect_equal(BindingKind.invalid, binding(&resolution, escaped).kind) try testing.expect_equal(1, diagnostics.count(&diagnostic_store)) } range_captures_preserve_duplicate_builtin_and_discard_errors test { pool := strpool.init(mem.c_allocator) defer strpool.deinit(&pool) diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) source :: `run proc() void { ` outer :: 0 ` for outer..2 |outer| { outer } ` for 0..2 |int| { int } ` for 0..2 |item, item| { item } ` for 0..2 |local| { ` local :: 1 ` local ` } ` for 0..2 |_| { ` break ` _ ` missing ` } ` for missing_start..missing_end |i| {} `} scan_state := lexer.init(mem.c_allocator) defer lexer.deinit(&scan_state) try lexer.scan(&scan_state, source, &pool, &diagnostic_store) tree := try parser.parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) defer ast.deinit(&tree) resolution := try resolve(mem.c_allocator, &tree, &pool, &diagnostic_store) defer deinit(&resolution) body :: ast.function_decl(&tree, ast.package_decl(&tree, 0)).body for (usize(1)..usize(3)) |index| { loop :: ast.for_stmt(&tree, ast.block_statement(&tree, body, index)) if (loop.capture) |capture| { try testing.expect_equal(BindingKind.invalid, binding(&resolution, capture).kind) reference :: ast.statement_expr(&tree, ast.block_statement(&tree, loop.body, 0)) try testing.expect_equal( binding(&resolution, capture).data.diagnostic, binding(&resolution, reference).data.diagnostic, ) } else return .expectation_failed } indexed :: ast.for_stmt(&tree, ast.block_statement(&tree, body, 3)) if (indexed.index) |index| try testing.expect_equal(BindingKind.invalid, binding(&resolution, index).kind) else return .expectation_failed if (indexed.capture) |capture| { reference :: ast.statement_expr(&tree, ast.block_statement(&tree, indexed.body, 0)) try testing.expect_equal(capture, binding(&resolution, reference).data.declaration) } else return .expectation_failed local_loop :: ast.for_stmt(&tree, ast.block_statement(&tree, body, 4)) duplicate :: ast.block_statement(&tree, local_loop.body, 0) try testing.expect_equal(BindingKind.invalid, binding(&resolution, duplicate).kind) discard :: ast.for_stmt(&tree, ast.block_statement(&tree, body, 5)) try testing.expect(discard.capture == null) for (usize(1)..usize(3)) |index| { reference :: ast.statement_expr(&tree, ast.block_statement(&tree, discard.body, index)) try testing.expect_equal(BindingKind.invalid, binding(&resolution, reference).kind) } missing_loop :: ast.for_stmt(&tree, ast.block_statement(&tree, body, 6)) bounds :: ast.range_expr(&tree, missing_loop.iterable) try testing.expect_equal(BindingKind.invalid, binding(&resolution, bounds.start).kind) try testing.expect_equal(BindingKind.invalid, binding(&resolution, bounds.end).kind) try testing.expect_equal(8, diagnostics.count(&diagnostic_store)) } resolves_array_counts_elements_indices_and_unreachable_targets test { pool := strpool.init(mem.c_allocator) defer strpool.deinit(&pool) diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) source :: `size usize :: 2 `make proc(first, second [size]mut [2]i64) [size]i64 { ` return first[0] ` dead [local_size]mut [size - 1]f64 := [[later]] ` local_size :: size ` later f64 :: 1.0 ` (dead[index])[0] = later ` index usize :: 0 `} scan_state := lexer.init(mem.c_allocator) defer lexer.deinit(&scan_state) try lexer.scan(&scan_state, source, &pool, &diagnostic_store) tree := try parser.parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) defer ast.deinit(&tree) resolution := try resolve(mem.c_allocator, &tree, &pool, &diagnostic_store) defer deinit(&resolution) try testing.expect_equal(0, diagnostics.count(&diagnostic_store)) size :: ast.package_decl(&tree, 0) function :: ast.package_decl(&tree, 1) view :: ast.function_decl(&tree, function) return_type :: ast.array_type(&tree, view.return_type) try testing.expect_equal(size, binding(&resolution, return_type.count?).data.declaration) try testing.expect_equal(BuiltinType.i64, binding(&resolution, return_type.element).data.builtin) first :: ast.function_param(&tree, function, 0) second :: ast.function_param(&tree, function, 1) first_type :: ast.param(&tree, first).type try testing.expect_equal(first_type, ast.param(&tree, second).type) parameter_array :: ast.array_type(&tree, first_type) try testing.expect_equal(size, binding(&resolution, parameter_array.count?).data.declaration) row :: ast.array_type(&tree, parameter_array.element) try testing.expect_equal(BuiltinType.i64, binding(&resolution, row.element).data.builtin) returned :: ast.return_stmt(&tree, ast.block_statement(&tree, view.body, 0)).value? try testing.expect_equal(first, binding(&resolution, ast.index_expr(&tree, returned).base).data.declaration) dead :: ast.block_statement(&tree, view.body, 1) local_size :: ast.block_statement(&tree, view.body, 2) later :: ast.block_statement(&tree, view.body, 3) index :: ast.block_statement(&tree, view.body, 5) declaration :: ast.statement_decl(&tree, dead) array :: ast.array_type(&tree, declaration.type?) try testing.expect_equal(local_size, binding(&resolution, array.count?).data.declaration) inner :: ast.array_type(&tree, array.element) count_expression :: ast.binary_expr(&tree, inner.count?) try testing.expect_equal(size, binding(&resolution, count_expression.left).data.declaration) try testing.expect_equal(BuiltinType.f64, binding(&resolution, inner.element).data.builtin) literal_row :: ast.array_element(&tree, declaration.initializer, 0) try testing.expect_equal(later, binding( &resolution, ast.array_element(&tree, literal_row, 0), ).data.declaration) assignment :: ast.assignment_stmt(&tree, ast.block_statement(&tree, view.body, 4)) target :: ast.index_expr(&tree, ast.index_expr(&tree, assignment.target).base) try testing.expect_equal(dead, binding(&resolution, target.base).data.declaration) try testing.expect_equal(index, binding(&resolution, target.index).data.declaration) try testing.expect_equal(later, binding(&resolution, assignment.value).data.declaration) try testing.expect_equal(BuiltinType.usize, binding( &resolution, ast.statement_decl(&tree, size).type?, ).data.builtin) } array_signature_counts_use_package_scope_and_local_counts_see_parameters test { pool := strpool.init(mem.c_allocator) defer strpool.deinit(&pool) diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) source :: `size :: 2 `inspect proc(n usize, a, b [n]i64) [body_size]i64 { ` body_size :: size ` local [n]i64 :: [] ` return [] `} scan_state := lexer.init(mem.c_allocator) defer lexer.deinit(&scan_state) try lexer.scan(&scan_state, source, &pool, &diagnostic_store) tree := try parser.parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) defer ast.deinit(&tree) try testing.expect_equal(0, diagnostics.count(&diagnostic_store)) resolution := try resolve(mem.c_allocator, &tree, &pool, &diagnostic_store) defer deinit(&resolution) try testing.expect_equal(2, diagnostics.count(&diagnostic_store)) function :: ast.package_decl(&tree, 1) view :: ast.function_decl(&tree, function) parameter :: ast.function_param(&tree, function, 0) grouped_type :: ast.param(&tree, ast.function_param(&tree, function, 1)).type try testing.expect_equal(grouped_type, ast.param( &tree, ast.function_param(&tree, function, 2), ).type) signature_count :: ast.array_type(&tree, grouped_type).count? try testing.expect_equal(BindingKind.invalid, binding(&resolution, signature_count).kind) result_count :: ast.array_type(&tree, view.return_type).count? try testing.expect_equal(BindingKind.invalid, binding(&resolution, result_count).kind) local :: ast.statement_decl(&tree, ast.block_statement(&tree, view.body, 1)) local_count :: ast.array_type(&tree, local.type?).count? try testing.expect_equal(BindingKind.declaration, binding(&resolution, local_count).kind) try testing.expect_equal(parameter, binding(&resolution, local_count).data.declaration) try testing.expect_equal(BuiltinType.i64, binding( &resolution, ast.array_type(&tree, local.type?).element, ).data.builtin) } resolves_every_array_child_error_and_preserves_lexer_diagnostics test { pool := strpool.init(mem.c_allocator) defer strpool.deinit(&pool) diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) source :: `main proc() void { ` return ` data [missing_count]MissingElement :: [absent, missing_element] ` (missing_base[missing_index])[missing_outer] = missing_rhs ` malformed [~]i64 :: [~] `} scan_state := lexer.init(mem.c_allocator) defer lexer.deinit(&scan_state) try lexer.scan(&scan_state, source, &pool, &diagnostic_store) tree := try parser.parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) defer ast.deinit(&tree) try testing.expect_equal(2, diagnostics.count(&diagnostic_store)) resolution := try resolve(mem.c_allocator, &tree, &pool, &diagnostic_store) defer deinit(&resolution) try testing.expect_equal(10, diagnostics.count(&diagnostic_store)) body :: ast.function_decl(&tree, ast.package_decl(&tree, 0)).body declaration :: ast.statement_decl(&tree, ast.block_statement(&tree, body, 1)) type :: ast.array_type(&tree, declaration.type?) assignment :: ast.assignment_stmt(&tree, ast.block_statement(&tree, body, 2)) outer :: ast.index_expr(&tree, assignment.target) inner :: ast.index_expr(&tree, outer.base) unknowns [8]ast.NodeId :: [ type.count?, type.element, ast.array_element(&tree, declaration.initializer, 0), ast.array_element(&tree, declaration.initializer, 1), inner.base, inner.index, outer.index, assignment.value, ] for (unknowns) |id| { result :: binding(&resolution, id) try testing.expect_equal(BindingKind.invalid, result.kind) try testing.expect_equal(diagnostics.Code.unknown_name, diagnostics.get( &diagnostic_store, result.data.diagnostic, )?.code) } malformed :: ast.statement_decl(&tree, ast.block_statement(&tree, body, 3)) malformed_count :: ast.array_type(&tree, malformed.type?).count? malformed_element :: ast.array_element(&tree, malformed.initializer, 0) try testing.expect_equal(diagnostics.DiagnosticId(0), binding(&resolution, malformed_count).data.diagnostic) try testing.expect_equal(diagnostics.DiagnosticId(1), binding(&resolution, malformed_element).data.diagnostic) } resolves_interleaved_compilations_with_independent_identifier_orders test { first_pool := strpool.init(mem.c_allocator) defer strpool.deinit(&first_pool) second_pool := strpool.init(mem.c_allocator) defer strpool.deinit(&second_pool) first_diagnostics := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&first_diagnostics) second_diagnostics := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&second_diagnostics) first_source :: "alpha :: beta\nbeta i64 :: 1\nmain proc() bool { return true }\n" second_source :: "beta :: alpha\nalpha bool :: false\nmain proc() i64 { return 1 }\n" first_scan := lexer.init(mem.c_allocator) defer lexer.deinit(&first_scan) second_scan := lexer.init(mem.c_allocator) defer lexer.deinit(&second_scan) try lexer.scan(&first_scan, first_source, &first_pool, &first_diagnostics) first_tree := try parser.parse(mem.c_allocator, first_scan.tokens.items, first_source, &first_diagnostics) defer ast.deinit(&first_tree) try lexer.scan(&second_scan, second_source, &second_pool, &second_diagnostics) second_tree := try parser.parse(mem.c_allocator, second_scan.tokens.items, second_source, &second_diagnostics) defer ast.deinit(&second_tree) first_name :: ast.name_id(&first_tree, ast.package_decl(&first_tree, 0)) second_name :: ast.name_id(&second_tree, ast.package_decl(&second_tree, 0)) try testing.expect_equal(first_name, second_name) try testing.expect_equal("alpha", strpool.get_str(&first_pool, first_name)?) try testing.expect_equal("beta", strpool.get_str(&second_pool, second_name)?) first_resolution := try resolve(mem.c_allocator, &first_tree, &first_pool, &first_diagnostics) defer deinit(&first_resolution) second_resolution := try resolve(mem.c_allocator, &second_tree, &second_pool, &second_diagnostics) defer deinit(&second_resolution) first_decl :: ast.package_decl(&first_tree, 0) second_decl :: ast.package_decl(&second_tree, 0) first_target :: ast.package_decl(&first_tree, 1) second_target :: ast.package_decl(&second_tree, 1) first_use :: ast.statement_decl(&first_tree, first_decl).initializer second_use :: ast.statement_decl(&second_tree, second_decl).initializer try testing.expect_equal(BindingKind.declaration, binding(&first_resolution, first_use).kind) try testing.expect_equal(first_target, binding(&first_resolution, first_use).data.declaration) try testing.expect_equal(BindingKind.declaration, binding(&second_resolution, second_use).kind) try testing.expect_equal(second_target, binding(&second_resolution, second_use).data.declaration) first_type :: ast.statement_decl(&first_tree, first_target).type? second_type :: ast.statement_decl(&second_tree, second_target).type? try testing.expect_equal(BindingKind.builtin, binding(&first_resolution, first_type).kind) try testing.expect_equal(BuiltinType.i64, binding(&first_resolution, first_type).data.builtin) try testing.expect_equal(BindingKind.builtin, binding(&second_resolution, second_type).kind) try testing.expect_equal(BuiltinType.bool, binding(&second_resolution, second_type).data.builtin) first_return :: ast.function_decl(&first_tree, ast.package_decl(&first_tree, 2)).return_type second_return :: ast.function_decl(&second_tree, ast.package_decl(&second_tree, 2)).return_type try testing.expect_equal(BuiltinType.bool, binding(&first_resolution, first_return).data.builtin) try testing.expect_equal(BuiltinType.i64, binding(&second_resolution, second_return).data.builtin) try testing.expect_equal(usize(0), diagnostics.count(&first_diagnostics)) try testing.expect_equal(usize(0), diagnostics.count(&second_diagnostics)) } resolves_match_capture_scopes_without_looking_up_member_names test { pool := strpool.init(mem.c_allocator) defer strpool.deinit(&pool) diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) source :: `Tag :: enum { if, else } `Value :: union(Tag) { if i32, else void } `inspect proc(value Value) void { ` match value { ` .if |payload|: { local :: payload } ` .else: { local :: 1 } ` } ` payload ` local `} scan_state := lexer.init(mem.c_allocator) defer lexer.deinit(&scan_state) try lexer.scan(&scan_state, source, &pool, &diagnostic_store) tree := try parser.parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) defer ast.deinit(&tree) try testing.expect_equal(0, diagnostics.count(&diagnostic_store)) resolution := try resolve(mem.c_allocator, &tree, &pool, &diagnostic_store) defer deinit(&resolution) try testing.expect_equal(2, diagnostics.count(&diagnostic_store)) body :: ast.function_decl(&tree, ast.package_decl(&tree, 2)).body matched :: ast.statement_expr(&tree, ast.block_statement(&tree, body, 0)) first :: ast.match_arm(&tree, matched, 0) arm :: ast.match_arm_view(&tree, first) local :: ast.statement_decl(&tree, ast.block_statement(&tree, arm.body, 0)) try testing.expect_equal(arm.capture?, binding(&resolution, local.initializer).data.declaration) member :: ast.enum_literal(&tree, ast.match_pattern(&tree, first, 0)) try testing.expect_equal(BindingKind.none, binding(&resolution, member).kind) for (usize(1)..usize(3)) |index| { reference :: ast.statement_expr(&tree, ast.block_statement(&tree, body, index)) try testing.expect_equal(BindingKind.invalid, binding(&resolution, reference).kind) } } canonicalizes_forward_function_global_and_builtin_aliases test { pool := strpool.init(mem.c_allocator) defer strpool.deinit(&pool) diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) source :: `invoke :: alias call `copy :: alias storage `Number :: alias i32 `call :: alias original `original proc() i32 { return storage } `storage i32 := 1 `main proc() Number { ` copy = invoke() ` return copy `} scan_state := lexer.init(mem.c_allocator) defer lexer.deinit(&scan_state) try lexer.scan(&scan_state, source, &pool, &diagnostic_store) tree := try parser.parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) defer ast.deinit(&tree) resolution := try resolve(mem.c_allocator, &tree, &pool, &diagnostic_store) defer deinit(&resolution) try testing.expect_equal(0, diagnostics.count(&diagnostic_store)) original :: ast.package_decl(&tree, 4) storage :: ast.package_decl(&tree, 5) main :: ast.function_decl(&tree, ast.package_decl(&tree, 6)) assignment :: ast.assignment_stmt(&tree, ast.block_statement(&tree, main.body, 0)) try testing.expect_equal(storage, binding(&resolution, assignment.target).data.declaration) try testing.expect_equal(original, binding(&resolution, ast.call_expr(&tree, assignment.value).callee).data.declaration) try testing.expect_equal(BindingKind.builtin, binding(&resolution, main.return_type).kind) try testing.expect_equal(BuiltinType.i32, binding(&resolution, main.return_type).data.builtin) try testing.expect_equal(original, binding(&resolution, ast.package_decl(&tree, 0)).data.declaration) } canonicalizes_imported_aliases_and_namespace_alias_selections test { pool := strpool.init(mem.c_allocator) defer strpool.deinit(&pool) diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) builder := ast.init_builder(mem.c_allocator, "") defer ast.deinit_builder(&builder) library_source :: "original proc() i32 { return storage }\nstorage i32 := 1\nexported :: alias original\n" library_id :: try ast.set_source(&builder, "library.hon", library_source) library_scan := lexer.init(mem.c_allocator) defer lexer.deinit(&library_scan) library_scan.source_id = library_id try lexer.scan(&library_scan, library_source, &pool, &diagnostic_store) library :: try parser.parse_source(&builder, library_scan.tokens.items, &diagnostic_store) main_source :: `lib :: import "library" `invoke :: alias namespace.exported `copy :: alias namespace.storage `namespace :: alias lib `main proc() i32 { copy = invoke() ` return copy `} main_id :: try ast.set_source(&builder, "main.hon", main_source) main_scan := lexer.init(mem.c_allocator) defer lexer.deinit(&main_scan) main_scan.source_id = main_id try lexer.scan(&main_scan, main_source, &pool, &diagnostic_store) root :: try parser.parse_source(&builder, main_scan.tokens.items, &diagnostic_store) tree := ast.finish(&builder, root) defer ast.deinit(&tree) roots [2]ast.NodeId :: [root, library] imports [1]Import :: [Import{ node = ast.package_decl_at(&tree, root, 0), name = strpool.get_id(&pool, "lib")?, target = library, }] resolution := try resolve_packages(mem.c_allocator, &tree, roots[..], imports[..], &pool, &diagnostic_store) defer deinit(&resolution) try testing.expect_equal(0, diagnostics.count(&diagnostic_store)) original :: ast.package_decl_at(&tree, library, 0) storage :: ast.package_decl_at(&tree, library, 1) main :: ast.function_decl(&tree, ast.package_decl_at(&tree, root, 4)) assignment :: ast.assignment_stmt(&tree, ast.block_statement(&tree, main.body, 0)) try testing.expect_equal(storage, binding(&resolution, assignment.target).data.declaration) try testing.expect_equal(original, binding(&resolution, ast.call_expr(&tree, assignment.value).callee).data.declaration) namespace :: ast.package_decl_at(&tree, root, 3) try testing.expect_equal(BindingKind.namespace, binding(&resolution, namespace).kind) } resolves_inline_match_captures_and_all_bodies test { pool := strpool.init(mem.c_allocator) defer strpool.deinit(&pool) diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) source :: `inspect proc(value int) void { ` match value { inline |payload, tag|: { ` payload ` tag ` } } ` match value { inline |same, same|: same } ` match value { inline |payload, value|: value } ` match value { .if: 1, inline |_|: missing } `} scan_state := lexer.init(mem.c_allocator) defer lexer.deinit(&scan_state) try lexer.scan(&scan_state, source, &pool, &diagnostic_store) tree := try parser.parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) defer ast.deinit(&tree) try testing.expect_equal(0, diagnostics.count(&diagnostic_store)) resolution := try resolve(mem.c_allocator, &tree, &pool, &diagnostic_store) defer deinit(&resolution) try testing.expect_equal(3, diagnostics.count(&diagnostic_store)) body :: ast.function_decl(&tree, ast.package_decl(&tree, 0)).body first_match :: ast.statement_expr(&tree, ast.block_statement(&tree, body, 0)) first :: ast.match_arm_view(&tree, ast.match_arm(&tree, first_match, 0)) payload_ref :: ast.statement_expr(&tree, ast.block_statement(&tree, first.body, 0)) tag_ref :: ast.statement_expr(&tree, ast.block_statement(&tree, first.body, 1)) try testing.expect_equal(first.capture?, binding(&resolution, payload_ref).data.declaration) try testing.expect_equal(first.tag_capture?, binding(&resolution, tag_ref).data.declaration) for (usize(1)..usize(3)) |i| { matched :: ast.statement_expr(&tree, ast.block_statement(&tree, body, i)) arm :: ast.match_arm_view(&tree, ast.match_arm(&tree, matched, 0)) try testing.expect_equal(BindingKind.invalid, binding(&resolution, arm.tag_capture?).kind) } last_match :: ast.statement_expr(&tree, ast.block_statement(&tree, body, 3)) last :: ast.match_arm_view(&tree, ast.match_arm(&tree, last_match, 1)) try testing.expect_equal(BindingKind.invalid, binding(&resolution, last.body).kind) }