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:
@@ -723,7 +723,25 @@ pub const Parser = struct {
|
||||
if (args.items.len > 0) {
|
||||
try self.expect(.comma);
|
||||
}
|
||||
// Args can be int literals (for lengths) or type expressions
|
||||
// Pack-spread type arg: `Combined($R, ..sources.T)`.
|
||||
if (self.current.tag == .dot_dot) {
|
||||
const sp_start = self.current.loc.start;
|
||||
self.advance(); // skip '..'
|
||||
const operand = try self.parseTypeExpr();
|
||||
try args.append(self.allocator, try self.createNode(sp_start, .{ .spread_expr = .{ .operand = operand } }));
|
||||
continue;
|
||||
}
|
||||
// An arg is either a TYPE (`f32`, `*T`, `[]u8`, `List(T)`) or a
|
||||
// compile-time integer expression in a value position — a
|
||||
// `Vector` lane count or a generic `$N: u32` arg: `Vector(N, f32)`,
|
||||
// `Vector(M + 1, f32)`. Parse the primary as a literal / type,
|
||||
// then continue as a const-int expression iff an arithmetic
|
||||
// operator follows. A complete type arg is always followed by
|
||||
// `,` / `)`, so `parseBinaryRhs` is a no-op for plain types and
|
||||
// the continuation is unambiguous; `Prec.additive` bounds it to
|
||||
// `+ - * / %`. The shared evaluator folds the expression; a
|
||||
// non-const value position is diagnosed during lowering.
|
||||
var arg: *Node = undefined;
|
||||
if (self.current.tag == .int_literal) {
|
||||
const arg_start = self.current.loc.start;
|
||||
const text = self.tokenSlice(self.current);
|
||||
@@ -738,16 +756,12 @@ pub const Parser = struct {
|
||||
return self.fail("invalid integer literal in type argument");
|
||||
};
|
||||
self.advance();
|
||||
try args.append(self.allocator, try self.createNode(arg_start, .{ .int_literal = .{ .value = value } }));
|
||||
} else if (self.current.tag == .dot_dot) {
|
||||
// Pack-spread type arg: `Combined($R, ..sources.T)`.
|
||||
const sp_start = self.current.loc.start;
|
||||
self.advance(); // skip '..'
|
||||
const operand = try self.parseTypeExpr();
|
||||
try args.append(self.allocator, try self.createNode(sp_start, .{ .spread_expr = .{ .operand = operand } }));
|
||||
arg = try self.createNode(arg_start, .{ .int_literal = .{ .value = value } });
|
||||
} else {
|
||||
try args.append(self.allocator, try self.parseTypeExpr());
|
||||
arg = try self.parseTypeExpr();
|
||||
}
|
||||
arg = try self.parseBinaryRhs(arg, Prec.additive);
|
||||
try args.append(self.allocator, arg);
|
||||
}
|
||||
try self.expect(.r_paren);
|
||||
return try self.createNode(start, .{ .parameterized_type_expr = .{
|
||||
@@ -3501,7 +3515,15 @@ pub const Parser = struct {
|
||||
self.current.tag == .l_bracket or self.current.tag == .r_bracket or
|
||||
self.current.tag == .l_paren or self.current.tag == .r_paren or
|
||||
self.current.tag == .comma or self.current.tag == .int_literal or
|
||||
self.current.tag == .star or self.current.tag == .question or
|
||||
// Arithmetic operators appear in a const-expression dimension /
|
||||
// lane / value-param in a return type: `-> [N + 1]f32`,
|
||||
// `-> Vector(N + 1, f32)`. They must be skipped while scanning
|
||||
// for the body brace, else the decl is misread as a bodyless
|
||||
// function-type alias and the `{` body errors as "expected ';'".
|
||||
// (`.star` doubles as the pointer sigil and is already listed.)
|
||||
self.current.tag == .star or self.current.tag == .slash or
|
||||
self.current.tag == .percent or self.current.tag == .plus or
|
||||
self.current.tag == .minus or self.current.tag == .question or
|
||||
self.current.tag == .bang or
|
||||
self.current.tag == .colon or self.current.tag == .arrow)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user