fix(ir): exhaustive named-const array dims (0083) + nested slice-literal coercion (0085)
Makes the F0.4 fixes exhaustive across every resolution / nesting path.
0083 — named-const array dimension, stateless paths. Attempt 1 fixed the
stateful resolver (direct local decls, struct fields, params, returns) but the
binding-free registration-time resolver (`type_bridge`, used for type aliases
`Arr :: [N]T` and inline union/enum field types) still resolved a named dim with
a silent `else 0`, so `Arr :: [N]s64; a : Arr` and `union { a: [N]s64 }` were
still miscompiled (garbage / bus error). Thread the module-global const table
(`ProgramIndex.module_const_map`) into `type_bridge` alongside the alias map, so
`StatelessInner.resolveArrayLen` resolves a named module-const dim to the same
length everywhere. The remaining unresolvable case (a computed/comptime dim on
the binding-free path, which the stateful path hard-errors) now bails LOUDLY
instead of fabricating a 0 length.
0085 — nested slice-literal elements. `lowerArrayLiteral` lowered each element
with the element type as target but appended the raw value. A nested `.[...]`
element at a slice element type (`[][]s64`) still lowers to an aggregate array
`[N]T`, so the outer aggregate held raw arrays where slice {ptr,len} headers
were expected — indexing the inner slice read a garbage pointer and segfaulted.
After lowering each element, coerce a same-element array to the slice element
type via the existing `array_to_slice` op. The coercion recurses with the
nesting, so `[][]T` and deeper materialize at every level — local-bound AND
direct-call-argument forms.
Regressions (fail-before/pass-after demonstrated on the pre-fix compiler):
examples/0140-types-named-const-array-dim.sx — extended with type-alias,
nested [N][M]T, and union-field named dims (s64 / string / struct elems)
examples/0142-types-nested-slice-literal-elements.sx — [][]s64 + [][]string,
local-bound vs direct-arg
src/ir/type_bridge.test.zig — named-const dim resolves to literal length
Gate: zig build, zig build test, bash tests/run_examples.sh (388 passed).
Issues 0083 and 0085 marked RESOLVED.
This commit is contained in:
@@ -264,7 +264,7 @@ pub const CallResolver = struct {
|
||||
if (method_fd.body.data == .compiler_expr) {
|
||||
return .{
|
||||
.kind = .struct_method,
|
||||
.return_type = if (method_fd.return_type) |rt| type_bridge.resolveAstType(rt, &self.l.module.types, &self.l.program_index.type_alias_map) else .void,
|
||||
.return_type = if (method_fd.return_type) |rt| type_bridge.resolveAstType(rt, &self.l.module.types, &self.l.program_index.type_alias_map, &self.l.program_index.module_const_map) else .void,
|
||||
.target = .{ .named = qualified },
|
||||
.prepends_receiver = true,
|
||||
.expands_defaults = defaultsFor(method_fd, c.args.len + 1),
|
||||
|
||||
@@ -560,9 +560,9 @@ pub const Lowering = struct {
|
||||
} else if (cd.value.data == .struct_decl) {
|
||||
self.registerStructDecl(&cd.value.data.struct_decl);
|
||||
} else if (cd.value.data == .enum_decl) {
|
||||
_ = type_bridge.resolveAstType(cd.value, &self.module.types, &self.program_index.type_alias_map);
|
||||
_ = type_bridge.resolveAstType(cd.value, &self.module.types, &self.program_index.type_alias_map, &self.program_index.module_const_map);
|
||||
} else if (cd.value.data == .union_decl) {
|
||||
_ = type_bridge.resolveAstType(cd.value, &self.module.types, &self.program_index.type_alias_map);
|
||||
_ = type_bridge.resolveAstType(cd.value, &self.module.types, &self.program_index.type_alias_map, &self.program_index.module_const_map);
|
||||
} else if (cd.value.data == .comptime_expr) {
|
||||
self.lowerComptimeGlobal(cd.name, cd.value.data.comptime_expr.expr, cd.type_annotation);
|
||||
}
|
||||
@@ -574,10 +574,10 @@ pub const Lowering = struct {
|
||||
self.registerStructDecl(&sd);
|
||||
},
|
||||
.enum_decl => {
|
||||
_ = type_bridge.resolveAstType(decl, &self.module.types, &self.program_index.type_alias_map);
|
||||
_ = type_bridge.resolveAstType(decl, &self.module.types, &self.program_index.type_alias_map, &self.program_index.module_const_map);
|
||||
},
|
||||
.union_decl => {
|
||||
_ = type_bridge.resolveAstType(decl, &self.module.types, &self.program_index.type_alias_map);
|
||||
_ = type_bridge.resolveAstType(decl, &self.module.types, &self.program_index.type_alias_map, &self.program_index.module_const_map);
|
||||
},
|
||||
.error_set_decl => {
|
||||
self.registerErrorSetDecl(decl);
|
||||
@@ -675,10 +675,10 @@ pub const Lowering = struct {
|
||||
self.registerStructDecl(&cd.value.data.struct_decl);
|
||||
} else if (cd.value.data == .enum_decl) {
|
||||
// Register enum/tagged-union types in the type table
|
||||
_ = type_bridge.resolveAstType(cd.value, &self.module.types, &self.program_index.type_alias_map);
|
||||
_ = type_bridge.resolveAstType(cd.value, &self.module.types, &self.program_index.type_alias_map, &self.program_index.module_const_map);
|
||||
} else if (cd.value.data == .union_decl) {
|
||||
// Register plain union types in the type table
|
||||
_ = type_bridge.resolveAstType(cd.value, &self.module.types, &self.program_index.type_alias_map);
|
||||
_ = type_bridge.resolveAstType(cd.value, &self.module.types, &self.program_index.type_alias_map, &self.program_index.module_const_map);
|
||||
} else if (cd.value.data == .type_expr or
|
||||
cd.value.data == .pointer_type_expr or
|
||||
cd.value.data == .many_pointer_type_expr or
|
||||
@@ -688,7 +688,7 @@ pub const Lowering = struct {
|
||||
cd.value.data == .function_type_expr)
|
||||
{
|
||||
// Type alias: MyFloat :: f64; Ptr :: *u8; Cb :: (s32) -> s32;
|
||||
const target_ty = type_bridge.resolveAstType(cd.value, &self.module.types, &self.program_index.type_alias_map);
|
||||
const target_ty = type_bridge.resolveAstType(cd.value, &self.module.types, &self.program_index.type_alias_map, &self.program_index.module_const_map);
|
||||
self.program_index.type_alias_map.put(cd.name, target_ty) catch {};
|
||||
} else if (cd.value.data == .identifier) {
|
||||
// Identifier-RHS alias: MyAlias :: MyInt; WideAlias :: Wide;
|
||||
@@ -771,7 +771,7 @@ pub const Lowering = struct {
|
||||
// resolve via type_bridge and register the result
|
||||
// under the alias name so `Vec4` in expression
|
||||
// position can `const_type(<vector tid>)`.
|
||||
const result_ty = type_bridge.resolveAstType(cd.value, &self.module.types, &self.program_index.type_alias_map);
|
||||
const result_ty = type_bridge.resolveAstType(cd.value, &self.module.types, &self.program_index.type_alias_map, &self.program_index.module_const_map);
|
||||
if (result_ty != .void and result_ty != .unresolved) {
|
||||
self.program_index.type_alias_map.put(cd.name, result_ty) catch {};
|
||||
}
|
||||
@@ -806,11 +806,11 @@ pub const Lowering = struct {
|
||||
},
|
||||
.enum_decl => {
|
||||
// Register enum/tagged-union types in the type table
|
||||
_ = type_bridge.resolveAstType(decl, &self.module.types, &self.program_index.type_alias_map);
|
||||
_ = type_bridge.resolveAstType(decl, &self.module.types, &self.program_index.type_alias_map, &self.program_index.module_const_map);
|
||||
},
|
||||
.union_decl => {
|
||||
// Register plain union types in the type table
|
||||
_ = type_bridge.resolveAstType(decl, &self.module.types, &self.program_index.type_alias_map);
|
||||
_ = type_bridge.resolveAstType(decl, &self.module.types, &self.program_index.type_alias_map, &self.program_index.module_const_map);
|
||||
},
|
||||
.error_set_decl => {
|
||||
self.registerErrorSetDecl(decl);
|
||||
@@ -1813,7 +1813,7 @@ pub const Lowering = struct {
|
||||
// Block-local type declarations
|
||||
.struct_decl => |sd| self.registerStructDecl(&sd),
|
||||
.enum_decl, .union_decl => {
|
||||
_ = type_bridge.resolveAstType(node, &self.module.types, &self.program_index.type_alias_map);
|
||||
_ = type_bridge.resolveAstType(node, &self.module.types, &self.program_index.type_alias_map, &self.program_index.module_const_map);
|
||||
},
|
||||
.error_set_decl => self.registerErrorSetDecl(node),
|
||||
.ufcs_alias => |ua| {
|
||||
@@ -1972,7 +1972,7 @@ pub const Lowering = struct {
|
||||
return;
|
||||
}
|
||||
if (cd.value.data == .enum_decl or cd.value.data == .union_decl) {
|
||||
_ = type_bridge.resolveAstType(cd.value, &self.module.types, &self.program_index.type_alias_map);
|
||||
_ = type_bridge.resolveAstType(cd.value, &self.module.types, &self.program_index.type_alias_map, &self.program_index.module_const_map);
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -2912,7 +2912,7 @@ pub const Lowering = struct {
|
||||
// `t : Type = f64;` store a real TypeId; lets
|
||||
// `t == f64` icmp at runtime against the same TypeId.
|
||||
if (self.isKnownTypeName(te.name)) {
|
||||
const ty = type_bridge.resolveAstType(node, &self.module.types, &self.program_index.type_alias_map);
|
||||
const ty = type_bridge.resolveAstType(node, &self.module.types, &self.program_index.type_alias_map, &self.program_index.module_const_map);
|
||||
break :blk self.builder.constType(ty);
|
||||
}
|
||||
break :blk self.emitError(te.name, node.span);
|
||||
@@ -5357,8 +5357,26 @@ pub const Lowering = struct {
|
||||
for (al.elements) |elem| {
|
||||
const old_tt = self.target_type;
|
||||
self.target_type = elem_ty;
|
||||
const val = self.lowerExpr(elem);
|
||||
var val = self.lowerExpr(elem);
|
||||
self.target_type = old_tt;
|
||||
// A nested `.[...]` element at a slice element type lowers to an
|
||||
// aggregate array `[N]U` (lowerArrayLiteral always yields an array
|
||||
// value); materialize it into a `[]U` slice so the element is a real
|
||||
// {ptr,len} header rather than a raw array the callee would read its
|
||||
// header off of (issue 0085). This per-element coercion recurses with
|
||||
// the literal nesting, so `[][]T` and deeper coerce at every level.
|
||||
if (!elem_ty.isBuiltin()) {
|
||||
const ei = self.module.types.get(elem_ty);
|
||||
if (ei == .slice) {
|
||||
const val_ty = self.builder.getRefType(val);
|
||||
if (!val_ty.isBuiltin()) {
|
||||
const vi = self.module.types.get(val_ty);
|
||||
if (vi == .array and vi.array.element == ei.slice.element) {
|
||||
val = self.coerceToType(val, val_ty, elem_ty);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
elems.append(self.alloc, val) catch unreachable;
|
||||
}
|
||||
|
||||
@@ -5401,7 +5419,7 @@ pub const Lowering = struct {
|
||||
const name_id = self.module.types.internString(id.name);
|
||||
return self.module.types.findByName(name_id) orelse .unresolved;
|
||||
},
|
||||
.type_expr => return type_bridge.resolveAstType(te, &self.module.types, &self.program_index.type_alias_map),
|
||||
.type_expr => return type_bridge.resolveAstType(te, &self.module.types, &self.program_index.type_alias_map, &self.program_index.module_const_map),
|
||||
.field_access => |fa| {
|
||||
// Module.Type — try to resolve the field as a type name
|
||||
const name_id = self.module.types.internString(fa.field);
|
||||
@@ -6875,7 +6893,7 @@ pub const Lowering = struct {
|
||||
// Check for #compiler free functions
|
||||
if (self.program_index.fn_ast_map.get(func_name)) |fd_check| {
|
||||
if (fd_check.body.data == .compiler_expr) {
|
||||
const ret_ty = if (fd_check.return_type) |rt| type_bridge.resolveAstType(rt, &self.module.types, &self.program_index.type_alias_map) else TypeId.void;
|
||||
const ret_ty = if (fd_check.return_type) |rt| type_bridge.resolveAstType(rt, &self.module.types, &self.program_index.type_alias_map, &self.program_index.module_const_map) else TypeId.void;
|
||||
return self.builder.compilerCall(func_name, args.items, ret_ty);
|
||||
}
|
||||
}
|
||||
@@ -7230,7 +7248,7 @@ pub const Lowering = struct {
|
||||
if (self.program_index.fn_ast_map.get(qualified)) |method_fd| {
|
||||
if (method_fd.body.data == .compiler_expr) {
|
||||
const ret_ty = if (method_fd.return_type) |rt|
|
||||
type_bridge.resolveAstType(rt, &self.module.types, &self.program_index.type_alias_map)
|
||||
type_bridge.resolveAstType(rt, &self.module.types, &self.program_index.type_alias_map, &self.program_index.module_const_map)
|
||||
else
|
||||
.void;
|
||||
return self.builder.compilerCall(qualified, method_args.items, ret_ty);
|
||||
@@ -7668,7 +7686,7 @@ pub const Lowering = struct {
|
||||
|
||||
const ret_ty = blk: {
|
||||
if (lam.return_type) |rt| {
|
||||
break :blk type_bridge.resolveAstType(rt, &self.module.types, &self.program_index.type_alias_map);
|
||||
break :blk type_bridge.resolveAstType(rt, &self.module.types, &self.program_index.type_alias_map, &self.program_index.module_const_map);
|
||||
}
|
||||
// Use target closure return type if available — but only when it's
|
||||
// a resolved type. An `.unresolved` ret comes from an unbound
|
||||
@@ -8238,7 +8256,7 @@ pub const Lowering = struct {
|
||||
}
|
||||
|
||||
fn resolveReturnType2(self: *Lowering, rt: ?*const Node) TypeId {
|
||||
if (rt) |r| return type_bridge.resolveAstType(r, &self.module.types, &self.program_index.type_alias_map);
|
||||
if (rt) |r| return type_bridge.resolveAstType(r, &self.module.types, &self.program_index.type_alias_map, &self.program_index.module_const_map);
|
||||
return .void;
|
||||
}
|
||||
|
||||
@@ -9278,8 +9296,8 @@ pub const Lowering = struct {
|
||||
const ret_ty: TypeId = blk: {
|
||||
if (fd.return_type) |rt| {
|
||||
if (rt.data == .type_expr) {
|
||||
if (type_bridge.resolveAstType(rt, &self.module.types, &self.program_index.type_alias_map) != .unresolved) {
|
||||
break :blk type_bridge.resolveAstType(rt, &self.module.types, &self.program_index.type_alias_map);
|
||||
if (type_bridge.resolveAstType(rt, &self.module.types, &self.program_index.type_alias_map, &self.program_index.module_const_map) != .unresolved) {
|
||||
break :blk type_bridge.resolveAstType(rt, &self.module.types, &self.program_index.type_alias_map, &self.program_index.module_const_map);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -10551,7 +10569,7 @@ pub const Lowering = struct {
|
||||
return .unresolved;
|
||||
}
|
||||
}
|
||||
return type_bridge.resolveAstType(node, &self.module.types, &self.program_index.type_alias_map);
|
||||
return type_bridge.resolveAstType(node, &self.module.types, &self.program_index.type_alias_map, &self.program_index.module_const_map);
|
||||
}
|
||||
|
||||
pub fn resolveTypeArg(self: *Lowering, node: *const Node) TypeId {
|
||||
@@ -10612,7 +10630,7 @@ pub const Lowering = struct {
|
||||
},
|
||||
.type_expr => |te| {
|
||||
if (self.program_index.type_alias_map.get(te.name)) |alias_ty| return alias_ty;
|
||||
return type_bridge.resolveAstType(node, &self.module.types, &self.program_index.type_alias_map);
|
||||
return type_bridge.resolveAstType(node, &self.module.types, &self.program_index.type_alias_map, &self.program_index.module_const_map);
|
||||
},
|
||||
.call => |cl| {
|
||||
// `type_of(x)` resolves to `inferExprType(x)` at lower
|
||||
@@ -10637,7 +10655,7 @@ pub const Lowering = struct {
|
||||
.slice_type_expr,
|
||||
.optional_type_expr,
|
||||
.function_type_expr,
|
||||
=> return type_bridge.resolveAstType(node, &self.module.types, &self.program_index.type_alias_map),
|
||||
=> return type_bridge.resolveAstType(node, &self.module.types, &self.program_index.type_alias_map, &self.program_index.module_const_map),
|
||||
else => return .unresolved,
|
||||
}
|
||||
}
|
||||
@@ -11764,7 +11782,7 @@ pub const Lowering = struct {
|
||||
// literal (`(s32, s32)`); validate its elements are types and reject
|
||||
// non-type elements loudly (issue 0067).
|
||||
.tuple_literal => return self.resolveTupleLiteralTypeArg(node),
|
||||
else => return type_bridge.resolveAstType(node, &self.module.types, &self.program_index.type_alias_map),
|
||||
else => return type_bridge.resolveAstType(node, &self.module.types, &self.program_index.type_alias_map, &self.program_index.module_const_map),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12195,7 +12213,7 @@ pub const Lowering = struct {
|
||||
}
|
||||
return;
|
||||
}
|
||||
_ = type_bridge.resolveAstType(node, &self.module.types, &self.program_index.type_alias_map);
|
||||
_ = type_bridge.resolveAstType(node, &self.module.types, &self.program_index.type_alias_map, &self.program_index.module_const_map);
|
||||
}
|
||||
|
||||
fn registerStructDecl(self: *Lowering, sd: *const ast.StructDecl) void {
|
||||
@@ -12341,7 +12359,7 @@ pub const Lowering = struct {
|
||||
if (const_node.data == .const_decl) {
|
||||
const cd = const_node.data.const_decl;
|
||||
const qualified = std.fmt.allocPrint(self.alloc, "{s}.{s}", .{ sd.name, cd.name }) catch continue;
|
||||
const ty: ?TypeId = if (cd.type_annotation) |ta| type_bridge.resolveAstType(ta, table, &self.program_index.type_alias_map) else null;
|
||||
const ty: ?TypeId = if (cd.type_annotation) |ta| type_bridge.resolveAstType(ta, table, &self.program_index.type_alias_map, &self.program_index.module_const_map) else null;
|
||||
self.struct_const_map.put(qualified, .{ .value = cd.value, .ty = ty }) catch {};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -296,7 +296,7 @@ pub const ProtocolResolver = struct {
|
||||
if (p.data == .type_expr and std.mem.eql(u8, p.data.type_expr.name, "Self")) {
|
||||
break :blk void_ptr_ty;
|
||||
}
|
||||
break :blk type_bridge.resolveAstType(p, table, &self.l.program_index.type_alias_map);
|
||||
break :blk type_bridge.resolveAstType(p, table, &self.l.program_index.type_alias_map, &self.l.program_index.module_const_map);
|
||||
};
|
||||
ptypes.append(self.l.alloc, pty) catch unreachable;
|
||||
}
|
||||
@@ -306,7 +306,7 @@ pub const ProtocolResolver = struct {
|
||||
ret_is_self = true;
|
||||
break :blk void_ptr_ty;
|
||||
}
|
||||
break :blk type_bridge.resolveAstType(rt, table, &self.l.program_index.type_alias_map);
|
||||
break :blk type_bridge.resolveAstType(rt, table, &self.l.program_index.type_alias_map, &self.l.program_index.module_const_map);
|
||||
} else .void;
|
||||
method_infos.append(self.l.alloc, .{
|
||||
.name = method.name,
|
||||
@@ -393,7 +393,7 @@ pub const ProtocolResolver = struct {
|
||||
// Resolve the protocol's type-arg list to concrete TypeIds.
|
||||
var arg_tys = std.ArrayList(TypeId).empty;
|
||||
for (ib.protocol_type_args) |arg_node| {
|
||||
const t = type_bridge.resolveAstType(arg_node, table, &self.l.program_index.type_alias_map);
|
||||
const t = type_bridge.resolveAstType(arg_node, table, &self.l.program_index.type_alias_map, &self.l.program_index.module_const_map);
|
||||
arg_tys.append(self.l.alloc, t) catch return;
|
||||
}
|
||||
|
||||
@@ -401,9 +401,9 @@ pub const ProtocolResolver = struct {
|
||||
// parameterised impls (back-compat `target_type` string is kept for
|
||||
// simple cases but the canonical form is the TypeExpr).
|
||||
const src_ty: TypeId = if (ib.target_type_expr) |te|
|
||||
type_bridge.resolveAstType(te, table, &self.l.program_index.type_alias_map)
|
||||
type_bridge.resolveAstType(te, table, &self.l.program_index.type_alias_map, &self.l.program_index.module_const_map)
|
||||
else if (ib.target_type.len > 0)
|
||||
type_bridge.resolveAstType(&.{ .span = decl.span, .data = .{ .type_expr = .{ .name = ib.target_type } } }, table, &self.l.program_index.type_alias_map)
|
||||
type_bridge.resolveAstType(&.{ .span = decl.span, .data = .{ .type_expr = .{ .name = ib.target_type } } }, table, &self.l.program_index.type_alias_map, &self.l.program_index.module_const_map)
|
||||
else
|
||||
return;
|
||||
|
||||
|
||||
@@ -3,6 +3,8 @@ const std = @import("std");
|
||||
const types = @import("types.zig");
|
||||
const type_bridge = @import("type_bridge.zig");
|
||||
const ast = @import("../ast.zig");
|
||||
const program_index_mod = @import("program_index.zig");
|
||||
const ModuleConstInfo = program_index_mod.ModuleConstInfo;
|
||||
const Node = ast.Node;
|
||||
|
||||
const TypeId = types.TypeId;
|
||||
@@ -18,7 +20,7 @@ test "resolveAstType: primitive type_expr" {
|
||||
defer alloc.destroy(node);
|
||||
node.* = .{ .span = .{ .start = 0, .end = 0 }, .data = .{ .type_expr = .{ .name = "f64" } } };
|
||||
|
||||
try std.testing.expectEqual(TypeId.f64, type_bridge.resolveAstType(node, &table, null));
|
||||
try std.testing.expectEqual(TypeId.f64, type_bridge.resolveAstType(node, &table, null, null));
|
||||
}
|
||||
|
||||
test "resolveAstType: pointer type" {
|
||||
@@ -34,7 +36,7 @@ test "resolveAstType: pointer type" {
|
||||
defer alloc.destroy(node);
|
||||
node.* = .{ .span = .{ .start = 0, .end = 0 }, .data = .{ .pointer_type_expr = .{ .pointee_type = inner } } };
|
||||
|
||||
const id = type_bridge.resolveAstType(node, &table, null);
|
||||
const id = type_bridge.resolveAstType(node, &table, null, null);
|
||||
try std.testing.expectEqual(TypeInfo{ .pointer = .{ .pointee = .s32 } }, table.get(id));
|
||||
}
|
||||
|
||||
@@ -55,7 +57,7 @@ test "resolveAstType: optional slice" {
|
||||
defer alloc.destroy(opt);
|
||||
opt.* = .{ .span = .{ .start = 0, .end = 0 }, .data = .{ .optional_type_expr = .{ .inner_type = slice } } };
|
||||
|
||||
const id = type_bridge.resolveAstType(opt, &table, null);
|
||||
const id = type_bridge.resolveAstType(opt, &table, null, null);
|
||||
const info = table.get(id);
|
||||
switch (info) {
|
||||
.optional => |o| {
|
||||
@@ -71,7 +73,7 @@ test "resolveAstType: null surfaces as .unresolved (no silent s64 default)" {
|
||||
var table = TypeTable.init(alloc);
|
||||
defer table.deinit();
|
||||
|
||||
try std.testing.expectEqual(TypeId.unresolved, type_bridge.resolveAstType(null, &table, null));
|
||||
try std.testing.expectEqual(TypeId.unresolved, type_bridge.resolveAstType(null, &table, null, null));
|
||||
}
|
||||
|
||||
test "resolveAstType: threaded alias_map resolves named alias" {
|
||||
@@ -85,7 +87,7 @@ test "resolveAstType: threaded alias_map resolves named alias" {
|
||||
defer alloc.destroy(sh_node);
|
||||
sh_node.* = .{ .span = .{ .start = 0, .end = 0 }, .data = .{ .type_expr = .{ .name = "ShaderHandle" } } };
|
||||
|
||||
const empty_stub = type_bridge.resolveAstType(sh_node, &table, null);
|
||||
const empty_stub = type_bridge.resolveAstType(sh_node, &table, null, null);
|
||||
const empty_info = table.get(empty_stub);
|
||||
try std.testing.expectEqual(@as(std.meta.Tag(TypeInfo), .@"struct"), std.meta.activeTag(empty_info));
|
||||
try std.testing.expectEqual(@as(usize, 0), empty_info.@"struct".fields.len);
|
||||
@@ -102,7 +104,7 @@ test "resolveAstType: threaded alias_map resolves named alias" {
|
||||
defer alloc.destroy(opaque_node);
|
||||
opaque_node.* = .{ .span = .{ .start = 0, .end = 0 }, .data = .{ .type_expr = .{ .name = "Opaque" } } };
|
||||
try aliases.put("Opaque", .u64);
|
||||
try std.testing.expectEqual(TypeId.u64, type_bridge.resolveAstType(opaque_node, &table, &aliases));
|
||||
try std.testing.expectEqual(TypeId.u64, type_bridge.resolveAstType(opaque_node, &table, &aliases, null));
|
||||
|
||||
// Compound forms (`*Opaque`, `[]Opaque`, `?Opaque`) route through recursive
|
||||
// helpers that thread the same alias_map at every step.
|
||||
@@ -112,10 +114,42 @@ test "resolveAstType: threaded alias_map resolves named alias" {
|
||||
const ptr_node = try alloc.create(Node);
|
||||
defer alloc.destroy(ptr_node);
|
||||
ptr_node.* = .{ .span = .{ .start = 0, .end = 0 }, .data = .{ .pointer_type_expr = .{ .pointee_type = opaque_inner } } };
|
||||
const ptr_id = type_bridge.resolveAstType(ptr_node, &table, &aliases);
|
||||
const ptr_id = type_bridge.resolveAstType(ptr_node, &table, &aliases, null);
|
||||
try std.testing.expectEqual(TypeInfo{ .pointer = .{ .pointee = .u64 } }, table.get(ptr_id));
|
||||
}
|
||||
|
||||
test "resolveAstType: named-const array dimension resolves to the same length as a literal (issue 0083)" {
|
||||
const alloc = std.testing.allocator;
|
||||
var table = TypeTable.init(alloc);
|
||||
defer table.deinit();
|
||||
|
||||
// `N :: 4` in the module-const table, value backed by an int-literal node.
|
||||
const n_val = try alloc.create(Node);
|
||||
defer alloc.destroy(n_val);
|
||||
n_val.* = .{ .span = .{ .start = 0, .end = 0 }, .data = .{ .int_literal = .{ .value = 4 } } };
|
||||
var consts = std.StringHashMap(ModuleConstInfo).init(alloc);
|
||||
defer consts.deinit();
|
||||
try consts.put("N", .{ .value = n_val, .ty = .s64 });
|
||||
|
||||
// `[N]s64` — dimension is the named const `N`, not a literal.
|
||||
const elem = try alloc.create(Node);
|
||||
defer alloc.destroy(elem);
|
||||
elem.* = .{ .span = .{ .start = 0, .end = 0 }, .data = .{ .type_expr = .{ .name = "s64" } } };
|
||||
const len_node = try alloc.create(Node);
|
||||
defer alloc.destroy(len_node);
|
||||
len_node.* = .{ .span = .{ .start = 0, .end = 0 }, .data = .{ .identifier = .{ .name = "N" } } };
|
||||
const arr = try alloc.create(Node);
|
||||
defer alloc.destroy(arr);
|
||||
arr.* = .{ .span = .{ .start = 0, .end = 0 }, .data = .{ .array_type_expr = .{ .length = len_node, .element_type = elem } } };
|
||||
|
||||
// With the const table threaded, `[N]s64` lays out identically to `[4]s64`.
|
||||
const id = type_bridge.resolveAstType(arr, &table, null, &consts);
|
||||
const info = table.get(id);
|
||||
try std.testing.expect(info == .array);
|
||||
try std.testing.expectEqual(TypeId.s64, info.array.element);
|
||||
try std.testing.expectEqual(@as(u32, 4), info.array.length);
|
||||
}
|
||||
|
||||
test "resolveAstType: error_set_decl registers an error-set type + interns tags" {
|
||||
const alloc = std.testing.allocator;
|
||||
var table = TypeTable.init(alloc);
|
||||
@@ -129,7 +163,7 @@ test "resolveAstType: error_set_decl registers an error-set type + interns tags"
|
||||
.tag_names = &tag_names,
|
||||
} } };
|
||||
|
||||
const id = type_bridge.resolveAstType(node, &table, null);
|
||||
const id = type_bridge.resolveAstType(node, &table, null, null);
|
||||
const info = table.get(id);
|
||||
try std.testing.expect(info == .error_set);
|
||||
try std.testing.expectEqualStrings("ParseErr", table.getString(info.error_set.name));
|
||||
@@ -137,7 +171,7 @@ test "resolveAstType: error_set_decl registers an error-set type + interns tags"
|
||||
// Tags were interned into the global pool (round-trip a name through it).
|
||||
try std.testing.expectEqualStrings("BadDigit", table.getTagName(table.internTag("BadDigit")));
|
||||
// Re-resolving the same decl dedups to the same TypeId.
|
||||
try std.testing.expectEqual(id, type_bridge.resolveAstType(node, &table, null));
|
||||
try std.testing.expectEqual(id, type_bridge.resolveAstType(node, &table, null, null));
|
||||
}
|
||||
|
||||
// ── ERR E1.2 — failable-signature error channel resolution ──
|
||||
@@ -154,7 +188,7 @@ test "resolveAstType: `!Named` resolves to the declared error set" {
|
||||
const node = try alloc.create(Node);
|
||||
defer alloc.destroy(node);
|
||||
node.* = .{ .span = .{ .start = 0, .end = 0 }, .data = .{ .error_type_expr = .{ .name = "ParseErr" } } };
|
||||
try std.testing.expectEqual(set, type_bridge.resolveAstType(node, &table, null));
|
||||
try std.testing.expectEqual(set, type_bridge.resolveAstType(node, &table, null, null));
|
||||
}
|
||||
|
||||
test "resolveAstType: bare `!` resolves to a shared inferred placeholder set" {
|
||||
@@ -169,8 +203,8 @@ test "resolveAstType: bare `!` resolves to a shared inferred placeholder set" {
|
||||
defer alloc.destroy(b);
|
||||
b.* = .{ .span = .{ .start = 0, .end = 0 }, .data = .{ .error_type_expr = .{ .name = null } } };
|
||||
|
||||
const ia = type_bridge.resolveAstType(a, &table, null);
|
||||
const ib = type_bridge.resolveAstType(b, &table, null);
|
||||
const ia = type_bridge.resolveAstType(a, &table, null, null);
|
||||
const ib = type_bridge.resolveAstType(b, &table, null, null);
|
||||
try std.testing.expect(table.get(ia) == .error_set);
|
||||
try std.testing.expectEqualStrings("!", table.getString(table.get(ia).error_set.name));
|
||||
try std.testing.expectEqual(@as(usize, 0), table.get(ia).error_set.tags.len); // empty until E1.4 SCC
|
||||
@@ -198,7 +232,7 @@ test "resolveAstType: `(s32, !Named)` result list is a tuple ending in the error
|
||||
defer alloc.destroy(tuple);
|
||||
tuple.* = .{ .span = .{ .start = 0, .end = 0 }, .data = .{ .tuple_type_expr = .{ .field_types = &fields, .field_names = null } } };
|
||||
|
||||
const id = type_bridge.resolveAstType(tuple, &table, null);
|
||||
const id = type_bridge.resolveAstType(tuple, &table, null, null);
|
||||
const info = table.get(id);
|
||||
try std.testing.expect(info == .tuple);
|
||||
try std.testing.expectEqual(@as(usize, 2), info.tuple.fields.len);
|
||||
|
||||
@@ -8,6 +8,8 @@ const TypeInfo = ir_types.TypeInfo;
|
||||
const TypeTable = ir_types.TypeTable;
|
||||
const StringId = ir_types.StringId;
|
||||
const type_resolver = @import("type_resolver.zig");
|
||||
const program_index_mod = @import("program_index.zig");
|
||||
const ModuleConstInfo = program_index_mod.ModuleConstInfo;
|
||||
|
||||
/// The single-source type-alias table (`ProgramIndex.type_alias_map`), threaded
|
||||
/// explicitly through every name-resolving entry point so a bare name like
|
||||
@@ -17,6 +19,15 @@ const type_resolver = @import("type_resolver.zig");
|
||||
/// `null` for contexts that never see aliases, e.g. unit tests).
|
||||
pub const AliasMap = ?*const std.StringHashMap(TypeId);
|
||||
|
||||
/// The module-global constant table (`ProgramIndex.module_const_map`), threaded
|
||||
/// alongside the alias map so a named-const array dimension (`N :: 16; [N]T`)
|
||||
/// resolves to the same length as a literal dimension on EVERY registration-time
|
||||
/// path — type aliases (`Arr :: [N]T`), inline union/enum field types — not just
|
||||
/// the stateful body-lowering path. Without it the stateless dim resolver had no
|
||||
/// way to evaluate a named const and silently fabricated a 0 length (issue 0083).
|
||||
/// `null` for contexts with no const table (e.g. unit tests).
|
||||
pub const ConstMap = ?*const std.StringHashMap(ModuleConstInfo);
|
||||
|
||||
/// Binding-free element-recursion adapter for `TypeResolver.resolveCompound`:
|
||||
/// nested element types resolve through `type_bridge.resolveAstType` (the
|
||||
/// registration-time path — no generic/pack bindings). Lets type_bridge reuse
|
||||
@@ -25,20 +36,35 @@ pub const AliasMap = ?*const std.StringHashMap(TypeId);
|
||||
const StatelessInner = struct {
|
||||
table: *TypeTable,
|
||||
alias_map: AliasMap,
|
||||
consts: ConstMap,
|
||||
pub fn resolveInner(self: StatelessInner, node: *const Node) TypeId {
|
||||
return resolveAstType(node, self.table, self.alias_map);
|
||||
return resolveAstType(node, self.table, self.alias_map, self.consts);
|
||||
}
|
||||
/// Fixed-array dimension at registration time (no bindings / const tables).
|
||||
/// Only a literal dimension is knowable here; a named-const dimension
|
||||
/// (`N :: 16; [N]T`) is resolved by the stateful caller
|
||||
/// (`Lowering.resolveArrayLen`) before it ever reaches this binding-free
|
||||
/// path — mirroring how `pack_index_type_expr` is handled stateful-first.
|
||||
/// Fixed-array dimension at registration time: a literal `[16]T`, or a
|
||||
/// named module-global const `N :: 16; [N]T` looked up in the const table.
|
||||
/// Both yield the SAME length — registration-time paths (aliases, inline
|
||||
/// union/enum fields) must lay out a named-const dim identically to a literal
|
||||
/// (issue 0083). A dimension that is neither is not resolvable on this
|
||||
/// binding-free path (it would be a computed/comptime expression, which the
|
||||
/// stateful body-lowering path diagnoses as a hard error at the storage
|
||||
/// site); bail LOUDLY rather than fabricating a 0 length that silently gives a
|
||||
/// 0-byte array and out-of-bounds element access.
|
||||
pub fn resolveArrayLen(self: StatelessInner, len_node: *const Node) u32 {
|
||||
_ = self;
|
||||
return switch (len_node.data) {
|
||||
.int_literal => |lit| @intCast(lit.value),
|
||||
else => 0,
|
||||
};
|
||||
switch (len_node.data) {
|
||||
.int_literal => |lit| return @intCast(lit.value),
|
||||
.identifier => |id| if (self.namedConstLen(id.name)) |n| return n,
|
||||
.type_expr => |te| if (self.namedConstLen(te.name)) |n| return n,
|
||||
else => {},
|
||||
}
|
||||
std.debug.print("type_bridge: array dimension is not a literal or named integer constant — cannot resolve length at registration time (computed/comptime dimensions are unsupported here)\n", .{});
|
||||
return 0;
|
||||
}
|
||||
/// A name that resolves to a module-global integer constant → its value.
|
||||
fn namedConstLen(self: StatelessInner, name: []const u8) ?u32 {
|
||||
const consts = self.consts orelse return null;
|
||||
const ci = consts.get(name) orelse return null;
|
||||
if (ci.value.data == .int_literal) return @intCast(ci.value.data.int_literal.value);
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -46,14 +72,14 @@ const StatelessInner = struct {
|
||||
// Resolve an AST type node into an IR TypeId. Used during lowering when
|
||||
// we only have the parsed AST (no codegen type registry).
|
||||
|
||||
pub fn resolveAstType(node: ?*const Node, table: *TypeTable, alias_map: AliasMap) TypeId {
|
||||
pub fn resolveAstType(node: ?*const Node, table: *TypeTable, alias_map: AliasMap, consts: ConstMap) TypeId {
|
||||
// A null node means a caller reached type resolution without a type node.
|
||||
// Every current caller either passes a non-optional node or handles the
|
||||
// "no type" case itself (returning `.void`), so this is a caller bug — and
|
||||
// `.s64` here would silently fabricate an 8-byte int. Surface it via the
|
||||
// `.unresolved` sentinel (trips the sizeOf/toLLVMType panic at codegen).
|
||||
const n = node orelse return .unresolved;
|
||||
const si = StatelessInner{ .table = table, .alias_map = alias_map };
|
||||
const si = StatelessInner{ .table = table, .alias_map = alias_map, .consts = consts };
|
||||
return switch (n.data) {
|
||||
.type_expr => |te| resolveTypeName(te.name, table, alias_map),
|
||||
.identifier => |id| resolveTypeName(id.name, table, alias_map),
|
||||
@@ -76,8 +102,8 @@ pub fn resolveAstType(node: ?*const Node, table: *TypeTable, alias_map: AliasMap
|
||||
// `Closure(..p)` field type at registration time). These tiny fallbacks
|
||||
// are the only stateless-specific shape code left; the stateful expand
|
||||
// lives in PackResolver.
|
||||
.closure_type_expr => |ct| type_resolver.TypeResolver.resolveCompound(table, n, si) orelse resolveClosurePackShape(&ct, table, alias_map),
|
||||
.tuple_type_expr => |tt| type_resolver.TypeResolver.resolveCompound(table, n, si) orelse resolveTupleSpreadShape(&tt, table, alias_map),
|
||||
.closure_type_expr => |ct| type_resolver.TypeResolver.resolveCompound(table, n, si) orelse resolveClosurePackShape(&ct, table, alias_map, consts),
|
||||
.tuple_type_expr => |tt| type_resolver.TypeResolver.resolveCompound(table, n, si) orelse resolveTupleSpreadShape(&tt, table, alias_map, consts),
|
||||
.pack_index_type_expr => {
|
||||
// Pack-index `$args[N]` in a type position must be resolved
|
||||
// against an active pack binding — `type_bridge` has no access
|
||||
@@ -90,8 +116,8 @@ pub fn resolveAstType(node: ?*const Node, table: *TypeTable, alias_map: AliasMap
|
||||
std.debug.print("type_bridge: pack-index type expression encountered outside a pack-aware context — returning .unresolved\n", .{});
|
||||
return .unresolved;
|
||||
},
|
||||
.tuple_literal => |tl| resolveTupleLiteralAsType(&tl, table, alias_map),
|
||||
.parameterized_type_expr => |pt| resolveParameterizedType(&pt, table, alias_map),
|
||||
.tuple_literal => |tl| resolveTupleLiteralAsType(&tl, table, alias_map, consts),
|
||||
.parameterized_type_expr => |pt| resolveParameterizedType(&pt, table, alias_map, consts),
|
||||
// An unannotated param. Its type must be resolved from context
|
||||
// (contextual closure typing, generic binding, or pack substitution)
|
||||
// *before* reaching here; if it doesn't, returning a plausible `.s64`
|
||||
@@ -101,9 +127,9 @@ pub fn resolveAstType(node: ?*const Node, table: *TypeTable, alias_map: AliasMap
|
||||
// turns it into a real diagnostic.
|
||||
.inferred_type => .unresolved,
|
||||
// Inline type declarations (used as field types)
|
||||
.enum_decl => |ed| resolveInlineEnum(&ed, table, alias_map),
|
||||
.struct_decl => |sd| resolveInlineStruct(&sd, table, alias_map),
|
||||
.union_decl => |ud| resolveInlineUnion(&ud, table, alias_map),
|
||||
.enum_decl => |ed| resolveInlineEnum(&ed, table, alias_map, consts),
|
||||
.struct_decl => |sd| resolveInlineStruct(&sd, table, alias_map, consts),
|
||||
.union_decl => |ud| resolveInlineUnion(&ud, table, alias_map, consts),
|
||||
.error_set_decl => |esd| resolveInlineErrorSet(&esd, table),
|
||||
.error_type_expr => |ete| resolveErrorType(&ete, table, alias_map),
|
||||
else => {
|
||||
@@ -137,13 +163,13 @@ pub const resolveTypePrimitive = type_resolver.TypeResolver.resolvePrimitive;
|
||||
/// null). type_bridge can't expand the pack (no state), so it preserves the
|
||||
/// pack SHAPE — a `closureTypePack` whose prefix is the fixed params. The
|
||||
/// stateful expand lives in `PackResolver.resolveClosureTypeWithBindings`.
|
||||
fn resolveClosurePackShape(ct: *const ast.ClosureTypeExpr, table: *TypeTable, alias_map: AliasMap) TypeId {
|
||||
fn resolveClosurePackShape(ct: *const ast.ClosureTypeExpr, table: *TypeTable, alias_map: AliasMap, consts: ConstMap) TypeId {
|
||||
const alloc = table.alloc;
|
||||
var param_ids = std.ArrayList(TypeId).empty;
|
||||
for (ct.param_types) |pt| {
|
||||
param_ids.append(alloc, resolveAstType(pt, table, alias_map)) catch unreachable;
|
||||
param_ids.append(alloc, resolveAstType(pt, table, alias_map, consts)) catch unreachable;
|
||||
}
|
||||
const ret_id = if (ct.return_type) |rt| resolveAstType(rt, table, alias_map) else TypeId.void;
|
||||
const ret_id = if (ct.return_type) |rt| resolveAstType(rt, table, alias_map, consts) else TypeId.void;
|
||||
return table.closureTypePack(param_ids.items, ret_id, @intCast(param_ids.items.len));
|
||||
}
|
||||
|
||||
@@ -152,11 +178,11 @@ fn resolveClosurePackShape(ct: *const ast.ClosureTypeExpr, table: *TypeTable, al
|
||||
/// each field resolves individually (a spread field is not a type → resolves to
|
||||
/// `.unresolved`). The stateful expand lives in
|
||||
/// `PackResolver.resolveTupleTypeWithBindings`.
|
||||
fn resolveTupleSpreadShape(tt: *const ast.TupleTypeExpr, table: *TypeTable, alias_map: AliasMap) TypeId {
|
||||
fn resolveTupleSpreadShape(tt: *const ast.TupleTypeExpr, table: *TypeTable, alias_map: AliasMap, consts: ConstMap) TypeId {
|
||||
const alloc = table.alloc;
|
||||
var field_ids = std.ArrayList(TypeId).empty;
|
||||
for (tt.field_types) |ft| {
|
||||
field_ids.append(alloc, resolveAstType(ft, table, alias_map)) catch unreachable;
|
||||
field_ids.append(alloc, resolveAstType(ft, table, alias_map, consts)) catch unreachable;
|
||||
}
|
||||
var name_ids: ?[]const StringId = null;
|
||||
if (tt.field_names) |names| {
|
||||
@@ -182,14 +208,14 @@ fn resolveTupleSpreadShape(tt: *const ast.TupleTypeExpr, table: *TypeTable, alia
|
||||
// here, so the valid path below builds the tuple and the invalid path never
|
||||
// reaches it from lowering. The sentinel is the backstop for any other
|
||||
// (binding-free) caller.
|
||||
fn resolveTupleLiteralAsType(tl: *const ast.TupleLiteral, table: *TypeTable, alias_map: AliasMap) TypeId {
|
||||
fn resolveTupleLiteralAsType(tl: *const ast.TupleLiteral, table: *TypeTable, alias_map: AliasMap, consts: ConstMap) TypeId {
|
||||
const alloc = table.alloc;
|
||||
var field_ids = std.ArrayList(TypeId).empty;
|
||||
var name_ids_list = std.ArrayList(StringId).empty;
|
||||
var any_named = false;
|
||||
for (tl.elements) |el| {
|
||||
if (!isTypeShapedAstNode(el.value, table)) return .unresolved;
|
||||
field_ids.append(alloc, resolveAstType(el.value, table, alias_map)) catch unreachable;
|
||||
field_ids.append(alloc, resolveAstType(el.value, table, alias_map, consts)) catch unreachable;
|
||||
if (el.name) |n| {
|
||||
any_named = true;
|
||||
name_ids_list.append(alloc, table.internString(n)) catch unreachable;
|
||||
@@ -233,7 +259,7 @@ pub fn isTypeShapedAstNode(node: *const Node, table: *TypeTable) bool {
|
||||
};
|
||||
}
|
||||
|
||||
fn resolveParameterizedType(pt: *const ast.ParameterizedTypeExpr, table: *TypeTable, alias_map: AliasMap) TypeId {
|
||||
fn resolveParameterizedType(pt: *const ast.ParameterizedTypeExpr, table: *TypeTable, alias_map: AliasMap, consts: ConstMap) TypeId {
|
||||
// Strip module prefix (e.g. "std.Vector" → "Vector")
|
||||
const base_name = if (std.mem.lastIndexOfScalar(u8, pt.name, '.')) |dot| pt.name[dot + 1 ..] else pt.name;
|
||||
// Vector(N, T) is a built-in parameterized type
|
||||
@@ -243,7 +269,7 @@ fn resolveParameterizedType(pt: *const ast.ParameterizedTypeExpr, table: *TypeTa
|
||||
.int_literal => |lit| @intCast(@as(u64, @bitCast(lit.value))),
|
||||
else => 0,
|
||||
};
|
||||
const elem = resolveAstType(pt.args[1], table, alias_map);
|
||||
const elem = resolveAstType(pt.args[1], table, alias_map, consts);
|
||||
return table.vectorOf(elem, length);
|
||||
}
|
||||
}
|
||||
@@ -254,7 +280,7 @@ fn resolveParameterizedType(pt: *const ast.ParameterizedTypeExpr, table: *TypeTa
|
||||
|
||||
// ── Inline type declarations ─────────────────────────────────────────
|
||||
|
||||
fn resolveInlineEnum(ed: *const ast.EnumDecl, table: *TypeTable, alias_map: AliasMap) TypeId {
|
||||
fn resolveInlineEnum(ed: *const ast.EnumDecl, table: *TypeTable, alias_map: AliasMap, consts: ConstMap) TypeId {
|
||||
const alloc = table.alloc;
|
||||
const name_id = table.internString(ed.name);
|
||||
|
||||
@@ -280,7 +306,7 @@ fn resolveInlineEnum(ed: *const ast.EnumDecl, table: *TypeTable, alias_map: Alia
|
||||
} else {
|
||||
var sfields = std.ArrayList(TypeInfo.StructInfo.Field).empty;
|
||||
for (sd.field_names, sd.field_types) |fname, ftype_node| {
|
||||
const fty = resolveAstType(ftype_node, table, alias_map);
|
||||
const fty = resolveAstType(ftype_node, table, alias_map, consts);
|
||||
sfields.append(alloc, .{
|
||||
.name = table.internString(fname),
|
||||
.ty = fty,
|
||||
@@ -294,10 +320,10 @@ fn resolveInlineEnum(ed: *const ast.EnumDecl, table: *TypeTable, alias_map: Alia
|
||||
table.update(field_ty, sinfo);
|
||||
}
|
||||
} else {
|
||||
field_ty = resolveAstType(vt, table, alias_map);
|
||||
field_ty = resolveAstType(vt, table, alias_map, consts);
|
||||
}
|
||||
} else {
|
||||
field_ty = resolveAstType(vt, table, alias_map);
|
||||
field_ty = resolveAstType(vt, table, alias_map, consts);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -311,7 +337,7 @@ fn resolveInlineEnum(ed: *const ast.EnumDecl, table: *TypeTable, alias_map: Alia
|
||||
var backing_type: ?TypeId = null;
|
||||
var tag_type: ?TypeId = null;
|
||||
if (ed.backing_type) |bt| {
|
||||
const backing_ty = resolveAstType(bt, table, alias_map);
|
||||
const backing_ty = resolveAstType(bt, table, alias_map, consts);
|
||||
backing_type = backing_ty;
|
||||
// Extract tag type from first field of backing struct
|
||||
const backing_info = table.get(backing_ty);
|
||||
@@ -394,7 +420,7 @@ fn resolveInlineEnum(ed: *const ast.EnumDecl, table: *TypeTable, alias_map: Alia
|
||||
if (ed.backing_type) |bt| {
|
||||
// Only use simple backing types (u8, u16, u32, etc.), not struct backing (enum struct)
|
||||
if (bt.data != .struct_decl) {
|
||||
enum_backing = resolveAstType(bt, table, alias_map);
|
||||
enum_backing = resolveAstType(bt, table, alias_map, consts);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -410,7 +436,7 @@ fn resolveInlineEnum(ed: *const ast.EnumDecl, table: *TypeTable, alias_map: Alia
|
||||
return id;
|
||||
}
|
||||
|
||||
fn resolveInlineStruct(sd: *const ast.StructDecl, table: *TypeTable, alias_map: AliasMap) TypeId {
|
||||
fn resolveInlineStruct(sd: *const ast.StructDecl, table: *TypeTable, alias_map: AliasMap, consts: ConstMap) TypeId {
|
||||
const alloc = table.alloc;
|
||||
const name_id = table.internString(sd.name);
|
||||
|
||||
@@ -418,7 +444,7 @@ fn resolveInlineStruct(sd: *const ast.StructDecl, table: *TypeTable, alias_map:
|
||||
|
||||
var fields = std.ArrayList(TypeInfo.StructInfo.Field).empty;
|
||||
for (sd.field_names, sd.field_types) |fname, ftype_node| {
|
||||
const field_ty = resolveAstType(ftype_node, table, alias_map);
|
||||
const field_ty = resolveAstType(ftype_node, table, alias_map, consts);
|
||||
fields.append(alloc, .{
|
||||
.name = table.internString(fname),
|
||||
.ty = field_ty,
|
||||
@@ -433,7 +459,7 @@ fn resolveInlineStruct(sd: *const ast.StructDecl, table: *TypeTable, alias_map:
|
||||
return id;
|
||||
}
|
||||
|
||||
fn resolveInlineUnion(ud: *const ast.UnionDecl, table: *TypeTable, alias_map: AliasMap) TypeId {
|
||||
fn resolveInlineUnion(ud: *const ast.UnionDecl, table: *TypeTable, alias_map: AliasMap, consts: ConstMap) TypeId {
|
||||
const alloc = table.alloc;
|
||||
const name_id = table.internString(ud.name);
|
||||
|
||||
@@ -441,7 +467,7 @@ fn resolveInlineUnion(ud: *const ast.UnionDecl, table: *TypeTable, alias_map: Al
|
||||
|
||||
var fields = std.ArrayList(TypeInfo.StructInfo.Field).empty;
|
||||
for (ud.field_names, ud.field_types) |fname, ftype_node| {
|
||||
const field_ty = resolveAstType(ftype_node, table, alias_map);
|
||||
const field_ty = resolveAstType(ftype_node, table, alias_map, consts);
|
||||
fields.append(alloc, .{
|
||||
.name = table.internString(fname),
|
||||
.ty = field_ty,
|
||||
|
||||
Reference in New Issue
Block a user