diff --git a/README.md b/README.md index 91cdf9b..77f5d33 100644 --- a/README.md +++ b/README.md @@ -69,6 +69,8 @@ name resolution (`@source/sema`) runs after parsing. it predeclares package name 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. +the checker keeps union-find parent links and group sizes in a compact array separate from per-node inference metadata. root walks and path compression access only that link storage; both temporary arrays are released before `check.check` returns. + numeric typing follows contextual rules within Honey's current `int` and `float` families. integer literals can adopt either family; float literals remain float. numeric demands propagate through arithmetic and inferred bindings before undemanded integers default to `int`. conflicting demands are errors; fixed `int` values do not implicitly convert to `float`. `+`, `-`, `*` and unary `-` require compatible numeric operands; `/` requires and supplies float context. `3 / 2`, `3.0 / 2`, and `ratio float :: 3 / 2` are valid; division involving fixed `int` values requires explicit conversion. type errors carry source spans and diagnostic identities; invalid operands propagate their existing identity without duplicate errors, while independent expressions, arguments and nested bodies are still checked. inference cycles without a type anchor are diagnosed. function signatures are checked independently of their bodies. bare `return` and same-line `return expression` are supported inside function bodies and must match the declared result type. nonvoid return-path checking is structural: every path must return or terminate in a diagnostic trap, and both conditional successors are considered without constant folding. a missing `else` or a loop permits fallthrough, even for a literal `true` condition; a later return can close that path. statements after a return and untaken branches or loops are still type-checked but are not executed. diff --git a/source/sema/check/check.hon b/source/sema/check/check.hon index 8cb4a8a..31e0721 100644 --- a/source/sema/check/check.hon +++ b/source/sema/check/check.hon @@ -70,10 +70,14 @@ Role :: enum { } @hide:file -Cell :: struct { - role Role = .none +GroupLink :: struct { parent usize = 0 size usize = 1 +} + +@hide:file +Cell :: struct { + role Role = .none type Type = Type{} numeric bool = false anchored bool = false @@ -106,6 +110,7 @@ State :: struct { diagnostics @mut diagnostics.Store types []mut Type cells []mut Cell + groups []mut GroupLink edges arraylist.ArrayList(Edge) arrays arraylist.ArrayList(ArrayType) collecting_assignment bool = false @@ -127,7 +132,10 @@ check proc( cells :: try mem.alloc(Cell, allocator, count) defer mem.free(allocator, cells) mem.set(cells, Cell{}) - for (0..count) |i| cells[i].parent = i + groups :: try mem.alloc(GroupLink, allocator, count) + defer mem.free(allocator, groups) + mem.set(groups, GroupLink{}) + for (0..count) |i| groups[i].parent = i queue :: try mem.alloc(ast.NodeId, allocator, count) defer mem.free(allocator, queue) state := State{ @@ -136,6 +144,7 @@ check proc( diagnostics = diagnostic_store, types = types, cells = cells, + groups = groups, edges = arraylist.init(allocator), arrays = arraylist.init(allocator), } @@ -432,11 +441,11 @@ invalid_type proc(diagnostic diagnostics.DiagnosticId) Type { @hide:file root proc(state @mut State, index usize) usize { current := index - while (state.cells[current].parent != current) current = state.cells[current].parent + while (state.groups[current].parent != current) current = state.groups[current].parent next := index - while (state.cells[next].parent != next) { - parent :: state.cells[next].parent - state.cells[next].parent = current + while (state.groups[next].parent != next) { + parent :: state.groups[next].parent + state.groups[next].parent = current next = parent } return current @@ -458,13 +467,13 @@ join proc(state @mut State, left, right ast.NodeId) void { a := root(state, usize(left)) b := root(state, usize(right)) if (a == b) return - if (state.cells[a].size < state.cells[b].size) { + if (state.groups[a].size < state.groups[b].size) { saved :: a a = b b = saved } - state.cells[b].parent = a - state.cells[a].size += state.cells[b].size + state.groups[b].parent = a + state.groups[a].size += state.groups[b].size if (state.cells[a].type.kind == .none or state.cells[b].type.kind == .invalid) { state.cells[a].type = state.cells[b].type }