fix(ir): unify named-const array-dim resolution + kill length-0 fabrication (0083)
A type alias whose dimension is a named const (`Arr :: [N]T`) resolves its dimension eagerly during scanDecls pass 1, on the stateless registration path, which can only read `module_const_map`. Typed consts (`N : s64 : 16`) register only in pass 2 and a forward-declared untyped const had not registered yet, so the stateless resolver saw an empty table, printed a non-fatal warning, fabricated length 0, and continued — yielding a 0-byte alloca, garbage reads, and a segfault for slice/struct elements. - scanDecls pass 0 pre-registers every integer-valued module const before any type alias resolves, so typed, untyped, and forward-referenced consts all resolve identically. - Both dim resolvers now share `program_index.moduleConstInt`, so the stateful body-lowering path and the stateless registration path cannot diverge. - `resolveArrayLen` returns `?u32`; `resolveCompound` yields `.unresolved` on null instead of a 0-length array. The stateful path emits a diagnostic; the alias-registration path surfaces an unresolved alias as a clean compile error that aborts the build. The Vector lane-count `else => 0` is fixed the same way. Regressions: examples/0143 (typed-const dim direct + via alias for s64/string/ struct, forward-ref alias, nested) and examples/1129 (an unresolvable computed dim halts with a clean diagnostic + non-zero exit). Both fail on the pre-fix compiler (garbage/segfault; warning+exit0) and pass after.
This commit is contained in:
65
examples/0143-types-typed-const-array-dim.sx
Normal file
65
examples/0143-types-typed-const-array-dim.sx
Normal file
@@ -0,0 +1,65 @@
|
|||||||
|
// A named-const array dimension lays out identically whether the const is
|
||||||
|
// TYPED (`N : s64 : 16`) or untyped (`N :: 16`), used DIRECTLY (`a : [N]T`) or
|
||||||
|
// through a type alias (`Arr :: [N]T`), and regardless of whether the const is
|
||||||
|
// declared before or after the alias that consumes it.
|
||||||
|
//
|
||||||
|
// Regression (issue 0083): the stateless registration-time resolver
|
||||||
|
// (type_bridge) only saw module consts that were already in `module_const_map`
|
||||||
|
// when a type alias resolved its dimension. Typed consts register in a later
|
||||||
|
// pass, and a forward-declared untyped const had not registered yet — so the
|
||||||
|
// alias dimension fabricated length 0 (a 0-byte alloca), and element access
|
||||||
|
// returned garbage (scalars) or bus-errored (slice/struct elements). Module
|
||||||
|
// consts are now pre-registered before any alias resolves, and both the
|
||||||
|
// stateful and stateless paths share one dimension resolver.
|
||||||
|
#import "modules/std.sx";
|
||||||
|
|
||||||
|
NT : s64 : 8; // typed const used as a dimension
|
||||||
|
|
||||||
|
P :: struct { x: s64; y: s64; }
|
||||||
|
|
||||||
|
// Type aliases whose dimension is the TYPED const NT (stateless registration).
|
||||||
|
TArr :: [NT]s64;
|
||||||
|
TSArr :: [NT]string;
|
||||||
|
TPArr :: [NT]P;
|
||||||
|
|
||||||
|
// Forward reference: this alias is declared BEFORE its dimension const NF.
|
||||||
|
FArr :: [NF]s64;
|
||||||
|
NF :: 5;
|
||||||
|
|
||||||
|
main :: () {
|
||||||
|
// Typed-const dimension, DIRECT local decl.
|
||||||
|
d : [NT]s64 = ---;
|
||||||
|
d[0] = 3;
|
||||||
|
d[7] = 21;
|
||||||
|
print("direct d0={} d7={} len={}\n", d[0], d[7], d.len);
|
||||||
|
|
||||||
|
// Typed-const dimension via ALIAS (scalar): same layout as the direct form.
|
||||||
|
a : TArr = ---;
|
||||||
|
a[0] = 7;
|
||||||
|
a[7] = 99;
|
||||||
|
print("alias a0={} a7={} len={}\n", a[0], a[7], a.len);
|
||||||
|
|
||||||
|
// Typed-const dimension via ALIAS (string elements): no bus error.
|
||||||
|
s : TSArr = ---;
|
||||||
|
s[0] = "hi";
|
||||||
|
s[7] = "yo";
|
||||||
|
print("alias s0={} s7={}\n", s[0], s[7]);
|
||||||
|
|
||||||
|
// Typed-const dimension via ALIAS (struct elements).
|
||||||
|
ps : TPArr = ---;
|
||||||
|
ps[0] = P.{ x = 1, y = 2 };
|
||||||
|
ps[7] = P.{ x = 5, y = 6 };
|
||||||
|
print("alias p0x={} p0y={} p7x={}\n", ps[0].x, ps[0].y, ps[7].x);
|
||||||
|
|
||||||
|
// Nested fixed array whose both dimensions are the typed const NT.
|
||||||
|
grid : [NT][NT]s64 = ---;
|
||||||
|
grid[0][0] = 1;
|
||||||
|
grid[7][7] = 10;
|
||||||
|
print("nested g00={} g77={}\n", grid[0][0], grid[7][7]);
|
||||||
|
|
||||||
|
// Forward-referenced alias dimension (untyped const declared after it).
|
||||||
|
f : FArr = ---;
|
||||||
|
f[0] = 4;
|
||||||
|
f[4] = 40;
|
||||||
|
print("fwd f0={} f4={} len={}\n", f[0], f[4], f.len);
|
||||||
|
}
|
||||||
20
examples/1129-diagnostics-array-dim-not-const.sx
Normal file
20
examples/1129-diagnostics-array-dim-not-const.sx
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
// An array dimension that is not a compile-time integer constant is a hard
|
||||||
|
// error, not a silently-fabricated 0-length array. Here a type alias's
|
||||||
|
// dimension is a computed expression (`M + 1`), which the registration-time
|
||||||
|
// resolver cannot evaluate.
|
||||||
|
//
|
||||||
|
// Regression (issue 0083): the stateless resolver printed a non-fatal warning
|
||||||
|
// and fabricated length 0, then let compilation continue — producing a 0-byte
|
||||||
|
// alloca and corrupt element access. It now yields the `.unresolved` sentinel,
|
||||||
|
// which the alias registration surfaces as this diagnostic, aborting the build
|
||||||
|
// with a non-zero exit.
|
||||||
|
#import "modules/std.sx";
|
||||||
|
|
||||||
|
M :: 4;
|
||||||
|
BadArr :: [M + 1]s64;
|
||||||
|
|
||||||
|
main :: () {
|
||||||
|
a : BadArr = ---;
|
||||||
|
a[0] = 7;
|
||||||
|
print("a0={}\n", a[0]);
|
||||||
|
}
|
||||||
1
examples/expected/0143-types-typed-const-array-dim.exit
Normal file
1
examples/expected/0143-types-typed-const-array-dim.exit
Normal file
@@ -0,0 +1 @@
|
|||||||
|
0
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
direct d0=3 d7=21 len=8
|
||||||
|
alias a0=7 a7=99 len=8
|
||||||
|
alias s0=hi s7=yo
|
||||||
|
alias p0x=1 p0y=2 p7x=5
|
||||||
|
nested g00=1 g77=10
|
||||||
|
fwd f0=4 f4=40 len=5
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
1
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
error: type alias 'BadArr' could not be resolved: an array dimension is not a compile-time integer constant
|
||||||
|
--> examples/1129-diagnostics-array-dim-not-const.sx:14:11
|
||||||
|
|
|
||||||
|
14 | BadArr :: [M + 1]s64;
|
||||||
|
| ^^^^^^^^^^
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
|
||||||
@@ -25,6 +25,30 @@
|
|||||||
> `src/ir/type_bridge.zig`. Regression: `examples/0140-types-named-const-array-dim.sx`
|
> `src/ir/type_bridge.zig`. Regression: `examples/0140-types-named-const-array-dim.sx`
|
||||||
> (direct + type-alias + nested `[N][M]T` + union-field dims, s64 / string /
|
> (direct + type-alias + nested `[N][M]T` + union-field dims, s64 / string /
|
||||||
> struct element types).
|
> struct element types).
|
||||||
|
>
|
||||||
|
> **Root-cause close-out (attempt 3).** Attempt 2 threaded the const map into
|
||||||
|
> `type_bridge` but the map wasn't fully populated when an alias resolved its
|
||||||
|
> dimension: type aliases (`Arr :: [N]T`) resolve EAGERLY in scanDecls pass 1,
|
||||||
|
> while TYPED consts (`N : s64 : 16`) register only in pass 2 and a
|
||||||
|
> forward-declared untyped const (`Arr :: [N]T; N :: 16`) hadn't registered yet
|
||||||
|
> either — so the stateless resolver saw an empty table, printed a non-fatal
|
||||||
|
> warning, fabricated length 0, and CONTINUED to garbage / a segfault. Three
|
||||||
|
> coordinated fixes: (1) a scanDecls **pass 0** pre-registers every integer-valued
|
||||||
|
> module const into `module_const_map` BEFORE any alias resolves, so typed,
|
||||||
|
> untyped, and forward-referenced consts all resolve identically; (2) both the
|
||||||
|
> stateful and stateless dim resolvers now share one routine
|
||||||
|
> (`program_index.moduleConstInt`) so they cannot disagree again; (3) the length-0
|
||||||
|
> fabrications are GONE — `resolveArrayLen` returns `?u32`, `resolveCompound`
|
||||||
|
> yields the `.unresolved` sentinel on null (never a 0-byte array), the stateful
|
||||||
|
> path emits a diagnostic, and the registration path surfaces an unresolved alias
|
||||||
|
> as a clean compile error that aborts the build (the `type_bridge.zig:270`
|
||||||
|
> Vector-lane `else => 0` is fixed the same way). Files:
|
||||||
|
> `src/ir/program_index.zig`, `src/ir/lower.zig`, `src/ir/type_bridge.zig`,
|
||||||
|
> `src/ir/type_resolver.zig`. Regressions:
|
||||||
|
> `examples/0143-types-typed-const-array-dim.sx` (typed-const dim direct + via
|
||||||
|
> alias for s64/string/struct, forward-ref alias, nested) and
|
||||||
|
> `examples/1129-diagnostics-array-dim-not-const.sx` (an unresolvable computed dim
|
||||||
|
> halts with a clean diagnostic + non-zero exit, not a fabricated 0-length array).
|
||||||
|
|
||||||
## Symptom
|
## Symptom
|
||||||
A fixed array whose dimension is a module-global integer constant (`N :: 16;
|
A fixed array whose dimension is a module-global integer constant (`N :: 16;
|
||||||
|
|||||||
@@ -653,6 +653,24 @@ pub const Lowering = struct {
|
|||||||
|
|
||||||
/// Pass 1: Scan declarations — register ASTs and extern stubs, but don't lower bodies.
|
/// Pass 1: Scan declarations — register ASTs and extern stubs, but don't lower bodies.
|
||||||
fn scanDecls(self: *Lowering, decls: []const *const Node) void {
|
fn scanDecls(self: *Lowering, decls: []const *const Node) void {
|
||||||
|
// Pass 0: register every integer-valued module const (`N :: 16` and the
|
||||||
|
// typed `N : s64 : 16`) BEFORE any type alias is resolved below. A type
|
||||||
|
// alias whose dimension is a named const (`Arr :: [N]T`) resolves its
|
||||||
|
// dimension eagerly here, on the stateless registration path; that path
|
||||||
|
// can only read `module_const_map`. Untyped consts would otherwise be
|
||||||
|
// registered only in declaration order (pass 1) and typed ones only after
|
||||||
|
// the alias fixpoint (pass 2) — so an alias declared before its const, or
|
||||||
|
// any alias over a typed const, saw an empty table and miscompiled the
|
||||||
|
// dimension to length 0 (issue 0083). The dimension only needs the value,
|
||||||
|
// so a placeholder type is fine; pass 2 overwrites typed consts with the
|
||||||
|
// resolved annotation type (issue 0070).
|
||||||
|
for (decls) |decl| {
|
||||||
|
if (decl.data != .const_decl) continue;
|
||||||
|
const cd = decl.data.const_decl;
|
||||||
|
if (cd.value.data == .int_literal) {
|
||||||
|
self.program_index.module_const_map.put(cd.name, .{ .value = cd.value, .ty = .s64 }) catch {};
|
||||||
|
}
|
||||||
|
}
|
||||||
for (decls) |decl| {
|
for (decls) |decl| {
|
||||||
self.setCurrentSourceFile(decl.source_file);
|
self.setCurrentSourceFile(decl.source_file);
|
||||||
const is_imported = if (self.main_file) |mf|
|
const is_imported = if (self.main_file) |mf|
|
||||||
@@ -689,6 +707,16 @@ pub const Lowering = struct {
|
|||||||
{
|
{
|
||||||
// Type alias: MyFloat :: f64; Ptr :: *u8; Cb :: (s32) -> s32;
|
// 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, &self.program_index.module_const_map);
|
const target_ty = type_bridge.resolveAstType(cd.value, &self.module.types, &self.program_index.type_alias_map, &self.program_index.module_const_map);
|
||||||
|
// The stateless resolver yields `.unresolved` for a shape
|
||||||
|
// it cannot build — e.g. `Arr :: [<computed>]T`, whose
|
||||||
|
// dimension is not a compile-time integer constant. Surface
|
||||||
|
// it as a clean diagnostic so the build aborts here rather
|
||||||
|
// than letting `.unresolved` reach codegen and `@panic` in
|
||||||
|
// sizeOf (issue 0083 — no fabricated 0-length array).
|
||||||
|
if (target_ty == .unresolved) {
|
||||||
|
if (self.diagnostics) |d|
|
||||||
|
d.addFmt(.err, cd.value.span, "type alias '{s}' could not be resolved: an array dimension is not a compile-time integer constant", .{cd.name});
|
||||||
|
}
|
||||||
self.program_index.type_alias_map.put(cd.name, target_ty) catch {};
|
self.program_index.type_alias_map.put(cd.name, target_ty) catch {};
|
||||||
} else if (cd.value.data == .identifier) {
|
} else if (cd.value.data == .identifier) {
|
||||||
// Identifier-RHS alias: MyAlias :: MyInt; WideAlias :: Wide;
|
// Identifier-RHS alias: MyAlias :: MyInt; WideAlias :: Wide;
|
||||||
@@ -11634,10 +11662,12 @@ pub const Lowering = struct {
|
|||||||
/// `[16]T` and a named-const `N :: 16; [N]T` must resolve to the SAME length:
|
/// `[16]T` and a named-const `N :: 16; [N]T` must resolve to the SAME length:
|
||||||
/// the dimension is a compile-time integer, looked up in the comptime / value
|
/// the dimension is a compile-time integer, looked up in the comptime / value
|
||||||
/// / module-const tables the stateful lowering owns. A dimension that isn't a
|
/// / module-const tables the stateful lowering owns. A dimension that isn't a
|
||||||
/// compile-time integer is a hard error — emitting a diagnostic (rather than
|
/// compile-time integer is a hard error: emit a diagnostic so the driver
|
||||||
/// fabricating a 0 length, which gives a 0-byte array and out-of-bounds
|
/// aborts (`hasErrors()`), then return a harmless `0` so body lowering
|
||||||
/// element access, issue 0083).
|
/// finishes without touching the `.unresolved` sentinel (which would `@panic`
|
||||||
pub fn resolveArrayLen(self: *Lowering, len_node: *const Node) u32 {
|
/// in `sizeOf` mid-lowering, before the diagnostic surfaces). The diagnostic —
|
||||||
|
/// not the returned length — is what guarantees no garbage ships (issue 0083).
|
||||||
|
pub fn resolveArrayLen(self: *Lowering, len_node: *const Node) ?u32 {
|
||||||
if (self.comptimeArrayDim(len_node)) |n| {
|
if (self.comptimeArrayDim(len_node)) |n| {
|
||||||
if (n < 0) {
|
if (n < 0) {
|
||||||
if (self.diagnostics) |d|
|
if (self.diagnostics) |d|
|
||||||
@@ -11673,10 +11703,10 @@ pub const Lowering = struct {
|
|||||||
if (self.comptime_value_bindings) |cvb| {
|
if (self.comptime_value_bindings) |cvb| {
|
||||||
if (cvb.get(name)) |v| return v;
|
if (cvb.get(name)) |v| return v;
|
||||||
}
|
}
|
||||||
if (self.program_index.module_const_map.get(name)) |ci| {
|
// The module-const branch is shared verbatim with the stateless
|
||||||
if (ci.value.data == .int_literal) return ci.value.data.int_literal.value;
|
// registration-time resolver (`type_bridge`) so a `[N]T` dimension
|
||||||
}
|
// resolves to the same length on both paths (issue 0083).
|
||||||
return null;
|
return program_index_mod.moduleConstInt(&self.program_index.module_const_map, name);
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Resolve a type node, checking type_bindings first for generic type params.
|
/// Resolve a type node, checking type_bindings first for generic type params.
|
||||||
|
|||||||
@@ -40,6 +40,21 @@ pub const ModuleConstInfo = struct {
|
|||||||
ty: TypeId,
|
ty: TypeId,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/// A name bound to a module-global integer constant → its value, else null.
|
||||||
|
/// SINGLE source for both array-dimension resolvers — the stateful
|
||||||
|
/// body-lowering path (`Lowering.comptimeIntNamed`) and the stateless
|
||||||
|
/// registration-time path (`type_bridge.StatelessInner`). They must agree on
|
||||||
|
/// which named consts a `[N]T` dimension resolves to; if they diverge, an array
|
||||||
|
/// laid out via a type alias (`Arr :: [N]T`, stateless) gets a different length
|
||||||
|
/// than the direct form (`a : [N]T`, stateful) — the issue-0083 miscompile.
|
||||||
|
/// Untyped (`N :: 16`) and typed (`N : s64 : 16`) consts both store an
|
||||||
|
/// `.int_literal` value node, so both resolve here identically.
|
||||||
|
pub fn moduleConstInt(consts: *const std.StringHashMap(ModuleConstInfo), name: []const u8) ?i64 {
|
||||||
|
const ci = consts.get(name) orelse return null;
|
||||||
|
if (ci.value.data == .int_literal) return ci.value.data.int_literal.value;
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
pub const GlobalInfo = struct { id: inst.GlobalId, ty: TypeId };
|
pub const GlobalInfo = struct { id: inst.GlobalId, ty: TypeId };
|
||||||
|
|
||||||
/// Single lowering access point for declaration-name / import / visibility
|
/// Single lowering access point for declaration-name / import / visibility
|
||||||
|
|||||||
@@ -41,30 +41,32 @@ const StatelessInner = struct {
|
|||||||
return resolveAstType(node, self.table, self.alias_map, self.consts);
|
return resolveAstType(node, self.table, self.alias_map, self.consts);
|
||||||
}
|
}
|
||||||
/// Fixed-array dimension at registration time: a literal `[16]T`, or a
|
/// 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.
|
/// named module-global const `N :: 16; [N]T` (typed `N : s64 : 16` too)
|
||||||
/// Both yield the SAME length — registration-time paths (aliases, inline
|
/// looked up in the const table. Both yield the SAME length — registration-
|
||||||
/// union/enum fields) must lay out a named-const dim identically to a literal
|
/// time paths (aliases, inline union/enum fields) must lay out a named-const
|
||||||
/// (issue 0083). A dimension that is neither is not resolvable on this
|
/// dim identically to a literal (issue 0083). Returns null when the dimension
|
||||||
/// binding-free path (it would be a computed/comptime expression, which the
|
/// is neither (a computed/comptime expression, or a name not bound to an
|
||||||
/// stateful body-lowering path diagnoses as a hard error at the storage
|
/// integer const). Null propagates to `resolveCompound`, which yields the
|
||||||
/// site); bail LOUDLY rather than fabricating a 0 length that silently gives a
|
/// `.unresolved` sentinel rather than fabricating a 0 length that silently
|
||||||
/// 0-byte array and out-of-bounds element access.
|
/// gives a 0-byte array and out-of-bounds element access; the registration
|
||||||
pub fn resolveArrayLen(self: StatelessInner, len_node: *const Node) u32 {
|
/// caller surfaces the unresolved alias/type as a clean diagnostic.
|
||||||
|
pub fn resolveArrayLen(self: StatelessInner, len_node: *const Node) ?u32 {
|
||||||
switch (len_node.data) {
|
switch (len_node.data) {
|
||||||
.int_literal => |lit| return @intCast(lit.value),
|
.int_literal => |lit| return if (lit.value >= 0) @intCast(lit.value) else null,
|
||||||
.identifier => |id| if (self.namedConstLen(id.name)) |n| return n,
|
.identifier => |id| if (self.namedConstLen(id.name)) |n| return n,
|
||||||
.type_expr => |te| if (self.namedConstLen(te.name)) |n| return n,
|
.type_expr => |te| if (self.namedConstLen(te.name)) |n| return n,
|
||||||
else => {},
|
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 null;
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
/// A name that resolves to a module-global integer constant → its value.
|
/// A name that resolves to a non-negative module-global integer constant →
|
||||||
|
/// its value. Shares `program_index.moduleConstInt` with the stateful
|
||||||
|
/// body-lowering resolver so the two paths cannot disagree on which named
|
||||||
|
/// consts a dimension resolves to (issue 0083).
|
||||||
fn namedConstLen(self: StatelessInner, name: []const u8) ?u32 {
|
fn namedConstLen(self: StatelessInner, name: []const u8) ?u32 {
|
||||||
const consts = self.consts orelse return null;
|
const consts = self.consts orelse return null;
|
||||||
const ci = consts.get(name) orelse return null;
|
const v = program_index_mod.moduleConstInt(consts, name) orelse return null;
|
||||||
if (ci.value.data == .int_literal) return @intCast(ci.value.data.int_literal.value);
|
return if (v >= 0) @intCast(v) else null;
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -265,10 +267,12 @@ fn resolveParameterizedType(pt: *const ast.ParameterizedTypeExpr, table: *TypeTa
|
|||||||
// Vector(N, T) is a built-in parameterized type
|
// Vector(N, T) is a built-in parameterized type
|
||||||
if (std.mem.eql(u8, base_name, "Vector")) {
|
if (std.mem.eql(u8, base_name, "Vector")) {
|
||||||
if (pt.args.len == 2) {
|
if (pt.args.len == 2) {
|
||||||
const length: u32 = switch (pt.args[0].data) {
|
// The lane count is a literal or a named module-const integer — the
|
||||||
.int_literal => |lit| @intCast(@as(u64, @bitCast(lit.value))),
|
// same dimension forms a fixed array accepts. An unresolvable count
|
||||||
else => 0,
|
// is NOT a 0-lane vector (which would silently mis-size every load /
|
||||||
};
|
// store); yield `.unresolved` so the failure surfaces (issue 0083).
|
||||||
|
const si = StatelessInner{ .table = table, .alias_map = alias_map, .consts = consts };
|
||||||
|
const length = si.resolveArrayLen(pt.args[0]) orelse return .unresolved;
|
||||||
const elem = resolveAstType(pt.args[1], table, alias_map, consts);
|
const elem = resolveAstType(pt.args[1], table, alias_map, consts);
|
||||||
return table.vectorOf(elem, length);
|
return table.vectorOf(elem, length);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,10 +23,10 @@ const PrimInner = struct {
|
|||||||
else => .unresolved,
|
else => .unresolved,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
pub fn resolveArrayLen(_: PrimInner, len_node: *const Node) u32 {
|
pub fn resolveArrayLen(_: PrimInner, len_node: *const Node) ?u32 {
|
||||||
return switch (len_node.data) {
|
return switch (len_node.data) {
|
||||||
.int_literal => |lit| @intCast(lit.value),
|
.int_literal => |lit| @intCast(lit.value),
|
||||||
else => 0,
|
else => null,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -95,10 +95,14 @@ pub const TypeResolver = struct {
|
|||||||
const elem = inner.resolveInner(at.element_type);
|
const elem = inner.resolveInner(at.element_type);
|
||||||
// The dimension is delegated to `inner` exactly like the element
|
// The dimension is delegated to `inner` exactly like the element
|
||||||
// type: a literal `[16]T` and a named-const `N :: 16; [N]T` must
|
// type: a literal `[16]T` and a named-const `N :: 16; [N]T` must
|
||||||
// produce the same length. The stateful resolver consults the
|
// produce the same length. `resolveArrayLen` returns null when the
|
||||||
// const tables; the binding-free one handles literal dims (issue
|
// dimension can't be resolved to a compile-time integer; that is
|
||||||
// 0083 — a 0 here gives a 0-byte array and OOB element access).
|
// never a 0-length array (which gives a 0-byte alloca and OOB
|
||||||
const len = inner.resolveArrayLen(at.length);
|
// element access — issue 0083). Yield the `.unresolved` sentinel
|
||||||
|
// instead, so the failure halts the build (the stateful resolver
|
||||||
|
// also emits a diagnostic; the registration-time caller surfaces
|
||||||
|
// the unresolved alias) rather than silently miscompiling.
|
||||||
|
const len = inner.resolveArrayLen(at.length) orelse break :blk TypeId.unresolved;
|
||||||
break :blk table.arrayOf(elem, len);
|
break :blk table.arrayOf(elem, len);
|
||||||
},
|
},
|
||||||
.function_type_expr => |ft| blk: {
|
.function_type_expr => |ft| blk: {
|
||||||
|
|||||||
Reference in New Issue
Block a user