ffi M5.A.next.4A.bare.1.B: bare $args lowers to []Type slice value

Step 4A final-slice fix. Bare `$<pack_name>` (no `[<int>]`)
in expression position now parses + lowers to a comptime
`[]Type` slice value carrying one `const_type(TypeId)` per
pack element.

Plumbing:

- src/ast.zig: new `ComptimePackRef { pack_name }` node +
  `comptime_pack_ref` variant in Data.
- src/parser.zig: `parsePrimary`'s `$` arm makes `[` optional
  after the pack name. With `[<int>]` → existing
  `pack_index_type_expr` (single Type value). Without → new
  `comptime_pack_ref` (whole pack as []Type).
- src/sema.zig: adds the no-op switch arms for the new node
  in `analyzeNode` and `findNodeAtOffset`.
- src/ir/lower.zig: `lowerExpr` arm reads `pack_arg_types[name]`
  and calls `buildPackSliceValue(arg_tys)`. The helper allocas
  a `[N x Any]` array, emits one `const_type(arg_tys[i])` per
  slot, then a slice `{data_ptr, len}` aggregate. No active
  binding → focused diagnostic + null slice placeholder. The
  IR slice element type is `Any` (matches the today's
  `Type → .any` mapping in type_bridge); the interp stores
  raw `.type_tag` Values directly (NOT Any-boxed) so
  `args[i]` at interp time reads a Type value.
- src/ir/emit_llvm.zig: relaxed `const_type` to silently emit
  undef-i64 instead of the previous stderr-noisy bail. Storage
  of Type values in runtime aggregates is harmless (undef in,
  undef out). Use-site misuse is caught by the bails on
  type_name/type_eq/has_impl and the bitcast guard.

`examples/170-pack-bare-value.sx` flips from the parse-error
lock-in to "0/1/3/4" — four call shapes of `len_of(..$args) ->
s64 { list := $args; return list.len; }`. The slice's `.len`
field carries the per-mono pack arity.

210/210 example tests + `zig build test` green.

The remaining 4A.bare slices (4 and 5) — resolveTypeArg
silent-arm fix for index_expr + smoke test of a real builder
walking $args — are separate commits per the cadence rule.
This commit is contained in:
agra
2026-05-27 19:10:37 +03:00
parent c792642d76
commit 5a4a19b3ab
5 changed files with 114 additions and 32 deletions

View File

@@ -2293,14 +2293,13 @@ pub const Parser = struct {
fn parsePrimary(self: *Parser) anyerror!*Node {
const start = self.current.loc.start;
// Pack-index in expression position: `$<pack_name>[<int_literal>]`.
// Yields a `pack_index_type_expr` AST node (same shape used in
// type positions in step 3) — lowering resolves it to a
// `const_type(TypeId)` value at the bound pack-arg type. Step 3
// already handles the same node in type positions; this arm
// extends it to value expressions, completing the round trip
// for builders that pass Type values to `type_name`/`type_eq`
// /etc at interp time.
// Pack references in expression position:
// `$<pack_name>[<int_literal>]` → `pack_index_type_expr`
// (single Type value, step 3 shape)
// `$<pack_name>` → `comptime_pack_ref`
// (whole pack as []Type value, step 4 final slice)
// Lowering routes each through `pack_arg_types` to either
// a `const_type(TypeId)` or a `[]Type` aggregate of them.
if (self.current.tag == .dollar) {
self.advance();
if (self.current.tag != .identifier) {
@@ -2308,23 +2307,25 @@ pub const Parser = struct {
}
const pname = self.tokenSlice(self.current);
self.advance();
if (self.current.tag != .l_bracket) {
return self.fail("expected '[' after '$<pack_name>' (pack indexing requires a literal index in expression position)");
if (self.current.tag == .l_bracket) {
self.advance(); // skip '['
if (self.current.tag != .int_literal) {
return self.fail("expected integer literal in pack index");
}
const idx_text = self.tokenSlice(self.current);
const idx_val = std.fmt.parseInt(i64, idx_text, 10) catch {
return self.fail("invalid integer literal in pack index");
};
if (idx_val < 0) return self.fail("pack index cannot be negative");
self.advance();
try self.expect(.r_bracket);
return try self.createNode(start, .{ .pack_index_type_expr = .{
.pack_name = pname,
.index = @intCast(idx_val),
} });
}
self.advance(); // skip '['
if (self.current.tag != .int_literal) {
return self.fail("expected integer literal in pack index");
}
const idx_text = self.tokenSlice(self.current);
const idx_val = std.fmt.parseInt(i64, idx_text, 10) catch {
return self.fail("invalid integer literal in pack index");
};
if (idx_val < 0) return self.fail("pack index cannot be negative");
self.advance();
try self.expect(.r_bracket);
return try self.createNode(start, .{ .pack_index_type_expr = .{
return try self.createNode(start, .{ .comptime_pack_ref = .{
.pack_name = pname,
.index = @intCast(idx_val),
} });
}
switch (self.current.tag) {