refactor(ir): add ResolveEnv + TypeResolver shell; own primitives + compounds (A2.1)

Architecture phase A2.1 -- behavior-preserving. Introduce src/ir/type_resolver.zig
as the canonical AST-type-node -> TypeId resolver (Principle 1), starting with:

- ResolveEnv: the explicit resolution-context shape (Principle 2) -- type/pack/
  comptime bindings + target_type. Defined now; consumed as A2.2/A2.3 move the
  cases that need it.
- TypeResolver.resolvePrimitive(name): the builtin keyword table, MOVED here from
  type_bridge.resolveTypePrimitive (now a re-export -> single source; its 7
  callers are unaffected; no import cycle).
- TypeResolver.resolveCompound(node, inner): the structural compound types
  *T / [*]T / []T / ?T / [N]T. Element types recurse via inner.resolveInner (an
  anytype callback) so generic structs / bindings in element position keep their
  full stateful resolution.

Lowering.resolveTypeWithBindings duplicated the 5 simple compounds across its
bindings and no-bindings blocks (10 arms). Both are replaced with a single
self.typeResolver().resolveCompound(node, self) delegation; adds
Lowering.resolveInner (recursion hook) + typeResolver() (by-value view).

Deliberately deferred: tuples, closures, and function types stay on the existing
pack-aware helpers (resolveClosure/Tuple/FunctionTypeWithBindings); A2.3 owns
their pack-projection logic.

Tests: src/ir/type_resolver.test.zig (resolvePrimitive keyword/null cases;
resolveCompound for all 5 + null for non-compound; ResolveEnv defaults), wired
into the ir.zig barrel.

No new fallback path; no duplicate truth. Gate green: zig build, zig build test,
bash tests/run_examples.sh (350 passed, 0 failed). lower.zig 19393 -> 19372.
This commit is contained in:
agra
2026-06-02 13:25:27 +03:00
parent 8fbaf9ca6a
commit 9eb85cf9e3
5 changed files with 213 additions and 79 deletions

View File

@@ -19,6 +19,7 @@ const TemplateParam = program_index_mod.TemplateParam;
const ProtocolDeclInfo = program_index_mod.ProtocolDeclInfo;
const ProtocolMethodInfo = program_index_mod.ProtocolMethodInfo;
const ModuleConstInfo = program_index_mod.ModuleConstInfo;
const TypeResolver = @import("type_resolver.zig").TypeResolver;
const TypeId = types.TypeId;
const StringId = types.StringId;
@@ -12730,6 +12731,24 @@ pub const Lowering = struct {
return self.resolveTypeWithBindings(type_ann);
}
/// Construct a `TypeResolver` view over the current lowering state (borrows
/// only; cheap by-value, reflects current `diagnostics` / `program_index`).
fn typeResolver(self: *Lowering) TypeResolver {
return .{
.alloc = self.alloc,
.types = &self.module.types,
.diagnostics = self.diagnostics,
.index = &self.program_index,
};
}
/// Inner-type recursion hook for `TypeResolver.resolveCompound`: resolves a
/// child type node through the full stateful resolver, so generic structs /
/// bindings / aliases in element position keep their resolution.
pub fn resolveInner(self: *Lowering, node: *const Node) TypeId {
return self.resolveTypeWithBindings(node);
}
/// Resolve a type node, checking type_bindings first for generic type params.
fn resolveTypeWithBindings(self: *Lowering, node: *const Node) TypeId {
// Pack-index in a type position: `$<pack>[<lit>]` resolves to the
@@ -12774,6 +12793,11 @@ pub const Lowering = struct {
}
}
}
// Structural compound types (`*T`, `[*]T`, `[]T`, `?T`, `[N]T`) are
// owned by TypeResolver (A2.1). Element types recurse through the full
// stateful resolver (`resolveInner` → here) so generic structs /
// bindings in element position keep their resolution.
if (self.typeResolver().resolveCompound(node, self)) |t| return t;
if (self.type_bindings) |tb| {
switch (node.data) {
.type_expr => |te| {
@@ -12786,31 +12810,6 @@ pub const Lowering = struct {
.identifier => |id| {
if (tb.get(id.name)) |ty| return ty;
},
// Compound types: resolve inner types with bindings
.slice_type_expr => |st| {
const elem = self.resolveTypeWithBindings(st.element_type);
return self.module.types.sliceOf(elem);
},
.pointer_type_expr => |pt| {
const pointee = self.resolveTypeWithBindings(pt.pointee_type);
return self.module.types.ptrTo(pointee);
},
.many_pointer_type_expr => |mp| {
const elem = self.resolveTypeWithBindings(mp.element_type);
return self.module.types.manyPtrTo(elem);
},
.optional_type_expr => |ot| {
const child = self.resolveTypeWithBindings(ot.inner_type);
return self.module.types.optionalOf(child);
},
.array_type_expr => |at| {
const elem = self.resolveTypeWithBindings(at.element_type);
const len: u32 = blk: {
if (at.length.data == .int_literal) break :blk @intCast(at.length.data.int_literal.value);
break :blk 0;
};
return self.module.types.arrayOf(elem, len);
},
.parameterized_type_expr => |pt| {
return self.resolveParameterizedWithBindings(&pt);
},
@@ -12834,30 +12833,10 @@ pub const Lowering = struct {
if (node.data == .call) {
return self.resolveTypeCallWithBindings(&node.data.call);
}
// Handle compound types that may contain generic structs (e.g., *List(ViewChild))
// These need the lowerer's resolveType to properly instantiate generics.
// Pointers / slices / many-pointers / optionals / arrays are owned by
// TypeResolver (handled above). The pack-aware tuple / closure /
// function shapes resolve here — A2.3 owns their pack projection logic.
switch (node.data) {
.pointer_type_expr => |pt| {
const pointee = self.resolveTypeWithBindings(pt.pointee_type);
return self.module.types.ptrTo(pointee);
},
.slice_type_expr => |st| {
const elem = self.resolveTypeWithBindings(st.element_type);
return self.module.types.sliceOf(elem);
},
.many_pointer_type_expr => |mp| {
const elem = self.resolveTypeWithBindings(mp.element_type);
return self.module.types.manyPtrTo(elem);
},
.optional_type_expr => |ot| {
const child = self.resolveTypeWithBindings(ot.inner_type);
return self.module.types.optionalOf(child);
},
.array_type_expr => |at| {
const elem = self.resolveTypeWithBindings(at.element_type);
const len: u32 = if (at.length.data == .int_literal) @intCast(at.length.data.int_literal.value) else 0;
return self.module.types.arrayOf(elem, len);
},
.closure_type_expr => |ct| {
return self.resolveClosureTypeWithBindings(&ct);
},