fix(ir): route every comptime-int through the shared evaluator (0083)

Attempts 1–4 fixed the array-dimension paths but the same length-0
fabrication class survived on every other site that resolves a
compile-time integer. Unify them all on the single shared
`program_index.evalConstIntExpr` so they cannot diverge:

- All three Vector lane resolvers (resolveTypeCallWithBindings,
  resolveParameterizedWithBindings, resolveArrayLiteralType) and both
  generic value-param binders (instantiateGenericStruct,
  instantiateTypeFunction) hand-rolled an `else => 0` switch. A
  module-const lane `Vector(N, f32)` fabricated a 0-lane `<0 x float>`
  (LLVM "huge alignment" abort); a value-param `Vec(N, f32)` fabricated
  a 0 binding / wrong mangled name. They now fold through the shared
  evaluator and emit a clean diagnostic + `.unresolved` on a non-const
  operand (resolveVectorLane / resolveValueParamArg) — never 0.
- evalComptimeInt (inline-for bounds) delegated to the shared evaluator,
  so `inline for 0..M` / `0..(M+1)` fold like array dims. The `<pack>.len`
  leaf moved into the shared folder via a new `ctx.lookupPackLen`.
- The unknown-type semantic checker no longer walks a value-param
  position (`Vector(N, …)` / `Vec(N, …)`) as a type name (was reporting
  "unknown type 'N'").
- The parameterized-type-arg parser and the function-body lookahead
  (hasFnBodyAfterArrow) accept a const-EXPRESSION in a value position, so
  `Vector(M + 1, f32)` and `[M + 1]T` parse as a return type too (the
  latter a pre-existing array-dim sibling that the same heuristic broke).

Regressions: examples/1501 (named-const + const-expr lane, direct +
alias, 3/4-lane reads), 1502 (runtime lane clean-halts, exit 1, no LLVM
crash), 0207 (Vec(N)/Vec(M+1) == Vec(3) instantiation), 0610 (inline-for
const bounds). Shared-evaluator unit test extended with the pack-len arm.

zig build && zig build test && bash tests/run_examples.sh: 395 passed,
0 failed.
This commit is contained in:
agra
2026-06-04 11:32:25 +03:00
parent cd39316f5e
commit a491a1bf73
23 changed files with 340 additions and 93 deletions

View File

@@ -612,6 +612,20 @@ pub const UnknownTypeChecker = struct {
}
}
/// True when arg `i` of a parameterized type `base(...)` is a VALUE
/// parameter (a compile-time integer such as a `Vector` lane count or a
/// generic `$N: u32` arg), not a type. Such a position must be skipped by
/// the unknown-type walk: a module-const arg (`Vector(N, f32)`) is a value,
/// not a type name. `Vector`'s arg 0 is always its lane count; a generic
/// struct template's value-param positions come from its declared params.
fn isValueParamPosition(self: UnknownTypeChecker, base: []const u8, i: usize) bool {
if (std.mem.eql(u8, base, "Vector")) return i == 0;
if (self.index.struct_template_map.get(base)) |tmpl| {
if (i < tmpl.type_params.len) return !tmpl.type_params[i].is_type_param;
}
return false;
}
/// Recurse a type-annotation node to its leaf names, reporting any unknown.
fn checkTypeNodeForUnknown(
self: UnknownTypeChecker,
@@ -643,8 +657,20 @@ pub const UnknownTypeChecker = struct {
if (ct.return_type) |rt| self.checkTypeNodeForUnknown(rt, declared, in_scope, type_vals);
},
// Builtin constructors (Vector) and generic templates resolve the
// base name specially; just check the type args.
.parameterized_type_expr => |pt| for (pt.args) |a| self.checkTypeNodeForUnknown(a, declared, in_scope, type_vals),
// base name specially; check only the TYPE args. A value-param
// position (a `Vector` lane count, or a generic `$N: u32` arg) holds
// a compile-time integer — `Vector(N, f32)` / `Vec(N, f32)` with `N`
// a module const — not a type name, so it must not be walked as one
// (it would falsely report "unknown type 'N'"). The lowering
// resolvers fold the value and emit the precise diagnostic if it
// isn't a compile-time integer.
.parameterized_type_expr => |pt| {
const base = if (std.mem.lastIndexOfScalar(u8, pt.name, '.')) |dot| pt.name[dot + 1 ..] else pt.name;
for (pt.args, 0..) |a, i| {
if (self.isValueParamPosition(base, i)) continue;
self.checkTypeNodeForUnknown(a, declared, in_scope, type_vals);
}
},
else => {},
}
}