From 675fa5049f119cdb9f854abf5ee23d744524b90a Mon Sep 17 00:00:00 2001 From: hl-valdemar Date: Sat, 24 Oct 2026 23:47:00 +0200 Subject: [PATCH] refactor: integrate compilation-owned identifier interning --- README.md | 2 + TODO.md | 1 + source/ast/ast.hon | 43 ++++- source/ast/ast.test.hon | 12 +- source/ast/renderer/renderer.test.hon | 6 +- source/lexer/scan.hon | 20 +- source/lexer/scan.test.hon | 129 ++++++++++--- source/lexer/token.hon | 12 +- source/lower/lower.test.hon | 16 +- source/main.hon | 8 +- source/parser/parse.hon | 19 +- source/parser/parse.test.hon | 235 ++++++++++++----------- source/sema/check/check.test.hon | 260 ++++++++++++-------------- source/sema/resolve.hon | 90 ++++++--- source/sema/resolve.test.hon | 142 +++++++++----- source/strpool/strpool.hon | 4 +- source/strpool/strpool.test.hon | 16 ++ 17 files changed, 626 insertions(+), 389 deletions(-) diff --git a/README.md b/README.md index 03424e1..d851c4a 100644 --- a/README.md +++ b/README.md @@ -65,6 +65,8 @@ range bounds that are compound or unary expressions require their own parenthese name resolution (`@source/sema`) runs after parsing. it predeclares package names and each block's direct locals, allowing forward references and mutually recursive functions. parameters and top-level function-body locals share a scope; each conditional arm and loop body introduces a child scope whose names do not escape it. signatures resolve names in the enclosing package scope. shadowing is forbidden, including reuse of builtin type names (`int`, `float`, `i64`, `f64`, `usize`, `bool`, `string`, `void`); sibling scopes may independently reuse names. +identifier interning is compilation-owned: initialize a `strpool.StringPool`, pass it to `lexer.scan` and `sema.resolve` before the diagnostic store, and release it after lowering. scanning interns identifier spellings once per pool; parsing preserves their IDs in compact AST storage, exposed through `ast.name_id`, while retaining source spans for diagnostics. name resolution keys scopes by those IDs and interns builtin names into the same pool, so identifier hashing and equality do not scan spelling bytes. IDs are meaningful only within their originating pool; independent compilations may assign the same numeric ID to different names. interning failures propagate as infrastructure errors rather than producing an invalid identifier ID. IR retains no pool dependency. + `sema.resolve` returns an owned binding table indexed by AST `NodeId`, without modifying the AST. `sema.binding` identifies a declaration, builtin type, or invalid binding carrying its diagnostic ID; nodes with no binding return `.none`. release the result with `sema.deinit`. unknown names and duplicate declarations add source-spanned diagnostics; invalid syntax retains its existing diagnostic identity. same-scope duplicates retain the first declaration for recovery, while forbidden inner declarations invalidate that name's uses rather than silently binding them to an outer declaration. type checking (`@source/sema/check`) runs after name resolution. `check.check` borrows the AST and binding table and returns owned per-node types and interned array descriptors; query with `check.type_of` and `check.array_type`, and release with `check.deinit`. declarations infer their initializer types, including forward references and function aliases; function types identify the declaration supplying the callable signature. annotations support builtin types and fixed arrays; type names are not ordinary values. `void` is valid as a function result, not as a parameter or stored value. diff --git a/TODO.md b/TODO.md index e69de29..efb09ba 100644 --- a/TODO.md +++ b/TODO.md @@ -0,0 +1 @@ +- [ ] stdlib: add an auto-hashmap implementation (inspired by zig) \ No newline at end of file diff --git a/source/ast/ast.hon b/source/ast/ast.hon index b13790a..f850e7c 100644 --- a/source/ast/ast.hon +++ b/source/ast/ast.hon @@ -16,6 +16,7 @@ import "@std/debug" import "@std/mem" import "@std/arraylist" import "@source/diagnostics" +import "@source/strpool" Span :: alias diagnostics.Span DiagnosticId :: alias diagnostics.DiagnosticId @@ -114,6 +115,7 @@ NodeKind :: enum { NodeData :: union { node_id NodeId extra u32 + name_id strpool.StringId diagnostic DiagnosticId mutability Mutability unary_operator UnaryOperator @@ -295,8 +297,15 @@ add_literal proc(builder @mut Builder, kind LiteralKind, span Span) NodeId ! mem return try add_node(builder, Node{ kind = node_kind }, span) } -add_identifier proc(builder @mut Builder, span Span) NodeId ! mem.AllocError { - return try add_node(builder, Node{ kind = .expr_identifier }, span) +add_identifier proc( + builder @mut Builder, + span Span, + interned_name strpool.StringId, +) NodeId ! mem.AllocError { + return try add_node(builder, Node{ + kind = .expr_identifier, + main = NodeData{ name_id = interned_name }, + }, span) } add_unary proc( @@ -475,6 +484,7 @@ add_statement_decl proc( builder @mut Builder, span Span, name Span, + interned_name strpool.StringId, type ?NodeId, mutability Mutability, initializer NodeId, @@ -488,16 +498,23 @@ add_statement_decl proc( kind = .stmt_decl, main = NodeData{ extra = extra }, data0 = NodeData{ mutability = mutability }, + data1 = NodeData{ name_id = interned_name }, }, span) } -add_param proc(builder @mut Builder, span, name Span, type NodeId) NodeId ! mem.AllocError { +add_param proc( + builder @mut Builder, + span, name Span, + interned_name strpool.StringId, + type NodeId, +) NodeId ! mem.AllocError { extra :: try add_extra(builder, name.start) _ = try add_extra(builder, name.end) return try add_node(builder, Node{ kind = .param_decl, main = NodeData{ extra = extra }, data0 = NodeData{ node_id = type }, + data1 = NodeData{ name_id = interned_name }, }, span) } @@ -505,6 +522,7 @@ add_function_decl proc( builder @mut Builder, span Span, name Span, + interned_name strpool.StringId, return_type NodeId, params []NodeId, body NodeId, @@ -513,6 +531,7 @@ add_function_decl proc( _ = try add_extra(builder, name.start) _ = try add_extra(builder, name.end) _ = try add_extra(builder, u32(return_type)) + _ = try add_extra(builder, u32(interned_name)) for (params) |parameter| _ = try add_extra(builder, u32(parameter)) return try add_node(builder, Node{ kind = .func_decl, @@ -579,6 +598,18 @@ node_count proc(tree @Tree) usize { return tree.storage.nodes.items.len } +#! returns the identifier's identity in the compilation-owned string pool. +#! accepts identifiers, declarations, and parameters; source spans remain available in typed views. +name_id proc(tree @Tree, id NodeId) strpool.StringId { + node :: get_node(&tree.storage, id) + return match node.kind { + .expr_identifier: node.main.name_id + .stmt_decl, .param_decl: node.data1.name_id + .func_decl: strpool.StringId(tree.storage.extra.items[usize(node.main.extra) + 3]) + else: unreachable + } +} + package_decl_count proc(tree @Tree) usize { root :: get_node(&tree.storage, tree.root) debug.assert(root.kind == .package) @@ -707,14 +738,14 @@ function_decl proc(tree @Tree, id NodeId) FunctionDeclView { function_param_count proc(tree @Tree, id NodeId) usize { node :: get_node(&tree.storage, id) debug.assert(node.kind == .func_decl) - return usize(node.data1.extra - node.main.extra - 3) + return usize(node.data1.extra - node.main.extra - 4) } function_param proc(tree @Tree, id NodeId, index usize) NodeId { node :: get_node(&tree.storage, id) debug.assert(node.kind == .func_decl) - debug.assert(index < usize(node.data1.extra - node.main.extra - 3)) - return NodeId(tree.storage.extra.items[usize(node.main.extra) + 3 + index]) + debug.assert(index < usize(node.data1.extra - node.main.extra - 4)) + return NodeId(tree.storage.extra.items[usize(node.main.extra) + 4 + index]) } param proc(tree @Tree, id NodeId) ParamView { diff --git a/source/ast/ast.test.hon b/source/ast/ast.test.hon index 05a6299..9c1b715 100644 --- a/source/ast/ast.test.hon +++ b/source/ast/ast.test.hon @@ -1,7 +1,11 @@ import "@std/mem" import "@std/testing" +import "@source/strpool" exposes_typed_declaration_views test { + strings := strpool.init(mem.c_allocator) + defer strpool.deinit(&strings) + value_id :: try strpool.intern(&strings, "value") builder := init_builder(mem.c_allocator, "value :: 42") defer deinit_builder(&builder) @@ -11,6 +15,7 @@ exposes_typed_declaration_views test { &builder, Span{ start = 0, end = 11 }, value_name, + value_id, null, .constant, value, @@ -23,6 +28,7 @@ exposes_typed_declaration_views test { try testing.expect_equal(1, package_decl_count(&tree)) decl :: package_decl(&tree, 0) try testing.expect_equal(DeclKind.statement, decl_kind(&tree, decl)) + try testing.expect_equal(value_id, name_id(&tree, decl)) statement :: statement_decl(&tree, decl) try testing.expect_equal(value_name.start, statement.name.start) @@ -34,11 +40,14 @@ exposes_typed_declaration_views test { } exposes_array_type_literal_and_index_views test { + strings := strpool.init(mem.c_allocator) + defer strpool.deinit(&strings) + element_id :: try strpool.intern(&strings, "int") builder := init_builder(mem.c_allocator, "") defer deinit_builder(&builder) span :: Span{ start = 0, end = 0 } count :: try add_literal(&builder, .int, span) - element :: try add_identifier(&builder, span) + element :: try add_identifier(&builder, span, element_id) row :: try add_array_type(&builder, span, count, element, false) matrix :: try add_array_type(&builder, span, null, row, true) elements [2]NodeId :: [count, element] @@ -55,6 +64,7 @@ exposes_array_type_literal_and_index_views test { try testing.expect_equal(count, array_type(&tree, row).count?) try testing.expect(!array_type(&tree, row).mutable) try testing.expect_equal(element, array_type(&tree, row).element) + try testing.expect_equal(element_id, name_id(&tree, element)) try testing.expect_equal(ExprKind.array_literal, expr_kind(&tree, literal)) try testing.expect_equal(2, array_element_count(&tree, literal)) try testing.expect_equal(count, array_element(&tree, literal, 0)) diff --git a/source/ast/renderer/renderer.test.hon b/source/ast/renderer/renderer.test.hon index 405593f..c29280d 100644 --- a/source/ast/renderer/renderer.test.hon +++ b/source/ast/renderer/renderer.test.hon @@ -7,8 +7,8 @@ import "@source/parser" import "@source/ast" renders_array_annotations_literals_and_indexed_assignments test { - strpool.strings = strpool.init(mem.c_allocator) - defer strpool.deinit(&strpool.strings) + strings := strpool.init(mem.c_allocator) + defer strpool.deinit(&strings) diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) source :: @@ -19,7 +19,7 @@ renders_array_annotations_literals_and_indexed_assignments test { `} scan_state := lexer.init(mem.c_allocator) defer lexer.deinit(&scan_state) - try lexer.scan(&scan_state, source, &diagnostic_store) + 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) renderer := init(mem.c_allocator) diff --git a/source/lexer/scan.hon b/source/lexer/scan.hon index 7646e75..e1e55d4 100644 --- a/source/lexer/scan.hon +++ b/source/lexer/scan.hon @@ -36,7 +36,7 @@ keywords strmap.StaticStringMap(TokenKind) :: strmap.init([ ]) #! owns token storage and retains its allocator; release with deinit. -#! identifier IDs refer to strings in the separately owned strpool.strings. +#! identifier IDs belong to the compilation's separately owned string pool. State :: struct { tokens arraylist.ArrayList(Token) } @@ -46,17 +46,17 @@ init proc(allocator mem.Allocator) State { return State{ tokens = arraylist.init(allocator) } } -#! releases token storage with the retained allocator, without releasing strpool.strings. +#! releases token storage with the retained allocator, without releasing identifier strings. deinit proc(state @mut State) void { arraylist.deinit(&state.tokens) } -#! borrows `program` and `diagnostic_store` during scanning; tokens remain state-owned. -#! token allocation uses the retained allocator; identifiers use strpool.strings's allocator. -#! initialize strpool.strings before scanning and keep it alive while using identifier IDs. +#! borrows `program`, `pool`, and `diagnostic_store`; tokens remain state-owned. +#! identifier storage uses the pool's allocator; keep the pool alive while using their IDs. scan proc( state @mut State, program []u8, + pool @mut strpool.StringPool, diagnostic_store @mut diagnostics.Store, ) void ! (mem.AllocError | strpool.InternError | diagnostics.CapacityError | SourceError) { if (program.len > usize(maxval!(u32))) return .source_too_large @@ -82,7 +82,7 @@ scan proc( if is_alpha(char) or char == '_' { start :: cursor - result :: scan_ident(start, program) + result :: try scan_ident(start, program, pool) try add_token(state, Token{ span = make_span(start, result.end), kind = result.kind, @@ -286,7 +286,11 @@ ScanIdentResult :: struct { } @hide:file -scan_ident proc(start usize, program []u8) ScanIdentResult { +scan_ident proc( + start usize, + program []u8, + pool @mut strpool.StringPool, +) ScanIdentResult ! (mem.AllocError | strpool.InternError) { cursor := start + 1 while ( cursor < program.len @@ -302,7 +306,7 @@ scan_ident proc(start usize, program []u8) ScanIdentResult { # don't intern keywords (already O(1) lookup via token kind) kind :: strmap.get(&keywords, program[start..cursor]) orelse .ident str_id :: if (kind == .ident) - strpool.intern(&strpool.strings, program[start..cursor]) catch strpool.no_str + try strpool.intern(pool, program[start..cursor]) else strpool.no_str return ScanIdentResult{ end = cursor, kind = kind, str_id = str_id } diff --git a/source/lexer/scan.test.hon b/source/lexer/scan.test.hon index d58bca2..5860988 100644 --- a/source/lexer/scan.test.hon +++ b/source/lexer/scan.test.hon @@ -4,14 +4,14 @@ import "@source/diagnostics" import "@source/strpool" reports_invalid_input_and_keeps_scanning test { - strpool.strings = strpool.init(mem.c_allocator) - defer strpool.deinit(&strpool.strings) + pool := strpool.init(mem.c_allocator) + defer strpool.deinit(&pool) diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) state := init(mem.c_allocator) defer deinit(&state) - try scan(&state, "if name # comment\n$1. 2 3.5 \"hi\"", &diagnostic_store) + try scan(&state, "if name # comment\n$1. 2 3.5 \"hi\"", &pool, &diagnostic_store) try testing.expect_equal(8, state.tokens.items.len) try testing.expect_equal(TokenKind.if, state.tokens.items[0].kind) @@ -25,21 +25,21 @@ reports_invalid_input_and_keeps_scanning test { try testing.expect_equal(2, diagnostics.count(&diagnostic_store)) try testing.expect_equal( "name", - strpool.get_str(&strpool.strings, state.tokens.items[1].data.str_id)?, + strpool.get_str(&pool, state.tokens.items[1].data.str_id)?, ) try testing.expect_equal(u32(3), state.tokens.items[1].span.start) try testing.expect_equal(u32(7), state.tokens.items[1].span.end) } reserves_boolean_keywords_and_scans_comparisons test { - strpool.strings = strpool.init(mem.c_allocator) - defer strpool.deinit(&strpool.strings) + pool := strpool.init(mem.c_allocator) + defer strpool.deinit(&pool) diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) state := init(mem.c_allocator) defer deinit(&state) source :: "true false bool trueish == != < <= > >=" - try scan(&state, source, &diagnostic_store) + try scan(&state, source, &pool, &diagnostic_store) expected [11]TokenKind :: [ TokenKind.true, .false, .ident, .ident, .equal_equal, .not_equal, .less, .less_equal, .greater, .greater_equal, .eof, @@ -50,29 +50,29 @@ reserves_boolean_keywords_and_scans_comparisons test { try testing.expect_equal(u32(4), state.tokens.items[0].span.end) try testing.expect_equal(u32(5), state.tokens.items[1].span.start) try testing.expect_equal(u32(10), state.tokens.items[1].span.end) - try testing.expect_equal("bool", strpool.get_str(&strpool.strings, state.tokens.items[2].data.str_id)?) + try testing.expect_equal("bool", strpool.get_str(&pool, state.tokens.items[2].data.str_id)?) try testing.expect_equal(0, diagnostics.count(&diagnostic_store)) } scans_logical_operators_without_splitting_identifiers test { - strpool.strings = strpool.init(mem.c_allocator) - defer strpool.deinit(&strpool.strings) + pool := strpool.init(mem.c_allocator) + defer strpool.deinit(&pool) diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) state := init(mem.c_allocator) defer deinit(&state) source :: "and or android orbit and_1 or2 ! != !! !true" - try scan(&state, source, &diagnostic_store) + try scan(&state, source, &pool, &diagnostic_store) expected [13]TokenKind :: [ TokenKind.and, .or, .ident, .ident, .ident, .ident, .bang, .not_equal, .bang, .bang, .bang, .true, .eof, ] try testing.expect_equal(expected.len, state.tokens.items.len) for (expected) |kind, index| try testing.expect_equal(kind, state.tokens.items[index].kind) - try testing.expect_equal("android", strpool.get_str(&strpool.strings, state.tokens.items[2].data.str_id)?) - try testing.expect_equal("orbit", strpool.get_str(&strpool.strings, state.tokens.items[3].data.str_id)?) - try testing.expect_equal("and_1", strpool.get_str(&strpool.strings, state.tokens.items[4].data.str_id)?) - try testing.expect_equal("or2", strpool.get_str(&strpool.strings, state.tokens.items[5].data.str_id)?) + try testing.expect_equal("android", strpool.get_str(&pool, state.tokens.items[2].data.str_id)?) + try testing.expect_equal("orbit", strpool.get_str(&pool, state.tokens.items[3].data.str_id)?) + try testing.expect_equal("and_1", strpool.get_str(&pool, state.tokens.items[4].data.str_id)?) + try testing.expect_equal("or2", strpool.get_str(&pool, state.tokens.items[5].data.str_id)?) try testing.expect_equal(u32(31), state.tokens.items[6].span.start) try testing.expect_equal(u32(32), state.tokens.items[6].span.end) try testing.expect_equal(u32(33), state.tokens.items[7].span.start) @@ -81,14 +81,14 @@ scans_logical_operators_without_splitting_identifiers test { } scans_loop_controls_with_exact_keyword_boundaries test { - strpool.strings = strpool.init(mem.c_allocator) - defer strpool.deinit(&strpool.strings) + pool := strpool.init(mem.c_allocator) + defer strpool.deinit(&pool) diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) state := init(mem.c_allocator) defer deinit(&state) source :: "break continue breakfast continued break_1 continue2" - try scan(&state, source, &diagnostic_store) + try scan(&state, source, &pool, &diagnostic_store) expected [7]TokenKind :: [.break, .continue, .ident, .ident, .ident, .ident, .eof] try testing.expect_equal(expected.len, state.tokens.items.len) for (expected) |kind, index| try testing.expect_equal(kind, state.tokens.items[index].kind) @@ -96,19 +96,19 @@ scans_loop_controls_with_exact_keyword_boundaries test { try testing.expect_equal(u32(5), state.tokens.items[0].span.end) try testing.expect_equal(u32(6), state.tokens.items[1].span.start) try testing.expect_equal(u32(14), state.tokens.items[1].span.end) - try testing.expect_equal("breakfast", strpool.get_str(&strpool.strings, state.tokens.items[2].data.str_id)?) - try testing.expect_equal("continued", strpool.get_str(&strpool.strings, state.tokens.items[3].data.str_id)?) + try testing.expect_equal("breakfast", strpool.get_str(&pool, state.tokens.items[2].data.str_id)?) + try testing.expect_equal("continued", strpool.get_str(&pool, state.tokens.items[3].data.str_id)?) try testing.expect_equal(0, diagnostics.count(&diagnostic_store)) } scans_ranges_without_consuming_float_dots test { - strpool.strings = strpool.init(mem.c_allocator) - defer strpool.deinit(&strpool.strings) + pool := strpool.init(mem.c_allocator) + defer strpool.deinit(&pool) diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) state := init(mem.c_allocator) defer deinit(&state) - try scan(&state, "0..10 0..=10 1.5..2.5 |@item, _| ... 1.", &diagnostic_store) + try scan(&state, "0..10 0..=10 1.5..2.5 |@item, _| ... 1.", &pool, &diagnostic_store) expected [19]TokenKind :: [ .int, .range, .int, .int, .range_inclusive, .int, .float, .range, .float, .pipe, .at, .ident, .comma, .ident, .pipe, @@ -124,13 +124,13 @@ scans_ranges_without_consuming_float_dots test { } scans_array_delimiters_and_mut_keyword test { - strpool.strings = strpool.init(mem.c_allocator) - defer strpool.deinit(&strpool.strings) + pool := strpool.init(mem.c_allocator) + defer strpool.deinit(&pool) diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) state := init(mem.c_allocator) defer deinit(&state) - try scan(&state, "[_]mut [2]i64 [1,2] [] mutable mut_1", &diagnostic_store) + try scan(&state, "[_]mut [2]i64 [1,2] [] mutable mut_1", &pool, &diagnostic_store) expected [18]TokenKind :: [ .open_bracket, .ident, .close_bracket, .mut, .open_bracket, .int, .close_bracket, .ident, @@ -141,3 +141,80 @@ scans_array_delimiters_and_mut_keyword test { for (expected) |kind, index| try testing.expect_equal(kind, state.tokens.items[index].kind) try testing.expect_equal(0, diagnostics.count(&diagnostic_store)) } + +reuses_identifiers_without_interning_keywords test { + pool := strpool.init(mem.c_allocator) + defer strpool.deinit(&pool) + diagnostic_store := diagnostics.init(mem.c_allocator) + defer diagnostics.deinit(&diagnostic_store) + state := init(mem.c_allocator) + defer deinit(&state) + try scan(&state, "alpha beta alpha if beta", &pool, &diagnostic_store) + first :: state.tokens.items[0].data.str_id + second :: state.tokens.items[1].data.str_id + try testing.expect(first != second) + try testing.expect_equal(first, state.tokens.items[2].data.str_id) + try testing.expect_equal(second, state.tokens.items[4].data.str_id) + try testing.expect_equal(null, strpool.get_id(&pool, "if")) + try testing.expect_equal(usize(2), pool.strings.items.len) +} + +@hide:file +AllocationState :: struct { + remaining usize + live usize = 0 +} + +@hide:file +limited_alloc proc(context ?@mut anyopaque, size usize, alignment usize) ?*mut u8 { + state @mut AllocationState :: ptrcast!(context?) + if (state.remaining == 0) return null + state.remaining -= 1 + memory :: mem.raw_alloc(mem.c_allocator, size, alignment) + if (memory) |_| state.live += 1 + return memory +} + +@hide:file +limited_realloc proc(context ?@mut anyopaque, memory ?*mut u8, old_size usize, new_size usize, alignment usize) ?*mut u8 { + state @mut AllocationState :: ptrcast!(context?) + if (state.remaining == 0) return null + state.remaining -= 1 + resized :: mem.raw_realloc(mem.c_allocator, memory, old_size, new_size, alignment) + if (resized) |_| { + if (memory) |_| {} else state.live += 1 + } + return resized +} + +@hide:file +limited_free proc(context ?@mut anyopaque, memory ?*mut u8, size usize, alignment usize) void { + state @mut AllocationState :: ptrcast!(context?) + if (memory) |_| state.live -= 1 + mem.raw_free(mem.c_allocator, memory, size, alignment) +} + +@hide:file +limited_vtable :: mem.AllocatorVTable{ alloc = limited_alloc, realloc = limited_realloc, free = limited_free } + +propagates_identifier_interning_allocation_failure test { + for (usize(0)..usize(3)) |remaining| { + allocation := AllocationState{ remaining = remaining } + allocator :: mem.Allocator{ context = &allocation, vtable = &limited_vtable } + pool := strpool.init(allocator) + diagnostic_store := diagnostics.init(mem.c_allocator) + defer diagnostics.deinit(&diagnostic_store) + state := init(mem.c_allocator) + defer deinit(&state) + failed := false + scan(&state, "identifier", &pool, &diagnostic_store) catch |error| { + failed = error == .out_of_memory + } + empty :: state.tokens.items.len == 0 and pool.strings.items.len == 0 + absent :: strpool.get_id(&pool, "identifier") == null + strpool.deinit(&pool) + try testing.expect(failed and empty and absent) + try testing.expect_equal(usize(0), diagnostics.count(&diagnostic_store)) + try testing.expect_equal(usize(0), allocation.live) + } +} diff --git a/source/lexer/token.hon b/source/lexer/token.hon index be503c1..fa2066e 100644 --- a/source/lexer/token.hon +++ b/source/lexer/token.hon @@ -69,16 +69,10 @@ TokenKind :: enum { render_token proc(token Token, program []u8) void { match token.kind { - .ident: { - if (strpool.get_str(&strpool.strings, token.data.str_id)) |str| { - debug.print("{}({})\n", {token.kind, str}) - } else { - debug.print("{}({})\n", {token.kind, program[token.span.start..token.span.end]}) - } - } - .string, .int, .float: + .ident, .string, .int, .float: debug.print("{}({})\n", {token.kind, program[token.span.start..token.span.end]}) - else: debug.print("{}\n", {token.kind}) + else: + debug.print("{}\n", {token.kind}) } } diff --git a/source/lower/lower.test.hon b/source/lower/lower.test.hon index e3b04e9..01306be 100644 --- a/source/lower/lower.test.hon +++ b/source/lower/lower.test.hon @@ -20,14 +20,14 @@ compile_owned proc(text []u8, diagnostic_store @mut diagnostics.Store) ir.Module source :: try mem.alloc(u8, mem.c_allocator, text.len) defer mem.free(mem.c_allocator, source) for (text) |byte, i| source[i] = byte - strpool.strings = strpool.init(mem.c_allocator) - defer strpool.deinit(&strpool.strings) + strings := strpool.init(mem.c_allocator) + defer strpool.deinit(&strings) scan_state := lexer.init(mem.c_allocator) defer lexer.deinit(&scan_state) - try lexer.scan(&scan_state, source, diagnostic_store) + 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, diagnostic_store) + resolution := try sema.resolve(mem.c_allocator, &tree, &strings, diagnostic_store) defer sema.deinit(&resolution) checked := try typecheck.check(mem.c_allocator, &tree, &resolution, diagnostic_store) defer typecheck.deinit(&checked) @@ -469,8 +469,8 @@ limited_free proc(context ?@mut anyopaque, memory ?*mut u8, size usize, alignmen limited_vtable :: mem.AllocatorVTable{ alloc = limited_alloc, realloc = limited_realloc, free = limited_free } releases_temporaries_and_partially_owned_modules_on_allocation_failure test { - strpool.strings = strpool.init(mem.c_allocator) - defer strpool.deinit(&strpool.strings) + strings := strpool.init(mem.c_allocator) + defer strpool.deinit(&strings) diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) source :: @@ -529,10 +529,10 @@ releases_temporaries_and_partially_owned_modules_on_allocation_failure test { `} scan_state := lexer.init(mem.c_allocator) defer lexer.deinit(&scan_state) - try lexer.scan(&scan_state, source, &diagnostic_store) + 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, &diagnostic_store) + resolution := try sema.resolve(mem.c_allocator, &tree, &strings, &diagnostic_store) defer sema.deinit(&resolution) checked := try typecheck.check(mem.c_allocator, &tree, &resolution, &diagnostic_store) defer typecheck.deinit(&checked) diff --git a/source/main.hon b/source/main.hon index 56f5d72..ed111ea 100644 --- a/source/main.hon +++ b/source/main.hon @@ -112,14 +112,14 @@ read_source proc(allocator mem.Allocator, runtime io.Io, path [;0]u8) arraylist. @hide:file compile proc(allocator mem.Allocator, source []u8, diagnostic_store @mut diagnostics.Store) ir.Module ! DriverError { - strpool.strings = strpool.init(allocator) - defer strpool.deinit(&strpool.strings) + strings := strpool.init(allocator) + defer strpool.deinit(&strings) scan_state := lexer.init(allocator) defer lexer.deinit(&scan_state) - try lexer.scan(&scan_state, source, diagnostic_store) + try lexer.scan(&scan_state, source, &strings, diagnostic_store) tree := try parser.parse(allocator, scan_state.tokens.items, source, diagnostic_store) defer ast.deinit(&tree) - resolution := try sema.resolve(allocator, &tree, diagnostic_store) + resolution := try sema.resolve(allocator, &tree, &strings, diagnostic_store) defer sema.deinit(&resolution) checked := try typecheck.check(allocator, &tree, &resolution, diagnostic_store) defer typecheck.deinit(&checked) diff --git a/source/parser/parse.hon b/source/parser/parse.hon index 61b61bc..73614a3 100644 --- a/source/parser/parse.hon +++ b/source/parser/parse.hon @@ -95,25 +95,26 @@ parse_func_decl proc(state @mut State) ast.NodeId ! ParseError { params arraylist.ArrayList(ast.NodeId) := arraylist.init(state.allocator) defer arraylist.deinit(¶ms) - names arraylist.ArrayList(ast.Span) := arraylist.init(state.allocator) + names arraylist.ArrayList(lexer.Token) := arraylist.init(state.allocator) defer arraylist.deinit(&names) while !next_is(state, .close_paren) { arraylist.clear(&names) while true { token :: try consume(state, .ident) - try arraylist.append(&names, token.span) + try arraylist.append(&names, token) if (next_is(state, .ident) or next_is(state, .open_bracket)) break _ = try consume(state, .comma) } type_id :: try parse_type(state) type_span :: ast.builder_span(&state.builder, type_id) - for (names.items) |name_span| { + for (names.items) |parameter_name| { param_id :: try ast.add_param( &state.builder, - ast.Span{ start = name_span.start, end = type_span.end }, - name_span, + ast.Span{ start = parameter_name.span.start, end = type_span.end }, + parameter_name.span, + parameter_name.data.str_id, type_id, ) try arraylist.append(¶ms, param_id) @@ -129,6 +130,7 @@ parse_func_decl proc(state @mut State) ast.NodeId ! ParseError { &state.builder, ast.Span{ start = name.span.start, end = body_span.end }, name.span, + name.data.str_id, return_type, params.items, body, @@ -315,7 +317,7 @@ parse_capture_name proc(state @mut State) ?ast.NodeId ! ParseError { if ( name.span.end != name.span.start + 1 or state.builder.source[usize(name.span.start)] != '_' - ) capture = try ast.add_identifier(&state.builder, name.span) + ) capture = try ast.add_identifier(&state.builder, name.span, name.data.str_id) return capture } @@ -480,6 +482,7 @@ parse_statement_decl proc(state @mut State) ast.NodeId ! ParseError { &state.builder, ast.Span{ start = name.span.start, end = end }, name.span, + name.data.str_id, type_id, mutability, initializer, @@ -511,7 +514,7 @@ parse_type proc(state @mut State) ast.NodeId ! ParseError { ) } token :: try consume(state, .ident) - return try ast.add_identifier(&state.builder, token.span) + return try ast.add_identifier(&state.builder, token.span, token.data.str_id) } @hide:file @@ -774,7 +777,7 @@ parse_primary proc(state @mut State) ast.NodeId ! ParseError { } .ident: { state.next_token += 1 - return try ast.add_identifier(&state.builder, token.span) + return try ast.add_identifier(&state.builder, token.span, token.data.str_id) } else: { diagnostic :: if (token.data.diagnostic != diagnostics.no_diagnostic) diff --git a/source/parser/parse.test.hon b/source/parser/parse.test.hon index ad080d7..0ced63a 100644 --- a/source/parser/parse.test.hon +++ b/source/parser/parse.test.hon @@ -8,14 +8,14 @@ import "@source/lexer" import "@source/ast" parses_expression_precedence_through_tree_views test { - strpool.strings = strpool.init(mem.c_allocator) - defer strpool.deinit(&strpool.strings) + 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 + 2 * 3\n" scan_state := lexer.init(mem.c_allocator) defer lexer.deinit(&scan_state) - try lexer.scan(&scan_state, source, &diagnostic_store) + try lexer.scan(&scan_state, source, &strings, &diagnostic_store) tree := try parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) defer ast.deinit(&tree) @@ -24,6 +24,7 @@ parses_expression_precedence_through_tree_views test { declaration :: ast.package_decl(&tree, 0) statement :: ast.statement_decl(&tree, declaration) try testing.expect_equal("value", ast.source_text(&tree, statement.name)) + try testing.expect_equal("value", strpool.get_str(&strings, ast.name_id(&tree, declaration))?) try testing.expect_equal(ast.ExprKind.binary, ast.expr_kind(&tree, statement.initializer)) add :: ast.binary_expr(&tree, statement.initializer) try testing.expect_equal(ast.BinaryOperator.add, add.operator) @@ -35,14 +36,14 @@ parses_expression_precedence_through_tree_views test { } parses_grouped_expression_precedence test { - strpool.strings = strpool.init(mem.c_allocator) - defer strpool.deinit(&strpool.strings) + strings := strpool.init(mem.c_allocator) + defer strpool.deinit(&strings) diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) source :: "value :: a + ((b - c)) * 2 / 3\n" scan_state := lexer.init(mem.c_allocator) defer lexer.deinit(&scan_state) - try lexer.scan(&scan_state, source, &diagnostic_store) + try lexer.scan(&scan_state, source, &strings, &diagnostic_store) tree := try parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) defer ast.deinit(&tree) @@ -66,8 +67,8 @@ parses_grouped_expression_precedence test { } parses_function_structure_through_tree_views test { - strpool.strings = strpool.init(mem.c_allocator) - defer strpool.deinit(&strpool.strings) + strings := strpool.init(mem.c_allocator) + defer strpool.deinit(&strings) diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) source :: @@ -76,7 +77,7 @@ parses_function_structure_through_tree_views test { `} scan_state := lexer.init(mem.c_allocator) defer lexer.deinit(&scan_state) - try lexer.scan(&scan_state, source, &diagnostic_store) + try lexer.scan(&scan_state, source, &strings, &diagnostic_store) tree := try parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) defer ast.deinit(&tree) @@ -85,6 +86,7 @@ parses_function_structure_through_tree_views test { try testing.expect_equal(ast.DeclKind.function, ast.decl_kind(&tree, declaration)) function :: ast.function_decl(&tree, declaration) try testing.expect_equal("grouped", ast.source_text(&tree, function.name)) + try testing.expect_equal("grouped", strpool.get_str(&strings, ast.name_id(&tree, declaration))?) try testing.expect_equal(3, ast.function_param_count(&tree, declaration)) first :: ast.param(&tree, ast.function_param(&tree, declaration, 0)) second :: ast.param(&tree, ast.function_param(&tree, declaration, 1)) @@ -92,6 +94,17 @@ parses_function_structure_through_tree_views test { try testing.expect_equal("a", ast.source_text(&tree, first.name)) try testing.expect_equal("b", ast.source_text(&tree, second.name)) try testing.expect_equal("c", ast.source_text(&tree, third.name)) + try testing.expect_equal( + "a", strpool.get_str(&strings, ast.name_id(&tree, ast.function_param(&tree, declaration, 0)))?, + ) + try testing.expect_equal( + "b", strpool.get_str(&strings, ast.name_id(&tree, ast.function_param(&tree, declaration, 1)))?, + ) + try testing.expect_equal( + "c", strpool.get_str(&strings, ast.name_id(&tree, ast.function_param(&tree, declaration, 2)))?, + ) + try testing.expect_equal("T", strpool.get_str(&strings, ast.name_id(&tree, first.type))?) + try testing.expect_equal("U", strpool.get_str(&strings, ast.name_id(&tree, third.type))?) try testing.expect_equal(first.type, second.type) try testing.expect(first.type != third.type) try testing.expect_equal(1, ast.block_statement_count(&tree, function.body)) @@ -99,14 +112,14 @@ parses_function_structure_through_tree_views test { } recovers_invalid_declaration_and_preserves_sibling test { - strpool.strings = strpool.init(mem.c_allocator) - defer strpool.deinit(&strpool.strings) + strings := strpool.init(mem.c_allocator) + defer strpool.deinit(&strings) diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) source :: "@ broken\nvalid :: 1\n" scan_state := lexer.init(mem.c_allocator) defer lexer.deinit(&scan_state) - try lexer.scan(&scan_state, source, &diagnostic_store) + try lexer.scan(&scan_state, source, &strings, &diagnostic_store) tree := try parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) defer ast.deinit(&tree) @@ -123,8 +136,8 @@ recovers_invalid_declaration_and_preserves_sibling test { } recovers_malformed_groups_and_preserves_following_statements test { - strpool.strings = strpool.init(mem.c_allocator) - defer strpool.deinit(&strpool.strings) + strings := strpool.init(mem.c_allocator) + defer strpool.deinit(&strings) diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) source :: @@ -137,7 +150,7 @@ recovers_malformed_groups_and_preserves_following_statements test { `after :: 4 scan_state := lexer.init(mem.c_allocator) defer lexer.deinit(&scan_state) - try lexer.scan(&scan_state, source, &diagnostic_store) + try lexer.scan(&scan_state, source, &strings, &diagnostic_store) tree := try parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) defer ast.deinit(&tree) @@ -162,8 +175,8 @@ recovers_malformed_groups_and_preserves_following_statements test { } parses_call_statements_and_postfix_precedence test { - strpool.strings = strpool.init(mem.c_allocator) - defer strpool.deinit(&strpool.strings) + strings := strpool.init(mem.c_allocator) + defer strpool.deinit(&strings) diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) source :: @@ -174,7 +187,7 @@ parses_call_statements_and_postfix_precedence test { `} scan_state := lexer.init(mem.c_allocator) defer lexer.deinit(&scan_state) - try lexer.scan(&scan_state, source, &diagnostic_store) + try lexer.scan(&scan_state, source, &strings, &diagnostic_store) tree := try parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) defer ast.deinit(&tree) @@ -238,8 +251,8 @@ parses_call_statements_and_postfix_precedence test { } recovers_malformed_calls_and_preserves_siblings test { - strpool.strings = strpool.init(mem.c_allocator) - defer strpool.deinit(&strpool.strings) + strings := strpool.init(mem.c_allocator) + defer strpool.deinit(&strings) diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) source :: @@ -253,7 +266,7 @@ recovers_malformed_calls_and_preserves_siblings test { `tail :: f() scan_state := lexer.init(mem.c_allocator) defer lexer.deinit(&scan_state) - try lexer.scan(&scan_state, source, &diagnostic_store) + try lexer.scan(&scan_state, source, &strings, &diagnostic_store) tree := try parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) defer ast.deinit(&tree) @@ -288,8 +301,8 @@ recovers_malformed_calls_and_preserves_siblings test { } parses_return_boundaries_and_recovers_trailing_tokens test { - strpool.strings = strpool.init(mem.c_allocator) - defer strpool.deinit(&strpool.strings) + strings := strpool.init(mem.c_allocator) + defer strpool.deinit(&strings) diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) source :: @@ -303,7 +316,7 @@ parses_return_boundaries_and_recovers_trailing_tokens test { ` return } scan_state := lexer.init(mem.c_allocator) defer lexer.deinit(&scan_state) - try lexer.scan(&scan_state, source, &diagnostic_store) + try lexer.scan(&scan_state, source, &strings, &diagnostic_store) tree := try parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) defer ast.deinit(&tree) @@ -333,15 +346,15 @@ parses_return_boundaries_and_recovers_trailing_tokens test { } parses_boolean_comparison_precedence_and_associativity test { - strpool.strings = strpool.init(mem.c_allocator) - defer strpool.deinit(&strpool.strings) + strings := strpool.init(mem.c_allocator) + defer strpool.deinit(&strings) diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) source :: `value :: -f() * 2 + 3 < 4 <= 5 > 6 >= 7 == true != false scan_state := lexer.init(mem.c_allocator) defer lexer.deinit(&scan_state) - try lexer.scan(&scan_state, source, &diagnostic_store) + try lexer.scan(&scan_state, source, &strings, &diagnostic_store) tree := try parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) defer ast.deinit(&tree) different :: ast.binary_expr(&tree, ast.statement_decl(&tree, ast.package_decl(&tree, 0)).initializer) @@ -368,8 +381,8 @@ parses_boolean_comparison_precedence_and_associativity test { } parses_factorial_and_canonical_conditional_arms test { - strpool.strings = strpool.init(mem.c_allocator) - defer strpool.deinit(&strpool.strings) + strings := strpool.init(mem.c_allocator) + defer strpool.deinit(&strings) diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) source :: @@ -385,7 +398,7 @@ parses_factorial_and_canonical_conditional_arms test { `} scan_state := lexer.init(mem.c_allocator) defer lexer.deinit(&scan_state) - try lexer.scan(&scan_state, source, &diagnostic_store) + try lexer.scan(&scan_state, source, &strings, &diagnostic_store) tree := try parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) defer ast.deinit(&tree) factorial :: ast.function_decl(&tree, ast.package_decl(&tree, 0)) @@ -410,14 +423,14 @@ parses_factorial_and_canonical_conditional_arms test { } attaches_dangling_else_to_nearest_if test { - strpool.strings = strpool.init(mem.c_allocator) - defer strpool.deinit(&strpool.strings) + 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() int { if (true) if (false) return 1 else return 2 else return 3 }" scan_state := lexer.init(mem.c_allocator) defer lexer.deinit(&scan_state) - try lexer.scan(&scan_state, source, &diagnostic_store) + try lexer.scan(&scan_state, source, &strings, &diagnostic_store) tree := try parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) defer ast.deinit(&tree) body :: ast.function_decl(&tree, ast.package_decl(&tree, 0)).body @@ -431,8 +444,8 @@ attaches_dangling_else_to_nearest_if test { } recovers_conditional_conditions_and_arms_without_losing_siblings test { - strpool.strings = strpool.init(mem.c_allocator) - defer strpool.deinit(&strpool.strings) + strings := strpool.init(mem.c_allocator) + defer strpool.deinit(&strings) diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) source :: @@ -447,7 +460,7 @@ recovers_conditional_conditions_and_arms_without_losing_siblings test { `tail :: 9 scan_state := lexer.init(mem.c_allocator) defer lexer.deinit(&scan_state) - try lexer.scan(&scan_state, source, &diagnostic_store) + try lexer.scan(&scan_state, source, &strings, &diagnostic_store) tree := try parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) defer ast.deinit(&tree) try testing.expect_equal(2, ast.package_decl_count(&tree)) @@ -475,8 +488,8 @@ recovers_conditional_conditions_and_arms_without_losing_siblings test { } recovers_invalid_guard_syntax_and_missing_arm_at_enclosing_brace test { - strpool.strings = strpool.init(mem.c_allocator) - defer strpool.deinit(&strpool.strings) + strings := strpool.init(mem.c_allocator) + defer strpool.deinit(&strings) diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) source :: @@ -488,7 +501,7 @@ recovers_invalid_guard_syntax_and_missing_arm_at_enclosing_brace test { `tail :: 4 scan_state := lexer.init(mem.c_allocator) defer lexer.deinit(&scan_state) - try lexer.scan(&scan_state, source, &diagnostic_store) + try lexer.scan(&scan_state, source, &strings, &diagnostic_store) tree := try parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) defer ast.deinit(&tree) try testing.expect_equal(2, ast.package_decl_count(&tree)) @@ -513,8 +526,8 @@ recovers_invalid_guard_syntax_and_missing_arm_at_enclosing_brace test { } parses_iterative_factorial_assignments_and_loop test { - strpool.strings = strpool.init(mem.c_allocator) - defer strpool.deinit(&strpool.strings) + strings := strpool.init(mem.c_allocator) + defer strpool.deinit(&strings) diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) source :: @@ -529,7 +542,7 @@ parses_iterative_factorial_assignments_and_loop test { `} scan_state := lexer.init(mem.c_allocator) defer lexer.deinit(&scan_state) - try lexer.scan(&scan_state, source, &diagnostic_store) + try lexer.scan(&scan_state, source, &strings, &diagnostic_store) tree := try parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) defer ast.deinit(&tree) @@ -552,8 +565,8 @@ parses_iterative_factorial_assignments_and_loop test { } parses_assignment_precedence_grouped_span_and_rhs_boundaries test { - strpool.strings = strpool.init(mem.c_allocator) - defer strpool.deinit(&strpool.strings) + strings := strpool.init(mem.c_allocator) + defer strpool.deinit(&strings) diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) source :: @@ -564,7 +577,7 @@ parses_assignment_precedence_grouped_span_and_rhs_boundaries test { ` value = 6 } scan_state := lexer.init(mem.c_allocator) defer lexer.deinit(&scan_state) - try lexer.scan(&scan_state, source, &diagnostic_store) + try lexer.scan(&scan_state, source, &strings, &diagnostic_store) tree := try parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) defer ast.deinit(&tree) @@ -590,8 +603,8 @@ parses_assignment_precedence_grouped_span_and_rhs_boundaries test { } parses_nested_loops_and_preserves_nearest_if_else test { - strpool.strings = strpool.init(mem.c_allocator) - defer strpool.deinit(&strpool.strings) + strings := strpool.init(mem.c_allocator) + defer strpool.deinit(&strings) diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) source :: @@ -604,7 +617,7 @@ parses_nested_loops_and_preserves_nearest_if_else test { `} scan_state := lexer.init(mem.c_allocator) defer lexer.deinit(&scan_state) - try lexer.scan(&scan_state, source, &diagnostic_store) + try lexer.scan(&scan_state, source, &strings, &diagnostic_store) tree := try parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) defer ast.deinit(&tree) @@ -628,8 +641,8 @@ parses_nested_loops_and_preserves_nearest_if_else test { } recovers_loop_guards_bodies_and_assignments_without_losing_braces test { - strpool.strings = strpool.init(mem.c_allocator) - defer strpool.deinit(&strpool.strings) + strings := strpool.init(mem.c_allocator) + defer strpool.deinit(&strings) diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) source :: @@ -645,7 +658,7 @@ recovers_loop_guards_bodies_and_assignments_without_losing_braces test { `tail :: 8 scan_state := lexer.init(mem.c_allocator) defer lexer.deinit(&scan_state) - try lexer.scan(&scan_state, source, &diagnostic_store) + try lexer.scan(&scan_state, source, &strings, &diagnostic_store) tree := try parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) defer ast.deinit(&tree) @@ -675,8 +688,8 @@ recovers_loop_guards_bodies_and_assignments_without_losing_braces test { } rejects_top_level_assignment_loop_else_and_assignment_expressions test { - strpool.strings = strpool.init(mem.c_allocator) - defer strpool.deinit(&strpool.strings) + strings := strpool.init(mem.c_allocator) + defer strpool.deinit(&strings) diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) source :: @@ -689,7 +702,7 @@ rejects_top_level_assignment_loop_else_and_assignment_expressions test { `tail :: 6 scan_state := lexer.init(mem.c_allocator) defer lexer.deinit(&scan_state) - try lexer.scan(&scan_state, source, &diagnostic_store) + try lexer.scan(&scan_state, source, &strings, &diagnostic_store) tree := try parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) defer ast.deinit(&tree) @@ -707,8 +720,8 @@ rejects_top_level_assignment_loop_else_and_assignment_expressions test { } recovers_nested_braces_in_malformed_loop_body_and_assignment test { - strpool.strings = strpool.init(mem.c_allocator) - defer strpool.deinit(&strpool.strings) + strings := strpool.init(mem.c_allocator) + defer strpool.deinit(&strings) diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) source :: @@ -724,7 +737,7 @@ recovers_nested_braces_in_malformed_loop_body_and_assignment test { `tail :: 8 scan_state := lexer.init(mem.c_allocator) defer lexer.deinit(&scan_state) - try lexer.scan(&scan_state, source, &diagnostic_store) + try lexer.scan(&scan_state, source, &strings, &diagnostic_store) tree := try parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) defer ast.deinit(&tree) @@ -742,14 +755,14 @@ recovers_nested_braces_in_malformed_loop_body_and_assignment test { } parses_logical_precedence_and_associativity test { - strpool.strings = strpool.init(mem.c_allocator) - defer strpool.deinit(&strpool.strings) + strings := strpool.init(mem.c_allocator) + defer strpool.deinit(&strings) diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) source :: "value :: !probe() == a + b * c < d and e and f or g or h\n" scan_state := lexer.init(mem.c_allocator) defer lexer.deinit(&scan_state) - try lexer.scan(&scan_state, source, &diagnostic_store) + try lexer.scan(&scan_state, source, &strings, &diagnostic_store) tree := try parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) defer ast.deinit(&tree) @@ -780,14 +793,14 @@ parses_logical_precedence_and_associativity test { } parses_right_associative_mixed_prefix_operators test { - strpool.strings = strpool.init(mem.c_allocator) - defer strpool.deinit(&strpool.strings) + strings := strpool.init(mem.c_allocator) + defer strpool.deinit(&strings) diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) source :: "value :: !-!probe()\nother :: -!false\n" scan_state := lexer.init(mem.c_allocator) defer lexer.deinit(&scan_state) - try lexer.scan(&scan_state, source, &diagnostic_store) + try lexer.scan(&scan_state, source, &strings, &diagnostic_store) tree := try parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) defer ast.deinit(&tree) @@ -809,8 +822,8 @@ parses_right_associative_mixed_prefix_operators test { } parses_logical_expressions_in_all_expression_positions test { - strpool.strings = strpool.init(mem.c_allocator) - defer strpool.deinit(&strpool.strings) + strings := strpool.init(mem.c_allocator) + defer strpool.deinit(&strings) diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) source :: @@ -853,7 +866,7 @@ parses_logical_expressions_in_all_expression_positions test { `} scan_state := lexer.init(mem.c_allocator) defer lexer.deinit(&scan_state) - try lexer.scan(&scan_state, source, &diagnostic_store) + try lexer.scan(&scan_state, source, &strings, &diagnostic_store) tree := try parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) defer ast.deinit(&tree) @@ -884,8 +897,8 @@ parses_logical_expressions_in_all_expression_positions test { } multiline_group_errors_preserve_statement_boundaries test { - strpool.strings = strpool.init(mem.c_allocator) - defer strpool.deinit(&strpool.strings) + strings := strpool.init(mem.c_allocator) + defer strpool.deinit(&strings) diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) source :: @@ -900,7 +913,7 @@ multiline_group_errors_preserve_statement_boundaries test { `tail :: 9 scan_state := lexer.init(mem.c_allocator) defer lexer.deinit(&scan_state) - try lexer.scan(&scan_state, source, &diagnostic_store) + try lexer.scan(&scan_state, source, &strings, &diagnostic_store) tree := try parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) defer ast.deinit(&tree) @@ -918,8 +931,8 @@ multiline_group_errors_preserve_statement_boundaries test { } recovers_missing_logical_operands_and_preserves_enclosing_nodes test { - strpool.strings = strpool.init(mem.c_allocator) - defer strpool.deinit(&strpool.strings) + strings := strpool.init(mem.c_allocator) + defer strpool.deinit(&strings) diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) source :: @@ -932,7 +945,7 @@ recovers_missing_logical_operands_and_preserves_enclosing_nodes test { `after :: true scan_state := lexer.init(mem.c_allocator) defer lexer.deinit(&scan_state) - try lexer.scan(&scan_state, source, &diagnostic_store) + try lexer.scan(&scan_state, source, &strings, &diagnostic_store) tree := try parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) defer ast.deinit(&tree) @@ -964,8 +977,8 @@ recovers_missing_logical_operands_and_preserves_enclosing_nodes test { } parses_leaf_loop_controls_and_nearest_else test { - strpool.strings = strpool.init(mem.c_allocator) - defer strpool.deinit(&strpool.strings) + strings := strpool.init(mem.c_allocator) + defer strpool.deinit(&strings) diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) source :: @@ -979,7 +992,7 @@ parses_leaf_loop_controls_and_nearest_else test { `} scan_state := lexer.init(mem.c_allocator) defer lexer.deinit(&scan_state) - try lexer.scan(&scan_state, source, &diagnostic_store) + try lexer.scan(&scan_state, source, &strings, &diagnostic_store) tree := try parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) defer ast.deinit(&tree) try testing.expect_equal(0, diagnostics.count(&diagnostic_store)) @@ -1006,8 +1019,8 @@ parses_leaf_loop_controls_and_nearest_else test { } rejects_control_tails_and_preserves_following_declarations test { - strpool.strings = strpool.init(mem.c_allocator) - defer strpool.deinit(&strpool.strings) + strings := strpool.init(mem.c_allocator) + defer strpool.deinit(&strings) diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) source :: @@ -1029,7 +1042,7 @@ rejects_control_tails_and_preserves_following_declarations test { `last :: 4 scan_state := lexer.init(mem.c_allocator) defer lexer.deinit(&scan_state) - try lexer.scan(&scan_state, source, &diagnostic_store) + try lexer.scan(&scan_state, source, &strings, &diagnostic_store) tree := try parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) defer ast.deinit(&tree) try testing.expect_equal(9, diagnostics.count(&diagnostic_store)) @@ -1057,8 +1070,8 @@ rejects_control_tails_and_preserves_following_declarations test { } preserves_control_bodies_after_malformed_guards test { - strpool.strings = strpool.init(mem.c_allocator) - defer strpool.deinit(&strpool.strings) + strings := strpool.init(mem.c_allocator) + defer strpool.deinit(&strings) diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) source :: @@ -1070,7 +1083,7 @@ preserves_control_bodies_after_malformed_guards test { `} scan_state := lexer.init(mem.c_allocator) defer lexer.deinit(&scan_state) - try lexer.scan(&scan_state, source, &diagnostic_store) + try lexer.scan(&scan_state, source, &strings, &diagnostic_store) tree := try parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) defer ast.deinit(&tree) try testing.expect_equal(2, diagnostics.count(&diagnostic_store)) @@ -1086,8 +1099,8 @@ preserves_control_bodies_after_malformed_guards test { } parses_range_endpoints_and_capture_shapes test { - strpool.strings = strpool.init(mem.c_allocator) - defer strpool.deinit(&strpool.strings) + strings := strpool.init(mem.c_allocator) + defer strpool.deinit(&strings) diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) source :: @@ -1104,7 +1117,7 @@ parses_range_endpoints_and_capture_shapes test { `} scan_state := lexer.init(mem.c_allocator) defer lexer.deinit(&scan_state) - try lexer.scan(&scan_state, source, &diagnostic_store) + try lexer.scan(&scan_state, source, &strings, &diagnostic_store) tree := try parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) defer ast.deinit(&tree) try testing.expect_equal(0, diagnostics.count(&diagnostic_store)) @@ -1119,6 +1132,9 @@ parses_range_endpoints_and_capture_shapes test { if (first.capture) |capture| { try testing.expect_equal(ast.ExprKind.identifier, ast.expr_kind(&tree, capture)) try testing.expect_equal("item", ast.source_text(&tree, ast.node_span(&tree, capture))) + try testing.expect_equal("item", strpool.get_str(&strings, ast.name_id(&tree, capture))?) + reference :: ast.statement_expr(&tree, ast.block_statement(&tree, first.body, 0)) + try testing.expect_equal(ast.name_id(&tree, capture), ast.name_id(&tree, reference)) } else return .expectation_failed second :: ast.for_stmt(&tree, ast.block_statement(&tree, body, 1)) try testing.expect(second.capture == null) @@ -1129,11 +1145,16 @@ parses_range_endpoints_and_capture_shapes test { try testing.expect(third.pointer_capture and third.index_capture and third.index == null) fourth :: ast.for_stmt(&tree, ast.block_statement(&tree, body, 3)) try testing.expect(fourth.index_capture and fourth.index != null) + try testing.expect_equal(ast.name_id(&tree, first.capture?), ast.name_id(&tree, third.capture?)) + try testing.expect_equal(ast.name_id(&tree, first.capture?), ast.name_id(&tree, fourth.capture?)) + try testing.expect_equal("index", strpool.get_str(&strings, ast.name_id(&tree, fourth.index?))?) + index_reference :: ast.statement_expr(&tree, ast.block_statement(&tree, fourth.body, 0)) + try testing.expect_equal(ast.name_id(&tree, fourth.index?), ast.name_id(&tree, index_reference)) } rejects_bro_invalid_range_headers_without_losing_siblings test { - strpool.strings = strpool.init(mem.c_allocator) - defer strpool.deinit(&strpool.strings) + strings := strpool.init(mem.c_allocator) + defer strpool.deinit(&strings) cases [13][]u8 :: [ "for -2..1 |i| {}", "for 0..limit+1 |i| {}", @@ -1159,7 +1180,7 @@ rejects_bro_invalid_range_headers_without_losing_siblings test { for ("\nkept :: 7\n}\nafter :: 9\n") |byte| try arraylist.append(&source, byte) scan_state := lexer.init(mem.c_allocator) defer lexer.deinit(&scan_state) - try lexer.scan(&scan_state, source.items, &diagnostic_store) + try lexer.scan(&scan_state, source.items, &strings, &diagnostic_store) tree := try parse(mem.c_allocator, scan_state.tokens.items, source.items, &diagnostic_store) defer ast.deinit(&tree) try testing.expect(diagnostics.count(&diagnostic_store) > 0) @@ -1175,8 +1196,8 @@ rejects_bro_invalid_range_headers_without_losing_siblings test { } range_loop_preserves_nearest_else_and_canonical_body test { - strpool.strings = strpool.init(mem.c_allocator) - defer strpool.deinit(&strpool.strings) + strings := strpool.init(mem.c_allocator) + defer strpool.deinit(&strings) diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) source :: @@ -1186,7 +1207,7 @@ range_loop_preserves_nearest_else_and_canonical_body test { `} scan_state := lexer.init(mem.c_allocator) defer lexer.deinit(&scan_state) - try lexer.scan(&scan_state, source, &diagnostic_store) + try lexer.scan(&scan_state, source, &strings, &diagnostic_store) tree := try parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) defer ast.deinit(&tree) try testing.expect_equal(0, diagnostics.count(&diagnostic_store)) @@ -1202,8 +1223,8 @@ range_loop_preserves_nearest_else_and_canonical_body test { } parses_recursive_array_annotations_literals_and_grouped_parameters test { - strpool.strings = strpool.init(mem.c_allocator) - defer strpool.deinit(&strpool.strings) + strings := strpool.init(mem.c_allocator) + defer strpool.deinit(&strings) diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) source :: @@ -1219,7 +1240,7 @@ parses_recursive_array_annotations_literals_and_grouped_parameters test { `} scan_state := lexer.init(mem.c_allocator) defer lexer.deinit(&scan_state) - try lexer.scan(&scan_state, source, &diagnostic_store) + try lexer.scan(&scan_state, source, &strings, &diagnostic_store) tree := try parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) defer ast.deinit(&tree) try testing.expect_equal(0, diagnostics.count(&diagnostic_store)) @@ -1232,6 +1253,10 @@ parses_recursive_array_annotations_literals_and_grouped_parameters test { try testing.expect(inner.mutable) try testing.expect_equal(ast.ExprKind.identifier, ast.expr_kind(&tree, inner.count?)) try testing.expect_equal("i64", ast.source_text(&tree, ast.node_span(&tree, inner.element))) + try testing.expect_equal( + ast.name_id(&tree, ast.package_decl(&tree, 3)), ast.name_id(&tree, inner.count?), + ) + try testing.expect_equal("i64", strpool.get_str(&strings, ast.name_id(&tree, inner.element))?) try testing.expect_equal(2, ast.array_element_count(&tree, declaration.initializer)) for (0..ast.array_element_count(&tree, declaration.initializer)) |i| { try testing.expect_equal(2, ast.array_element_count( @@ -1253,8 +1278,8 @@ parses_recursive_array_annotations_literals_and_grouped_parameters test { } parses_postfix_indices_and_general_assignment_targets test { - strpool.strings = strpool.init(mem.c_allocator) - defer strpool.deinit(&strpool.strings) + strings := strpool.init(mem.c_allocator) + defer strpool.deinit(&strings) diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) source :: @@ -1266,7 +1291,7 @@ parses_postfix_indices_and_general_assignment_targets test { `} scan_state := lexer.init(mem.c_allocator) defer lexer.deinit(&scan_state) - try lexer.scan(&scan_state, source, &diagnostic_store) + try lexer.scan(&scan_state, source, &strings, &diagnostic_store) tree := try parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) defer ast.deinit(&tree) try testing.expect_equal(0, diagnostics.count(&diagnostic_store)) @@ -1293,8 +1318,8 @@ parses_postfix_indices_and_general_assignment_targets test { } mirrors_array_type_and_leading_declaration_ambiguities test { - strpool.strings = strpool.init(mem.c_allocator) - defer strpool.deinit(&strpool.strings) + strings := strpool.init(mem.c_allocator) + defer strpool.deinit(&strings) diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) source :: @@ -1307,7 +1332,7 @@ mirrors_array_type_and_leading_declaration_ambiguities test { `} scan_state := lexer.init(mem.c_allocator) defer lexer.deinit(&scan_state) - try lexer.scan(&scan_state, source, &diagnostic_store) + try lexer.scan(&scan_state, source, &strings, &diagnostic_store) tree := try parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) defer ast.deinit(&tree) try testing.expect_equal(2, diagnostics.count(&diagnostic_store)) @@ -1325,8 +1350,8 @@ mirrors_array_type_and_leading_declaration_ambiguities test { } preserves_array_error_ids_control_bodies_and_following_siblings test { - strpool.strings = strpool.init(mem.c_allocator) - defer strpool.deinit(&strpool.strings) + strings := strpool.init(mem.c_allocator) + defer strpool.deinit(&strings) diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) source :: @@ -1340,7 +1365,7 @@ preserves_array_error_ids_control_bodies_and_following_siblings test { `tail :: 4 scan_state := lexer.init(mem.c_allocator) defer lexer.deinit(&scan_state) - try lexer.scan(&scan_state, source, &diagnostic_store) + try lexer.scan(&scan_state, source, &strings, &diagnostic_store) try testing.expect_equal(2, diagnostics.count(&diagnostic_store)) tree := try parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) defer ast.deinit(&tree) @@ -1370,8 +1395,8 @@ preserves_array_error_ids_control_bodies_and_following_siblings test { } recovers_nested_square_and_parenthesis_delimiters_without_consuming_siblings test { - strpool.strings = strpool.init(mem.c_allocator) - defer strpool.deinit(&strpool.strings) + strings := strpool.init(mem.c_allocator) + defer strpool.deinit(&strings) diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) source :: @@ -1385,7 +1410,7 @@ recovers_nested_square_and_parenthesis_delimiters_without_consuming_siblings tes `tail :: 5 scan_state := lexer.init(mem.c_allocator) defer lexer.deinit(&scan_state) - try lexer.scan(&scan_state, source, &diagnostic_store) + try lexer.scan(&scan_state, source, &strings, &diagnostic_store) tree := try parse(mem.c_allocator, scan_state.tokens.items, source, &diagnostic_store) defer ast.deinit(&tree) try testing.expect(diagnostics.count(&diagnostic_store) > 0) diff --git a/source/sema/check/check.test.hon b/source/sema/check/check.test.hon index a551401..bbc7d5a 100644 --- a/source/sema/check/check.test.hon +++ b/source/sema/check/check.test.hon @@ -46,8 +46,8 @@ code_count proc(diagnostic_store @diagnostics.Store, code diagnostics.Code) usiz } propagates_numeric_context_before_defaulting_forward_and_mutable_aliases test { - strpool.strings = strpool.init(mem.c_allocator) - defer strpool.deinit(&strpool.strings) + strings := strpool.init(mem.c_allocator) + defer strpool.deinit(&strings) diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) source :: @@ -66,10 +66,10 @@ propagates_numeric_context_before_defaulting_forward_and_mutable_aliases test { `nested float :: quotient / (alias + 1) scan_state := lexer.init(mem.c_allocator) defer lexer.deinit(&scan_state) - try lexer.scan(&scan_state, source, &diagnostic_store) + 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, &diagnostic_store) + 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) @@ -102,8 +102,8 @@ propagates_numeric_context_before_defaulting_forward_and_mutable_aliases test { } rejects_fixed_family_conversion_integer_division_and_conflicting_demands test { - strpool.strings = strpool.init(mem.c_allocator) - defer strpool.deinit(&strpool.strings) + strings := strpool.init(mem.c_allocator) + defer strpool.deinit(&strings) diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) source :: @@ -124,10 +124,10 @@ rejects_fixed_family_conversion_integer_division_and_conflicting_demands test { `good :: 9 scan_state := lexer.init(mem.c_allocator) defer lexer.deinit(&scan_state) - try lexer.scan(&scan_state, source, &diagnostic_store) + 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, &diagnostic_store) + 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) @@ -169,8 +169,8 @@ rejects_fixed_family_conversion_integer_division_and_conflicting_demands test { } checks_forward_alias_calls_grouped_parameters_and_mutual_recursion test { - strpool.strings = strpool.init(mem.c_allocator) - defer strpool.deinit(&strpool.strings) + strings := strpool.init(mem.c_allocator) + defer strpool.deinit(&strings) diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) source :: @@ -189,10 +189,10 @@ checks_forward_alias_calls_grouped_parameters_and_mutual_recursion test { `} scan_state := lexer.init(mem.c_allocator) defer lexer.deinit(&scan_state) - try lexer.scan(&scan_state, source, &diagnostic_store) + 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, &diagnostic_store) + 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) @@ -244,8 +244,8 @@ checks_forward_alias_calls_grouped_parameters_and_mutual_recursion test { } separates_type_annotations_from_values_and_restricts_void_storage test { - strpool.strings = strpool.init(mem.c_allocator) - defer strpool.deinit(&strpool.strings) + strings := strpool.init(mem.c_allocator) + defer strpool.deinit(&strings) diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) source :: @@ -266,10 +266,10 @@ separates_type_annotations_from_values_and_restricts_void_storage test { `good string :: "ok" scan_state := lexer.init(mem.c_allocator) defer lexer.deinit(&scan_state) - try lexer.scan(&scan_state, source, &diagnostic_store) + 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, &diagnostic_store) + 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) @@ -336,8 +336,8 @@ separates_type_annotations_from_values_and_restricts_void_storage test { } reports_nonvoid_fallthrough_without_poisoning_signatures_and_checks_unused_bodies test { - strpool.strings = strpool.init(mem.c_allocator) - defer strpool.deinit(&strpool.strings) + strings := strpool.init(mem.c_allocator) + defer strpool.deinit(&strings) diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) source :: @@ -353,10 +353,10 @@ reports_nonvoid_fallthrough_without_poisoning_signatures_and_checks_unused_bodie `} scan_state := lexer.init(mem.c_allocator) defer lexer.deinit(&scan_state) - try lexer.scan(&scan_state, source, &diagnostic_store) + 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, &diagnostic_store) + 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) @@ -391,8 +391,8 @@ reports_nonvoid_fallthrough_without_poisoning_signatures_and_checks_unused_bodie } checks_arguments_even_for_wrong_arity_noncallable_and_chained_callees test { - strpool.strings = strpool.init(mem.c_allocator) - defer strpool.deinit(&strpool.strings) + strings := strpool.init(mem.c_allocator) + defer strpool.deinit(&strings) diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) source :: @@ -410,10 +410,10 @@ checks_arguments_even_for_wrong_arity_noncallable_and_chained_callees test { `} scan_state := lexer.init(mem.c_allocator) defer lexer.deinit(&scan_state) - try lexer.scan(&scan_state, source, &diagnostic_store) + 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, &diagnostic_store) + 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) @@ -455,8 +455,8 @@ checks_arguments_even_for_wrong_arity_noncallable_and_chained_callees test { } terminates_unanchored_cycles_and_keeps_grounded_cycles_typed test { - strpool.strings = strpool.init(mem.c_allocator) - defer strpool.deinit(&strpool.strings) + strings := strpool.init(mem.c_allocator) + defer strpool.deinit(&strings) diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) source :: @@ -474,10 +474,10 @@ terminates_unanchored_cycles_and_keeps_grounded_cycles_typed test { `contextual_b :: contextual_a scan_state := lexer.init(mem.c_allocator) defer lexer.deinit(&scan_state) - try lexer.scan(&scan_state, source, &diagnostic_store) + 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, &diagnostic_store) + 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) @@ -530,8 +530,8 @@ terminates_unanchored_cycles_and_keeps_grounded_cycles_typed test { } preserves_existing_syntax_name_and_duplicate_ids_while_checking_siblings test { - strpool.strings = strpool.init(mem.c_allocator) - defer strpool.deinit(&strpool.strings) + strings := strpool.init(mem.c_allocator) + defer strpool.deinit(&strings) diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) source :: @@ -548,10 +548,10 @@ preserves_existing_syntax_name_and_duplicate_ids_while_checking_siblings test { `same :: -"initializer" scan_state := lexer.init(mem.c_allocator) defer lexer.deinit(&scan_state) - try lexer.scan(&scan_state, source, &diagnostic_store) + 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, &diagnostic_store) + 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) @@ -608,8 +608,8 @@ preserves_existing_syntax_name_and_duplicate_ids_while_checking_siblings test { } returns_supply_numeric_context_and_resolve_forward_local_and_parameter_names test { - strpool.strings = strpool.init(mem.c_allocator) - defer strpool.deinit(&strpool.strings) + strings := strpool.init(mem.c_allocator) + defer strpool.deinit(&strings) diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) source :: @@ -624,10 +624,10 @@ returns_supply_numeric_context_and_resolve_forward_local_and_parameter_names tes `seed :: 3 scan_state := lexer.init(mem.c_allocator) defer lexer.deinit(&scan_state) - try lexer.scan(&scan_state, source, &diagnostic_store) + 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, &diagnostic_store) + 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) @@ -666,8 +666,8 @@ returns_supply_numeric_context_and_resolve_forward_local_and_parameter_names tes } return_errors_preserve_identity_check_unreachable_code_and_suppress_missing_return_cascades test { - strpool.strings = strpool.init(mem.c_allocator) - defer strpool.deinit(&strpool.strings) + strings := strpool.init(mem.c_allocator) + defer strpool.deinit(&strings) diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) source :: @@ -687,10 +687,10 @@ return_errors_preserve_identity_check_unreachable_code_and_suppress_missing_retu `} scan_state := lexer.init(mem.c_allocator) defer lexer.deinit(&scan_state) - try lexer.scan(&scan_state, source, &diagnostic_store) + 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, &diagnostic_store) + 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) @@ -733,8 +733,8 @@ return_errors_preserve_identity_check_unreachable_code_and_suppress_missing_retu } checks_bool_comparisons_recursive_returns_and_nested_numeric_context test { - strpool.strings = strpool.init(mem.c_allocator) - defer strpool.deinit(&strpool.strings) + strings := strpool.init(mem.c_allocator) + defer strpool.deinit(&strings) diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) source :: @@ -769,10 +769,10 @@ checks_bool_comparisons_recursive_returns_and_nested_numeric_context test { `main proc() bool { return identity(flag) } scan_state := lexer.init(mem.c_allocator) defer lexer.deinit(&scan_state) - try lexer.scan(&scan_state, source, &diagnostic_store) + 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, &diagnostic_store) + 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) @@ -815,8 +815,8 @@ checks_bool_comparisons_recursive_returns_and_nested_numeric_context test { } rejects_bool_numeric_mixing_and_preserves_invalid_condition_identity test { - strpool.strings = strpool.init(mem.c_allocator) - defer strpool.deinit(&strpool.strings) + strings := strpool.init(mem.c_allocator) + defer strpool.deinit(&strings) diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) source :: @@ -835,10 +835,10 @@ rejects_bool_numeric_mixing_and_preserves_invalid_condition_identity test { `} scan_state := lexer.init(mem.c_allocator) defer lexer.deinit(&scan_state) - try lexer.scan(&scan_state, source, &diagnostic_store) + 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, &diagnostic_store) + 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) @@ -882,8 +882,8 @@ rejects_bool_numeric_mixing_and_preserves_invalid_condition_identity test { } analyzes_return_paths_after_trap_propagation_without_poisoning_signatures test { - strpool.strings = strpool.init(mem.c_allocator) - defer strpool.deinit(&strpool.strings) + strings := strpool.init(mem.c_allocator) + defer strpool.deinit(&strings) diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) source :: @@ -918,10 +918,10 @@ analyzes_return_paths_after_trap_propagation_without_poisoning_signatures test { `} scan_state := lexer.init(mem.c_allocator) defer lexer.deinit(&scan_state) - try lexer.scan(&scan_state, source, &diagnostic_store) + 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, &diagnostic_store) + 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) @@ -970,8 +970,8 @@ analyzes_return_paths_after_trap_propagation_without_poisoning_signatures test { } assignments_preserve_established_types_and_contextualize_numeric_values test { - strpool.strings = strpool.init(mem.c_allocator) - defer strpool.deinit(&strpool.strings) + strings := strpool.init(mem.c_allocator) + defer strpool.deinit(&strings) diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) source :: @@ -996,10 +996,10 @@ assignments_preserve_established_types_and_contextualize_numeric_values test { `} scan_state := lexer.init(mem.c_allocator) defer lexer.deinit(&scan_state) - try lexer.scan(&scan_state, source, &diagnostic_store) + 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, &diagnostic_store) + 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) @@ -1065,8 +1065,8 @@ assignments_preserve_established_types_and_contextualize_numeric_values test { } assignment_target_errors_preserve_bindings_and_independent_rhs_diagnostics test { - strpool.strings = strpool.init(mem.c_allocator) - defer strpool.deinit(&strpool.strings) + strings := strpool.init(mem.c_allocator) + defer strpool.deinit(&strings) diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) source :: @@ -1084,10 +1084,10 @@ assignment_target_errors_preserve_bindings_and_independent_rhs_diagnostics test `} scan_state := lexer.init(mem.c_allocator) defer lexer.deinit(&scan_state) - try lexer.scan(&scan_state, source, &diagnostic_store) + 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, &diagnostic_store) + 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) @@ -1124,8 +1124,8 @@ assignment_target_errors_preserve_bindings_and_independent_rhs_diagnostics test } function_assignment_checks_complete_signatures_without_retargeting_calls test { - strpool.strings = strpool.init(mem.c_allocator) - defer strpool.deinit(&strpool.strings) + strings := strpool.init(mem.c_allocator) + defer strpool.deinit(&strings) diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) source :: @@ -1145,10 +1145,10 @@ function_assignment_checks_complete_signatures_without_retargeting_calls test { `} scan_state := lexer.init(mem.c_allocator) defer lexer.deinit(&scan_state) - try lexer.scan(&scan_state, source, &diagnostic_store) + 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, &diagnostic_store) + 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) @@ -1178,8 +1178,8 @@ function_assignment_checks_complete_signatures_without_retargeting_calls test { } while_checks_nested_bodies_without_claiming_a_zero_iteration_return test { - strpool.strings = strpool.init(mem.c_allocator) - defer strpool.deinit(&strpool.strings) + strings := strpool.init(mem.c_allocator) + defer strpool.deinit(&strings) diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) source :: @@ -1203,10 +1203,10 @@ while_checks_nested_bodies_without_claiming_a_zero_iteration_return test { `} scan_state := lexer.init(mem.c_allocator) defer lexer.deinit(&scan_state) - try lexer.scan(&scan_state, source, &diagnostic_store) + 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, &diagnostic_store) + 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) @@ -1251,8 +1251,8 @@ while_checks_nested_bodies_without_claiming_a_zero_iteration_return test { } logical_types_preserve_comparison_context_and_assignment_isolation test { - strpool.strings = strpool.init(mem.c_allocator) - defer strpool.deinit(&strpool.strings) + strings := strpool.init(mem.c_allocator) + defer strpool.deinit(&strings) diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) source :: @@ -1270,10 +1270,10 @@ logical_types_preserve_comparison_context_and_assignment_isolation test { `} scan_state := lexer.init(mem.c_allocator) defer lexer.deinit(&scan_state) - try lexer.scan(&scan_state, source, &diagnostic_store) + 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, &diagnostic_store) + 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) @@ -1316,8 +1316,8 @@ logical_types_preserve_comparison_context_and_assignment_isolation test { } logical_operand_errors_are_independent_and_skipped_rhs_preserves_bool test { - strpool.strings = strpool.init(mem.c_allocator) - defer strpool.deinit(&strpool.strings) + strings := strpool.init(mem.c_allocator) + defer strpool.deinit(&strings) diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) source :: @@ -1342,10 +1342,10 @@ logical_operand_errors_are_independent_and_skipped_rhs_preserves_bool test { `} scan_state := lexer.init(mem.c_allocator) defer lexer.deinit(&scan_state) - try lexer.scan(&scan_state, source, &diagnostic_store) + 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, &diagnostic_store) + 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) @@ -1412,8 +1412,8 @@ logical_operand_errors_are_independent_and_skipped_rhs_preserves_bool test { } logical_external_mismatches_and_always_demanded_not_preserve_errors test { - strpool.strings = strpool.init(mem.c_allocator) - defer strpool.deinit(&strpool.strings) + strings := strpool.init(mem.c_allocator) + defer strpool.deinit(&strings) diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) source :: @@ -1435,10 +1435,10 @@ logical_external_mismatches_and_always_demanded_not_preserve_errors test { `} scan_state := lexer.init(mem.c_allocator) defer lexer.deinit(&scan_state) - try lexer.scan(&scan_state, source, &diagnostic_store) + 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, &diagnostic_store) + 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) @@ -1491,8 +1491,8 @@ logical_external_mismatches_and_always_demanded_not_preserve_errors test { } short_circuit_skip_paths_do_not_guarantee_a_return test { - strpool.strings = strpool.init(mem.c_allocator) - defer strpool.deinit(&strpool.strings) + strings := strpool.init(mem.c_allocator) + defer strpool.deinit(&strings) diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) source :: @@ -1514,10 +1514,10 @@ short_circuit_skip_paths_do_not_guarantee_a_return test { `take proc(value bool) void {} scan_state := lexer.init(mem.c_allocator) defer lexer.deinit(&scan_state) - try lexer.scan(&scan_state, source, &diagnostic_store) + 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, &diagnostic_store) + 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) @@ -1550,8 +1550,8 @@ short_circuit_skip_paths_do_not_guarantee_a_return test { } loop_controls_follow_lexical_bodies_and_check_unreachable_code test { - strpool.strings = strpool.init(mem.c_allocator) - defer strpool.deinit(&strpool.strings) + strings := strpool.init(mem.c_allocator) + defer strpool.deinit(&strings) diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) source :: @@ -1583,10 +1583,10 @@ loop_controls_follow_lexical_bodies_and_check_unreachable_code test { `} scan_state := lexer.init(mem.c_allocator) defer lexer.deinit(&scan_state) - try lexer.scan(&scan_state, source, &diagnostic_store) + 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, &diagnostic_store) + 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) @@ -1639,8 +1639,8 @@ loop_controls_follow_lexical_bodies_and_check_unreachable_code test { } loop_controls_do_not_guarantee_function_returns test { - strpool.strings = strpool.init(mem.c_allocator) - defer strpool.deinit(&strpool.strings) + strings := strpool.init(mem.c_allocator) + defer strpool.deinit(&strings) diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) source :: @@ -1661,10 +1661,10 @@ loop_controls_do_not_guarantee_function_returns test { `invalid proc() int { break } scan_state := lexer.init(mem.c_allocator) defer lexer.deinit(&scan_state) - try lexer.scan(&scan_state, source, &diagnostic_store) + 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, &diagnostic_store) + 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) @@ -1694,8 +1694,8 @@ loop_controls_do_not_guarantee_function_returns test { } range_bounds_receive_integer_context_without_joining_the_range_or_capture test { - strpool.strings = strpool.init(mem.c_allocator) - defer strpool.deinit(&strpool.strings) + strings := strpool.init(mem.c_allocator) + defer strpool.deinit(&strings) diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) source :: @@ -1714,10 +1714,10 @@ range_bounds_receive_integer_context_without_joining_the_range_or_capture test { `} scan_state := lexer.init(mem.c_allocator) defer lexer.deinit(&scan_state) - try lexer.scan(&scan_state, source, &diagnostic_store) + 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, &diagnostic_store) + 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) @@ -1753,8 +1753,8 @@ range_bounds_receive_integer_context_without_joining_the_range_or_capture test { } range_checks_both_bounds_and_preserves_fixed_declarations_and_source_errors test { - strpool.strings = strpool.init(mem.c_allocator) - defer strpool.deinit(&strpool.strings) + strings := strpool.init(mem.c_allocator) + defer strpool.deinit(&strings) diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) source :: @@ -1776,10 +1776,10 @@ range_checks_both_bounds_and_preserves_fixed_declarations_and_source_errors test `flexible := 3 scan_state := lexer.init(mem.c_allocator) defer lexer.deinit(&scan_state) - try lexer.scan(&scan_state, source, &diagnostic_store) + 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, &diagnostic_store) + 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) @@ -1826,8 +1826,8 @@ range_checks_both_bounds_and_preserves_fixed_declarations_and_source_errors test } range_values_nonrange_iterables_and_capture_modes_have_explicit_errors test { - strpool.strings = strpool.init(mem.c_allocator) - defer strpool.deinit(&strpool.strings) + strings := strpool.init(mem.c_allocator) + defer strpool.deinit(&strings) diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) source :: @@ -1851,10 +1851,10 @@ range_values_nonrange_iterables_and_capture_modes_have_explicit_errors test { `} scan_state := lexer.init(mem.c_allocator) defer lexer.deinit(&scan_state) - try lexer.scan(&scan_state, source, &diagnostic_store) + 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, &diagnostic_store) + 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) @@ -1901,8 +1901,8 @@ range_values_nonrange_iterables_and_capture_modes_have_explicit_errors test { } range_captures_are_fixed_readonly_and_assignment_values_do_not_retarget_bounds test { - strpool.strings = strpool.init(mem.c_allocator) - defer strpool.deinit(&strpool.strings) + strings := strpool.init(mem.c_allocator) + defer strpool.deinit(&strings) diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) source :: @@ -1927,10 +1927,10 @@ range_captures_are_fixed_readonly_and_assignment_values_do_not_retarget_bounds t `} scan_state := lexer.init(mem.c_allocator) defer lexer.deinit(&scan_state) - try lexer.scan(&scan_state, source, &diagnostic_store) + 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, &diagnostic_store) + 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) @@ -1986,8 +1986,8 @@ range_captures_are_fixed_readonly_and_assignment_values_do_not_retarget_bounds t } range_loop_return_flow_and_controls_are_lexical_and_body_errors_are_isolated test { - strpool.strings = strpool.init(mem.c_allocator) - defer strpool.deinit(&strpool.strings) + strings := strpool.init(mem.c_allocator) + defer strpool.deinit(&strings) diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) source :: @@ -2025,10 +2025,10 @@ range_loop_return_flow_and_controls_are_lexical_and_body_errors_are_isolated tes `} scan_state := lexer.init(mem.c_allocator) defer lexer.deinit(&scan_state) - try lexer.scan(&scan_state, source, &diagnostic_store) + 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, &diagnostic_store) + 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) @@ -2080,8 +2080,8 @@ range_loop_return_flow_and_controls_are_lexical_and_body_errors_are_isolated tes } invalid_range_capture_bindings_keep_resolver_diagnostic_ids_without_erasing_headers test { - strpool.strings = strpool.init(mem.c_allocator) - defer strpool.deinit(&strpool.strings) + strings := strpool.init(mem.c_allocator) + defer strpool.deinit(&strings) diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) source :: @@ -2094,10 +2094,10 @@ invalid_range_capture_bindings_keep_resolver_diagnostic_ids_without_erasing_head `} scan_state := lexer.init(mem.c_allocator) defer lexer.deinit(&scan_state) - try lexer.scan(&scan_state, source, &diagnostic_store) + 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, &diagnostic_store) + 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) @@ -2132,6 +2132,7 @@ invalid_range_capture_bindings_keep_resolver_diagnostic_ids_without_erasing_head @hide:file ArrayFixture :: struct { + strings strpool.StringPool tree ast.Tree resolution sema.Resolution diagnostics diagnostics.Store @@ -2140,17 +2141,19 @@ ArrayFixture :: struct { @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, &store) + 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, &store) + 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{ tree = tree, resolution = resolution, diagnostics = store, result = result } + return ArrayFixture{ strings = strings, tree = tree, resolution = resolution, diagnostics = store, result = result } } @hide:file @@ -2159,6 +2162,7 @@ deinit_array_fixture proc(fixture @mut ArrayFixture) void { sema.deinit(&fixture.resolution) ast.deinit(&fixture.tree) diagnostics.deinit(&fixture.diagnostics) + strpool.deinit(&fixture.strings) } @hide:file @@ -2172,8 +2176,6 @@ expect_array proc(result @Result, id ast.NodeId, len usize, mutable bool, elemen } fixed_arrays_contextualize_forward_aliases_counts_nested_literals_and_signatures test { - strpool.strings = strpool.init(mem.c_allocator) - defer strpool.deinit(&strpool.strings) source :: `context [2]f64 :: alias `alias :: seed @@ -2225,8 +2227,6 @@ fixed_arrays_contextualize_forward_aliases_counts_nested_literals_and_signatures } nested_array_numeric_context_flows_through_index_children_before_defaults test { - strpool.strings = strpool.init(mem.c_allocator) - defer strpool.deinit(&strpool.strings) source :: `first float :: later[0][0] `later := [[1, 2], [3, 4]] @@ -2262,8 +2262,6 @@ nested_array_numeric_context_flows_through_index_children_before_defaults test { } fixed_index_errors_are_occurrence_local_and_preserve_sources_and_siblings test { - strpool.strings = strpool.init(mem.c_allocator) - defer strpool.deinit(&strpool.strings) source :: `fixed i64 :: 0 `inferred := 0 @@ -2298,8 +2296,6 @@ fixed_index_errors_are_occurrence_local_and_preserve_sources_and_siblings test { } array_lengths_require_supported_constexpr_and_finalize_nested_inferred_counts test { - strpool.strings = strpool.init(mem.c_allocator) - defer strpool.deinit(&strpool.strings) source :: `two :: tail `tail :: 2 @@ -2355,8 +2351,6 @@ array_lengths_require_supported_constexpr_and_finalize_nested_inferred_counts te } indexed_writes_require_mutable_local_root_and_each_array_level_but_copies_are_values test { - strpool.strings = strpool.init(mem.c_allocator) - defer strpool.deinit(&strpool.strings) source :: `global [1]mut i64 := [1] `main proc(parameter [1]mut i64) void { @@ -2408,8 +2402,6 @@ indexed_writes_require_mutable_local_root_and_each_array_level_but_copies_are_va } usize_arithmetic_ranges_and_readonly_captures_keep_the_bound_family test { - strpool.strings = strpool.init(mem.c_allocator) - defer strpool.deinit(&strpool.strings) source :: `limit usize :: 18446744073709551615 `arithmetic usize :: (limit - 2) * 1 + 1 @@ -2470,8 +2462,6 @@ usize_arithmetic_ranges_and_readonly_captures_keep_the_bound_family test { } array_assignment_rhs_is_contextual_but_does_not_retarget_established_sources test { - strpool.strings = strpool.init(mem.c_allocator) - defer strpool.deinit(&strpool.strings) source :: `main proc() void { ` target [2]mut f64 := [1, 2] @@ -2513,8 +2503,6 @@ array_assignment_rhs_is_contextual_but_does_not_retarget_established_sources tes } array_signature_identity_includes_recursive_counts_and_mutability test { - strpool.strings = strpool.init(mem.c_allocator) - defer strpool.deinit(&strpool.strings) source :: `accept proc(value [1]mut i64) [1]mut i64 { return value } `different_count proc(value [2]mut i64) [1]mut i64 { return [1] } @@ -2559,8 +2547,6 @@ array_signature_identity_includes_recursive_counts_and_mutability test { } inferred_counts_follow_forward_fixed_aliases_empty_shapes_and_reject_local_counts test { - strpool.strings = strpool.init(mem.c_allocator) - defer strpool.deinit(&strpool.strings) source :: `forward [_]i64 :: later `later [_]i64 :: [1, 2] diff --git a/source/sema/resolve.hon b/source/sema/resolve.hon index d994d12..dc8983f 100644 --- a/source/sema/resolve.hon +++ b/source/sema/resolve.hon @@ -1,7 +1,7 @@ import "@std/debug" import "@std/mem" import "@std/hashmap" -import "@std/strmap" +import "@source/strpool" import "@source/ast" import "@source/diagnostics" @@ -14,16 +14,22 @@ BuiltinType :: enum { } @hide:file -builtins strmap.StaticStringMap(BuiltinType) :: strmap.init([ - { "int", .int }, - { "float", .float }, - { "bool", .bool }, - { "string", .string }, - { "void", .void }, - { "i64", .i64 }, - { "f64", .f64 }, - { "usize", .usize }, -]) +Builtin :: struct { + name []u8 + type BuiltinType +} + +@hide:file +builtins [8]Builtin :: [ + { name = "int", type = .int }, + { name = "float", type = .float }, + { name = "bool", type = .bool }, + { name = "string", type = .string }, + { name = "void", type = .void }, + { name = "i64", type = .i64 }, + { name = "f64", type = .f64 }, + { name = "usize", type = .usize }, +] BindingKind :: enum { none, declaration, builtin, invalid } @@ -50,7 +56,7 @@ Resolution :: struct { storage Storage } @hide:file Scope :: struct { - names hashmap.StringHashMap(Binding) + names hashmap.HashMap(strpool.StringId, Binding, name_hash, names_equal) parent ?@Scope } @@ -59,17 +65,19 @@ State :: struct { tree @ast.Tree diagnostics @mut diagnostics.Store bindings []mut Binding + builtin_ids [8]mut strpool.StringId = [0, 0, 0, 0, 0, 0, 0, 0] allocator mem.Allocator } -#! borrows `tree` and `diagnostic_store` during resolution, without retaining their storage. +#! borrows `tree`, its identifier `pool`, and `diagnostic_store` during resolution. #! `allocator` supplies caller-owned bindings and temporary scopes released before return. #! release the returned Resolution with deinit, which uses the retained allocator. resolve proc( allocator mem.Allocator, tree @ast.Tree, + pool @mut strpool.StringPool, diagnostic_store @mut diagnostics.Store, -) Resolution ! (mem.AllocError | diagnostics.CapacityError) { +) Resolution ! (mem.AllocError | strpool.InternError | diagnostics.CapacityError) { bindings :: try mem.alloc(Binding, allocator, ast.node_count(tree)) errdefer mem.free(allocator, bindings) mem.set(bindings, Binding{}) @@ -81,7 +89,13 @@ resolve proc( allocator = allocator, } - package_scope := Scope{ names = hashmap.init(allocator), parent = null } + for (builtins) |builtin, index| { + state.builtin_ids[index] = try strpool.intern(pool, builtin.name) + } + package_scope := Scope{ + names = hashmap.init(strpool.StringId, Binding, name_hash, names_equal, allocator), + parent = null, + } defer hashmap.deinit(&package_scope.names) for (0..ast.package_decl_count(tree)) |i| { id :: ast.package_decl(tree, i) @@ -115,19 +129,22 @@ binding proc(resolution @Resolution, id ast.NodeId) Binding { } @hide:file -lookup proc(scope ?@Scope, name []u8) ?Binding { +lookup proc(state @State, scope ?@Scope, name strpool.StringId) ?Binding { if scope |current| { if hashmap.get(¤t.names, name) |found| return found - return lookup(current.parent, name) + return lookup(state, current.parent, name) } - if strmap.get(&builtins, name) |type| { - return Binding{ kind = .builtin, data = BindingData{ builtin = type } } + for (state.builtin_ids) |id, index| { + if (id == name) return Binding{ + kind = .builtin, + data = BindingData{ builtin = builtins[index].type }, + } } return null } @hide:file -put_name proc(scope @mut Scope, name []u8, value Binding) void ! mem.AllocError { +put_name proc(scope @mut Scope, name strpool.StringId, value Binding) void ! mem.AllocError { hashmap.put(&scope.names, name, value) catch |err| { match err { .key_exists: unreachable @@ -143,7 +160,7 @@ predeclare proc( id ast.NodeId, name_span ast.Span, ) void ! (mem.AllocError | diagnostics.CapacityError) { - name :: ast.source_text(state.tree, name_span) + name :: ast.name_id(state.tree, id) if hashmap.get(&scope.names, name) |_| { diagnostic :: try diagnostics.add(state.diagnostics, .duplicate_name, name_span) state.bindings[usize(id)] = Binding{ @@ -154,7 +171,7 @@ predeclare proc( } value := Binding{ kind = .declaration, data = BindingData{ declaration = id } } - if lookup(scope.parent, name) |_| { + if lookup(state, scope.parent, name) |_| { diagnostic :: try diagnostics.add(state.diagnostics, .duplicate_name, name_span) # poison the inner name rather than silently binding uses to its ancestor. value = Binding{ kind = .invalid, data = BindingData{ diagnostic = diagnostic } } @@ -178,7 +195,10 @@ resolve_function proc( try resolve_expr(state, package_scope, ast.param(tree, parameter).type) } - scope := Scope{ names = hashmap.init(state.allocator), parent = package_scope } + scope := Scope{ + names = hashmap.init(strpool.StringId, Binding, name_hash, names_equal, state.allocator), + parent = package_scope, + } defer hashmap.deinit(&scope.names) for (0..ast.function_param_count(tree, id)) |i| { parameter :: ast.function_param(tree, id, i) @@ -189,7 +209,10 @@ resolve_function proc( @hide:file resolve_arm proc(state @mut State, parent @Scope, id ast.NodeId) void ! (mem.AllocError | diagnostics.CapacityError) { - scope := Scope{ names = hashmap.init(state.allocator), parent = parent } + scope := Scope{ + names = hashmap.init(strpool.StringId, Binding, name_hash, names_equal, state.allocator), + parent = parent, + } defer hashmap.deinit(&scope.names) try resolve_block(state, &scope, id) } @@ -231,7 +254,10 @@ resolve_block proc(state @mut State, scope @mut Scope, id ast.NodeId) void ! (me .for_stmt: { loop :: ast.for_stmt(tree, statement) try resolve_expr(state, scope, loop.iterable) - body_scope := Scope{ names = hashmap.init(state.allocator), parent = scope } + body_scope := Scope{ + names = hashmap.init(strpool.StringId, Binding, name_hash, names_equal, state.allocator), + parent = scope, + } defer hashmap.deinit(&body_scope.names) if (loop.capture) |capture| { try predeclare(state, &body_scope, capture, ast.node_span(tree, capture)) @@ -279,8 +305,8 @@ resolve_expr proc( .literal_int, .literal_float, .literal_bool, .literal_string: {} .identifier: { span :: ast.node_span(tree, id) - name :: ast.source_text(tree, span) - if lookup(scope, name) |found| { + name :: ast.name_id(tree, id) + if lookup(state, scope, name) |found| { state.bindings[usize(id)] = found } else { diagnostic :: try diagnostics.add(state.diagnostics, .unknown_name, span) @@ -325,3 +351,13 @@ resolve_expr proc( .invalid: resolve_invalid(state, id) } } + +@hide:file +name_hash proc(name strpool.StringId) usize { + return usize(name) +} + +@hide:file +names_equal proc(left, right strpool.StringId) bool { + return left == right +} diff --git a/source/sema/resolve.test.hon b/source/sema/resolve.test.hon index 92e0492..218b82b 100644 --- a/source/sema/resolve.test.hon +++ b/source/sema/resolve.test.hon @@ -8,8 +8,8 @@ import "@source/parser" import "@source/ast" resolves_forward_references_recursion_and_builtin_signatures test { - strpool.strings = strpool.init(mem.c_allocator) - defer strpool.deinit(&strpool.strings) + pool := strpool.init(mem.c_allocator) + defer strpool.deinit(&pool) diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) source :: @@ -25,10 +25,10 @@ resolves_forward_references_recursion_and_builtin_signatures test { `later int :: 1 scan_state := lexer.init(mem.c_allocator) defer lexer.deinit(&scan_state) - try lexer.scan(&scan_state, source, &diagnostic_store) + 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, &diagnostic_store) + resolution := try resolve(mem.c_allocator, &tree, &pool, &diagnostic_store) defer deinit(&resolution) first :: ast.package_decl(&tree, 0) @@ -110,8 +110,8 @@ resolves_forward_references_recursion_and_builtin_signatures test { } rejects_shadowing_and_duplicates_without_cascades test { - strpool.strings = strpool.init(mem.c_allocator) - defer strpool.deinit(&strpool.strings) + pool := strpool.init(mem.c_allocator) + defer strpool.deinit(&pool) diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) source :: @@ -132,11 +132,11 @@ rejects_shadowing_and_duplicates_without_cascades test { `same :: same scan_state := lexer.init(mem.c_allocator) defer lexer.deinit(&scan_state) - try lexer.scan(&scan_state, source, &diagnostic_store) + 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, &diagnostic_store) + resolution := try resolve(mem.c_allocator, &tree, &pool, &diagnostic_store) defer deinit(&resolution) function :: ast.package_decl(&tree, 0) @@ -228,8 +228,8 @@ rejects_shadowing_and_duplicates_without_cascades test { } preserves_syntax_diagnostics_and_resolves_shared_unknown_types_once test { - strpool.strings = strpool.init(mem.c_allocator) - defer strpool.deinit(&strpool.strings) + pool := strpool.init(mem.c_allocator) + defer strpool.deinit(&pool) diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) source :: @@ -243,7 +243,7 @@ preserves_syntax_diagnostics_and_resolves_shared_unknown_types_once test { `} scan_state := lexer.init(mem.c_allocator) defer lexer.deinit(&scan_state) - try lexer.scan(&scan_state, source, &diagnostic_store) + 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) @@ -255,7 +255,7 @@ preserves_syntax_diagnostics_and_resolves_shared_unknown_types_once test { 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, &diagnostic_store) + resolution := try resolve(mem.c_allocator, &tree, &pool, &diagnostic_store) defer deinit(&resolution) invalid_nodes [3]ast.NodeId :: [invalid_decl, empty, invalid_statement] @@ -300,8 +300,8 @@ preserves_syntax_diagnostics_and_resolves_shared_unknown_types_once test { } resolves_conditional_arm_scopes_without_leaks_or_shadowing test { - strpool.strings = strpool.init(mem.c_allocator) - defer strpool.deinit(&strpool.strings) + pool := strpool.init(mem.c_allocator) + defer strpool.deinit(&pool) diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) source :: @@ -323,11 +323,11 @@ resolves_conditional_arm_scopes_without_leaks_or_shadowing test { `} scan_state := lexer.init(mem.c_allocator) defer lexer.deinit(&scan_state) - try lexer.scan(&scan_state, source, &diagnostic_store) + 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, &diagnostic_store) + resolution := try resolve(mem.c_allocator, &tree, &pool, &diagnostic_store) defer deinit(&resolution) function :: ast.package_decl(&tree, 0) @@ -368,8 +368,8 @@ resolves_conditional_arm_scopes_without_leaks_or_shadowing test { } resolves_loop_assignments_in_lexical_scopes_with_forward_local_bindings test { - strpool.strings = strpool.init(mem.c_allocator) - defer strpool.deinit(&strpool.strings) + pool := strpool.init(mem.c_allocator) + defer strpool.deinit(&pool) diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) source :: @@ -394,11 +394,11 @@ resolves_loop_assignments_in_lexical_scopes_with_forward_local_bindings test { `} scan_state := lexer.init(mem.c_allocator) defer lexer.deinit(&scan_state) - try lexer.scan(&scan_state, source, &diagnostic_store) + 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, &diagnostic_store) + resolution := try resolve(mem.c_allocator, &tree, &pool, &diagnostic_store) defer deinit(&resolution) function :: ast.package_decl(&tree, 0) @@ -441,8 +441,8 @@ resolves_loop_assignments_in_lexical_scopes_with_forward_local_bindings test { } resolves_all_code_and_preserves_scopes_after_loop_controls test { - strpool.strings = strpool.init(mem.c_allocator) - defer strpool.deinit(&strpool.strings) + pool := strpool.init(mem.c_allocator) + defer strpool.deinit(&pool) diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) source :: @@ -464,11 +464,11 @@ resolves_all_code_and_preserves_scopes_after_loop_controls test { `} scan_state := lexer.init(mem.c_allocator) defer lexer.deinit(&scan_state) - try lexer.scan(&scan_state, source, &diagnostic_store) + 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, &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 @@ -487,8 +487,8 @@ resolves_all_code_and_preserves_scopes_after_loop_controls test { } range_bounds_use_outer_scope_and_captures_are_body_declarations test { - strpool.strings = strpool.init(mem.c_allocator) - defer strpool.deinit(&strpool.strings) + pool := strpool.init(mem.c_allocator) + defer strpool.deinit(&pool) diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) source :: @@ -506,10 +506,10 @@ range_bounds_use_outer_scope_and_captures_are_body_declarations test { `} scan_state := lexer.init(mem.c_allocator) defer lexer.deinit(&scan_state) - try lexer.scan(&scan_state, source, &diagnostic_store) + 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, &diagnostic_store) + 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)) @@ -540,8 +540,8 @@ range_bounds_use_outer_scope_and_captures_are_body_declarations test { } range_captures_preserve_duplicate_builtin_and_discard_errors test { - strpool.strings = strpool.init(mem.c_allocator) - defer strpool.deinit(&strpool.strings) + pool := strpool.init(mem.c_allocator) + defer strpool.deinit(&pool) diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) source :: @@ -563,10 +563,10 @@ range_captures_preserve_duplicate_builtin_and_discard_errors test { `} scan_state := lexer.init(mem.c_allocator) defer lexer.deinit(&scan_state) - try lexer.scan(&scan_state, source, &diagnostic_store) + 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, &diagnostic_store) + 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| { @@ -605,8 +605,8 @@ range_captures_preserve_duplicate_builtin_and_discard_errors test { } resolves_array_counts_elements_indices_and_unreachable_targets test { - strpool.strings = strpool.init(mem.c_allocator) - defer strpool.deinit(&strpool.strings) + pool := strpool.init(mem.c_allocator) + defer strpool.deinit(&pool) diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) source :: @@ -621,10 +621,10 @@ resolves_array_counts_elements_indices_and_unreachable_targets test { `} scan_state := lexer.init(mem.c_allocator) defer lexer.deinit(&scan_state) - try lexer.scan(&scan_state, source, &diagnostic_store) + 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, &diagnostic_store) + 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) @@ -669,8 +669,8 @@ resolves_array_counts_elements_indices_and_unreachable_targets test { } array_signature_counts_use_package_scope_and_local_counts_see_parameters test { - strpool.strings = strpool.init(mem.c_allocator) - defer strpool.deinit(&strpool.strings) + pool := strpool.init(mem.c_allocator) + defer strpool.deinit(&pool) diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) source :: @@ -682,11 +682,11 @@ array_signature_counts_use_package_scope_and_local_counts_see_parameters test { `} scan_state := lexer.init(mem.c_allocator) defer lexer.deinit(&scan_state) - try lexer.scan(&scan_state, source, &diagnostic_store) + 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, &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) @@ -710,8 +710,8 @@ array_signature_counts_use_package_scope_and_local_counts_see_parameters test { } resolves_every_array_child_error_and_preserves_lexer_diagnostics test { - strpool.strings = strpool.init(mem.c_allocator) - defer strpool.deinit(&strpool.strings) + pool := strpool.init(mem.c_allocator) + defer strpool.deinit(&pool) diagnostic_store := diagnostics.init(mem.c_allocator) defer diagnostics.deinit(&diagnostic_store) source :: @@ -723,11 +723,11 @@ resolves_every_array_child_error_and_preserves_lexer_diagnostics test { `} scan_state := lexer.init(mem.c_allocator) defer lexer.deinit(&scan_state) - try lexer.scan(&scan_state, source, &diagnostic_store) + 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, &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 @@ -755,3 +755,57 @@ resolves_every_array_child_error_and_preserves_lexer_diagnostics test { 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)) +} diff --git a/source/strpool/strpool.hon b/source/strpool/strpool.hon index 6fd74cb..0e25488 100644 --- a/source/strpool/strpool.hon +++ b/source/strpool/strpool.hon @@ -4,13 +4,11 @@ import "@std/hashmap" InternError :: enum { out_of_space } +#! identifies a string only within its owning pool. StringId :: alias u32 no_str :: maxval!(StringId) -#! global pool owned by main.hon; initialize before scanning and release with deinit. -strings StringPool := undefined - #! retains its allocator and owns interned copies; release all storage with deinit. StringPool :: struct { ids hashmap.StringHashMap(StringId) # string → id diff --git a/source/strpool/strpool.test.hon b/source/strpool/strpool.test.hon index cb5102e..cb34ab6 100644 --- a/source/strpool/strpool.test.hon +++ b/source/strpool/strpool.test.hon @@ -22,3 +22,19 @@ handles_interning test { try testing.expect_equal(null, invalid_str) try testing.expect_equal("hello", hello_str?) } + +keeps_independent_pool_ids_local test { + first := init(mem.c_allocator) + defer deinit(&first) + second := init(mem.c_allocator) + defer deinit(&second) + hello :: try intern(&first, "hello") + world :: try intern(&second, "world") + try testing.expect_equal(hello, world) + try testing.expect_equal("hello", get_str(&first, hello)?) + try testing.expect_equal("world", get_str(&second, world)?) + different :: try intern(&first, "world") + try testing.expect(different != hello) + try testing.expect_equal(hello, try intern(&first, "hello")) + try testing.expect_equal(world, try intern(&second, "world")) +}