cleanup: drop resolved-issue citations from src comments
Sweep all src/**.zig comments that cite resolved issues (issue NNNN / fix-NNNN / KB-N): the invariant or mechanism each comment states is kept; the historical citation is dropped, per the no-conclusion-comments rule. Pure-history parentheticals are removed outright. References to the 16 still-open issues (0030, 0041-0056) are untouched, as are test NAMES carrying regression provenance (matching the sanctioned "Regression (issue NNNN)" example-header convention). Also removes the issues/0019-import-non-transitive-c-scope/ fixture dir — the issue is superseded and its behavior is covered by examples/0706-modules-import-non-transitive.sx (the .md writeup stays). issues/0030's repro .sx stays: that issue is an open feature request. Gate: zig build OK; zig build test 426/426; run_examples 541/0; zero expected/ snapshot churn.
This commit is contained in:
@@ -75,7 +75,7 @@ pub const CallPlan = struct {
|
||||
/// The single bare-call author `selectPlainCallableAuthor` selected for a
|
||||
/// genuine flat same-name collision (R5 §#3). Carries the resolved
|
||||
/// `*FnDecl` + source so `plan` and the lowering call-path read ONE
|
||||
/// author and can no longer disagree (fix-0102 F2); the FuncId is
|
||||
/// author and can no longer disagree; the FuncId is
|
||||
/// materialized on demand. Only set when the bare name reroutes away from
|
||||
/// the first-wins winner; the common path still uses `func` / `named`.
|
||||
selected: Lowering.SelectedFunc,
|
||||
@@ -159,7 +159,7 @@ pub const CallResolver = struct {
|
||||
// Plain bare same-name flat collision (R5 §C): route through the ONE
|
||||
// author producer `selectedFreeAuthor` so `plan` types the call as the
|
||||
// SAME author the lowering call-path binds — they can no longer
|
||||
// disagree (fix-0102 F2). A generic / foreign / builtin author is not
|
||||
// disagree. A generic / foreign / builtin author is not
|
||||
// plain-free so the producer returns `.none`; `.ambiguous` / `.none`
|
||||
// fall through to the first-wins path below, byte-for-byte.
|
||||
switch (self.selectedFreeAuthor(c)) {
|
||||
@@ -319,7 +319,7 @@ pub const CallResolver = struct {
|
||||
// routes through the SAME author producer `selectedFreeAuthor` as a
|
||||
// bare call, so the planned target / return type IS the author
|
||||
// lowering dispatches — they can't disagree under a flat same-name
|
||||
// collision (fix-0102 F2 / R5 §C). Without this, plan typed the
|
||||
// collision (R5 §C). Without this, plan typed the
|
||||
// first-wins winner while lowering bound the selected shadow,
|
||||
// mis-tagging the call's result (a string-typed winner over an s64
|
||||
// shadow boxes a raw int as a string pointer → segfault).
|
||||
@@ -451,7 +451,7 @@ pub const CallResolver = struct {
|
||||
/// verdict (R5 §#3). Both `plan` (typing, via its `.selected` arm) and
|
||||
/// `lowerCall` (default expansion / param typing / dispatch) consume THIS one
|
||||
/// result, so they can never pick different same-name authors for the same
|
||||
/// call (fix-0102 F2). Side-effect-free: it consults ONLY the author selector
|
||||
/// call. Side-effect-free: it consults ONLY the author selector
|
||||
/// (`selectPlainCallableAuthor`) — never return-type inference or type-arg
|
||||
/// resolution — so `lowerCall` can compute it eagerly without emitting a
|
||||
/// premature diagnostic the full `plan` would (e.g. `cast(type)`'s type-arg).
|
||||
|
||||
@@ -1066,7 +1066,7 @@ test "emit: ERR E3.0 — DWARF debug info (compile unit + subprogram + per-inst
|
||||
try std.testing.expect(std.mem.indexOf(u8, ir_str, "\"Debug Info Version\"") != null);
|
||||
try std.testing.expect(std.mem.indexOf(u8, ir_str, "\"Dwarf Version\"") != null);
|
||||
try std.testing.expect(std.mem.indexOf(u8, ir_str, "DICompileUnit") != null);
|
||||
// Regression (issue 0058): a bare filename (no directory component) must
|
||||
// Regression: a bare filename (no directory component) must
|
||||
// still get a NON-EMPTY `directory:` — an empty `DW_AT_comp_dir` makes ld
|
||||
// silently drop the whole debug map, so the binary becomes undebuggable.
|
||||
try std.testing.expect(std.mem.indexOf(u8, ir_str, "DIFile(filename: \"probe.sx\", directory: \".\")") != null);
|
||||
@@ -1098,7 +1098,7 @@ test "emit: ERR E3.0 — no DWARF without a debug context (unit-test default)" {
|
||||
try std.testing.expect(std.mem.indexOf(u8, ir_str, "!dbg") == null);
|
||||
}
|
||||
|
||||
// ── issue 0074: FFI arg-type lookup must fail loudly, never silently `.void` ──
|
||||
// ── FFI arg-type lookup must fail loudly, never silently `.void` ──
|
||||
// `argIRTypeOrFail` backs the four FFI call-arg lowering sites (objc_msgSend,
|
||||
// JNI Call<Type>Method / non-virtual / constructor). A ref it cannot resolve is
|
||||
// a codegen invariant violation; it must surface the dedicated `.unresolved`
|
||||
@@ -1145,7 +1145,7 @@ test "emit: argIRTypeOrFail surfaces .unresolved for an unresolvable FFI arg ref
|
||||
try std.testing.expect(emitter.argIRTypeOrFail(bogus) != .void);
|
||||
}
|
||||
|
||||
// ── issue 0075: reflection-builtin arg-type lookup must fail loudly, never `.s64` ──
|
||||
// ── reflection-builtin arg-type lookup must fail loudly, never `.s64` ──
|
||||
// `reflectArgRepr` backs the `type_name` / `type_eq` reflection builtins, which read
|
||||
// their `Type` arg as a boxed `Any` aggregate (`.any` → extract value field) or a bare
|
||||
// i64 TypeId index. A ref it cannot resolve is a codegen invariant violation; it must
|
||||
|
||||
@@ -930,7 +930,7 @@ pub const LLVMEmitter = struct {
|
||||
.vtable => c.LLVMConstNull(llvm_ty), // placeholder — initialized in initVtableGlobals after function declarations
|
||||
// A top-level null-pointer global (`p : *s64 = null;`) and a
|
||||
// zero-initialized global both emit as the all-zero constant
|
||||
// of the global's type (issue 0081).
|
||||
// of the global's type.
|
||||
.null_val, .zeroinit => c.LLVMConstNull(llvm_ty),
|
||||
.undef => c.LLVMGetUndef(llvm_ty),
|
||||
// func_map is empty in Pass 0 (functions are declared in
|
||||
@@ -2527,7 +2527,7 @@ pub const LLVMEmitter = struct {
|
||||
break :blk c.LLVMConstNull(elem_ty);
|
||||
},
|
||||
// A null pointer field and a zero-initialized field both emit as
|
||||
// the all-zero constant of the leaf type (issue 0081).
|
||||
// the all-zero constant of the leaf type.
|
||||
.null_val, .zeroinit => c.LLVMConstNull(elem_ty),
|
||||
.undef => c.LLVMGetUndef(elem_ty),
|
||||
// Vtable constants are only ever produced for top-level protocol
|
||||
|
||||
@@ -50,7 +50,7 @@ test "expr_typer: binary comparison is bool, int arithmetic stays int" {
|
||||
try std.testing.expectEqual(TypeId.s64, l.inferExprType(&add));
|
||||
}
|
||||
|
||||
// issue 0088 (attempt 3): a non-comparison binary op infers the PROMOTED result
|
||||
// A non-comparison binary op infers the PROMOTED result
|
||||
// of (lhs, rhs), not the LHS alone — so a mixed int+float op types as the float
|
||||
// in EITHER operand order (was LHS-biased: `int + float` → s64 while
|
||||
// `float + int` → f64). This is what feeds the typed-const validation that
|
||||
@@ -116,7 +116,7 @@ test "expr_typer: deref of a non-pointer is unresolved" {
|
||||
try std.testing.expectEqual(TypeId.unresolved, l.inferExprType(&deref_n));
|
||||
}
|
||||
|
||||
// issue 0092: a raw `` `f64 `` value binding shadows the builtin numeric type
|
||||
// a raw `` `f64 `` value binding shadows the builtin numeric type
|
||||
// name — `` `f64.epsilon `` (an `.identifier` receiver) must type as the value's
|
||||
// field, NOT the float numeric-limit fold. A bare `f64.epsilon` (a `.type_expr`
|
||||
// receiver, never shadowed) still folds to the queried float type. This pins the
|
||||
@@ -159,8 +159,8 @@ test "expr_typer: raw value binding shadows numeric-limit, bare type still folds
|
||||
try std.testing.expectEqual(TypeId.f64, l.inferExprType(&type_fa));
|
||||
}
|
||||
|
||||
// issue 0093: a raw value binding can shadow a builtin numeric type name through
|
||||
// any of three sources — lexical scope (issue 0092), program globals, or module
|
||||
// a raw value binding can shadow a builtin numeric type name through
|
||||
// any of three sources — lexical scope, program globals, or module
|
||||
// value constants. The shared `identifierBindsValue` guard consults all three,
|
||||
// so a global `` `f32 := Box.{…} `` and a module-const `` `s16 :: Box.{…} `` each
|
||||
// read the value's field (NOT the numeric-limit fold), while a bare `f32.max` /
|
||||
|
||||
@@ -160,8 +160,8 @@ pub const ExprTyper = struct {
|
||||
if (type_name) |tn| {
|
||||
// Skip the fold when a raw value binding shadows the
|
||||
// builtin type name (`` `f64 := … ``) — mirrors the
|
||||
// lowerNumericLimit guard so inference matches lowering
|
||||
// (issues 0092, 0093). The shared helper consults all
|
||||
// lowerNumericLimit guard so inference matches
|
||||
// lowering. The shared helper consults all
|
||||
// three value sources (scope / globals / module consts);
|
||||
// a `.type_expr` receiver is never shadowed.
|
||||
const shadowed = fa.object.data == .identifier and
|
||||
|
||||
@@ -52,7 +52,7 @@ pub const GenericResolver = struct {
|
||||
const info = self.l.module.types.get(ty);
|
||||
return switch (info) {
|
||||
// A nominal type's mangle includes its `nominal_id` when nonzero so two
|
||||
// same-DISPLAY-name authors in different sources (issue 0105) produce
|
||||
// same-DISPLAY-name authors in different sources produce
|
||||
// DISTINCT monomorph symbols (`struct_to_string__Box` vs
|
||||
// `struct_to_string__Box__n1`) instead of one symbol with conflicting
|
||||
// signatures. `nominal_id == 0` (the single-author / structural case)
|
||||
@@ -103,7 +103,7 @@ pub const GenericResolver = struct {
|
||||
}
|
||||
|
||||
/// Append a `__n<id>` disambiguator to a nominal type's display name when its
|
||||
/// `nominal_id` is nonzero (a same-name shadow, issue 0105); id 0 returns the
|
||||
/// `nominal_id` is nonzero (a same-name shadow); id 0 returns the
|
||||
/// name unchanged so single-author mangling is byte-identical.
|
||||
fn mangleNominalName(self: GenericResolver, name: []const u8, nominal_id: u32) []const u8 {
|
||||
if (nominal_id == 0) return name;
|
||||
|
||||
@@ -808,7 +808,7 @@ test "comptime: type_eq builtin on type_tag values" {
|
||||
// ── Test: reflectTypeId reads an Any's runtime TYPE-TAG, not its payload ──
|
||||
// A reflection builtin on an Any must report the type OF a held value (the
|
||||
// tag) and only read the payload when the Any holds a Type value (tag ==
|
||||
// `.any`). Regression for issue 0090 (attempt 3): a boxed value like
|
||||
// `.any`). Regression: a boxed value like
|
||||
// `av : Any = 6` (`{ tag = s64, value = 6 }`) must resolve to `s64`, NOT
|
||||
// `types[6]` (`u8`).
|
||||
test "reflect: reflectTypeId branches on the Any tag" {
|
||||
|
||||
@@ -1082,7 +1082,7 @@ test "lower: vectorLaneIndex maps swizzle components, colour aliases, rejects no
|
||||
try std.testing.expectEqual(@as(?u32, 3), Lowering.vectorLaneIndex("a"));
|
||||
// Any non-lane field is rejected (null) so the read and write paths share
|
||||
// one rule — a non-lane store no longer falls through to an .unresolved
|
||||
// pointee that panics at LLVM emission (issue 0086).
|
||||
// pointee that panics at LLVM emission.
|
||||
try std.testing.expectEqual(@as(?u32, null), Lowering.vectorLaneIndex("q"));
|
||||
try std.testing.expectEqual(@as(?u32, null), Lowering.vectorLaneIndex("xy"));
|
||||
try std.testing.expectEqual(@as(?u32, null), Lowering.vectorLaneIndex("len"));
|
||||
@@ -1301,10 +1301,10 @@ fn countRealBodies(module: *ir_mod.Module, name: []const u8) usize {
|
||||
return n;
|
||||
}
|
||||
|
||||
// fix-0102b: two flat-imported modules each author `greet`. The first-wins merge
|
||||
// two flat-imported modules each author `greet`. The first-wins merge
|
||||
// keeps a.sx's author in the merged decl list (the WINNER) and drops b.sx's,
|
||||
// which the `module_decls` raw facts still retain (0102a). `main` itself can't bare-call `greet`
|
||||
// — under fix-0102c two flat authors make that ambiguous — so it calls a.sx's
|
||||
// — with two flat authors this is ambiguous; two flat authors make that ambiguous — so it calls a.sx's
|
||||
// `use_greet` wrapper, whose own-author call to `greet` binds a.sx's winner.
|
||||
// BEFORE the identity-addressable pass, only the winner has a real body — the
|
||||
// shadowed author has no slot at all (the pre-fix symptom: one `greet`).
|
||||
@@ -1420,8 +1420,8 @@ test "lower: shadowed same-name author gets its own FuncId + real body (fix-0102
|
||||
// pointer for BOTH same-name authors — the exact pointers `fn_ast_map` and
|
||||
// the `module_decls` raw facts carry — not a per-iteration switch-capture
|
||||
// temporary. If the winner were keyed by `&fd` (the scanDecls bug), this
|
||||
// lookup by the stable `fn_ast_map` pointer would miss (null). fix-0102c
|
||||
// routes calls through exactly these pointers, so the round-trip must hold.
|
||||
// lookup by the stable `fn_ast_map` pointer would miss (null). Bare-call
|
||||
// routing goes through exactly these pointers, so the round-trip must hold.
|
||||
const winner_fd = lowering.program_index.fn_ast_map.get("greet").?;
|
||||
const winner_fid = lowering.fn_decl_fids.get(winner_fd);
|
||||
try std.testing.expect(winner_fid != null);
|
||||
@@ -1442,7 +1442,7 @@ test "lower: shadowed same-name author gets its own FuncId + real body (fix-0102
|
||||
try std.testing.expect(shadow_fid != null);
|
||||
try std.testing.expect(shadow_fid.? != winner_fid.?);
|
||||
|
||||
// fix-0102c / Phase C: THE bare-name selector routes per caller file over the
|
||||
// Phase C: THE bare-name selector routes per caller file over the
|
||||
// Phase A author collector. `main` flat-imports two `greet` authors and is its
|
||||
// own author of neither → a bare `greet()` from `main` is ambiguous. a.sx
|
||||
// authors the WINNER, so its bare `greet` resolves through the existing path
|
||||
|
||||
@@ -214,13 +214,13 @@ pub const Lowering = struct {
|
||||
/// Identity map: authoring `*const ast.FnDecl` → the FuncId `declareFunction`
|
||||
/// created for it. The name-keyed function table (`resolveFuncByName`) returns
|
||||
/// the FIRST author of a name, so two same-name authors collide there; this
|
||||
/// map addresses each author's OWN slot by decl identity (fix-0102b), letting
|
||||
/// map addresses each author's OWN slot by decl identity, letting
|
||||
/// a SHADOWED author lower its body into a distinct FuncId.
|
||||
fn_decl_fids: std.AutoHashMap(*const ast.FnDecl, FuncId),
|
||||
/// FuncId-keyed lowered tracking — the identity twin of `lowered_functions`
|
||||
/// (which keys by name). A shadowed same-name author shares the winner's name
|
||||
/// but not its FuncId, so name-keyed tracking can't tell them apart; this
|
||||
/// records which specific FuncIds have had a real body lowered (fix-0102b).
|
||||
/// records which specific FuncIds have had a real body lowered.
|
||||
lowered_fids: std.AutoHashMap(FuncId, void),
|
||||
local_fn_counter: u32 = 0, // unique counter for mangling local function names
|
||||
/// Per-declaration nominal identity bookkeeping (E2). The FIRST source to
|
||||
@@ -228,7 +228,7 @@ pub const Lowering = struct {
|
||||
/// byte-identical to pre-E2 single-author registration); a later registration
|
||||
/// of the same name from a DIFFERENT source is a same-name SHADOW and gets a
|
||||
/// fresh id from `next_nominal_id`, so the two authors intern to DISTINCT
|
||||
/// TypeIds (closing issue 0105's last-wins collapse). `nominal_name_authors`
|
||||
/// TypeIds (closing the last-wins collapse). `nominal_name_authors`
|
||||
/// records each name's first author source to make that decision.
|
||||
nominal_name_authors: std.AutoHashMap(types.StringId, []const u8),
|
||||
next_nominal_id: u32 = 0,
|
||||
@@ -419,7 +419,7 @@ pub const Lowering = struct {
|
||||
/// `restore` puts the caller's state back. Lowering a callee must be
|
||||
/// transparent to the caller's own lowering — notably `block_terminated`,
|
||||
/// which leaking back would mark the caller's trailing statements
|
||||
/// dead-after-terminator (issue 0100 F2).
|
||||
/// dead-after-terminator.
|
||||
pub const FnBodyReentry = struct {
|
||||
l: *Lowering,
|
||||
func: ?FuncId,
|
||||
@@ -543,7 +543,7 @@ pub const Lowering = struct {
|
||||
// params aren't pushed into `self.scope` until body lowering, so bind
|
||||
// them into a temporary scope here; otherwise `inferExprType` can't
|
||||
// resolve `x`, the inference yields `.unresolved`, and that reaches LLVM
|
||||
// emission as `func.ret` (issue 0059). Whether it slipped through used to
|
||||
// emission as `func.ret`. Whether it slipped through used to
|
||||
// depend on a same-named binding lingering from earlier lowering.
|
||||
var tmp_scope = Scope.init(self.alloc, self.scope);
|
||||
defer tmp_scope.deinit();
|
||||
@@ -647,7 +647,7 @@ pub const Lowering = struct {
|
||||
/// bare-visible only inside the callee's own module — namespaced-only from the
|
||||
/// call site's view. Post-E1 the bare leaf is source-aware, so resolving that
|
||||
/// return type in the CALL SITE's context would wrongly reject it (the type
|
||||
/// analog of the issue-0100-F1 source pin that lowers a namespaced fn body in
|
||||
/// analog of the namespaced-fn-body source pin that lowers a namespaced fn body in
|
||||
/// its own module's context). `src == null` falls back to the call site's
|
||||
/// context unchanged.
|
||||
pub fn resolveTypeInSource(self: *Lowering, src: ?[]const u8, type_ann: *const Node) TypeId {
|
||||
@@ -706,13 +706,13 @@ pub const Lowering = struct {
|
||||
/// value / module-const tables the stateful lowering owns) and is narrowed to
|
||||
/// `u32` through the single range-checked `program_index.foldDimU32` — never a
|
||||
/// bare `@intCast`, so an oversized-but-valid `i64` dim (`[5_000_000_000]`)
|
||||
/// diagnoses instead of panicking the compiler (issue 0087). A dimension that
|
||||
/// diagnoses instead of panicking the compiler. A dimension that
|
||||
/// isn't a compile-time integer (or doesn't fit a `u32`) is a hard error:
|
||||
/// emit a diagnostic so the driver aborts (`hasErrors()`), then return a
|
||||
/// harmless `0` so body lowering finishes without touching the `.unresolved`
|
||||
/// sentinel (which would `@panic` in `sizeOf` mid-lowering, before the
|
||||
/// diagnostic surfaces). The diagnostic — not the returned length — is what
|
||||
/// guarantees no garbage ships (issue 0083).
|
||||
/// guarantees no garbage ships.
|
||||
pub fn resolveArrayLen(self: *Lowering, len_node: *const Node) ?u32 {
|
||||
const result = program_index_mod.foldDimU32(len_node, self, 0);
|
||||
if (result == .ok) return result.ok;
|
||||
@@ -720,7 +720,7 @@ pub const Lowering = struct {
|
||||
// shared diagnostic (single wording source — `program_index.reportDimError`,
|
||||
// also used by the stateless alias path so the two cannot diverge) and
|
||||
// return null so `resolveCompound` yields the `.unresolved` sentinel — NO
|
||||
// fabricated length (issue 0083: a `0` here gives a 0-byte alloca and OOB
|
||||
// fabricated length (a `0` here gives a 0-byte alloca and OOB
|
||||
// element access). Lowering the binding never computes the failed type's
|
||||
// size: `alloca` records the type but defers `sizeOf` to LLVM emission,
|
||||
// which the emitted diagnostic pre-empts via `hasErrors()`, and a
|
||||
@@ -764,7 +764,7 @@ pub const Lowering = struct {
|
||||
/// True iff `name` is a FLOAT-valued module const (`F : f64 : 2.5`,
|
||||
/// `K : f64 : 4.0`, untyped `M :: 4.0`, untyped-EXPR `ME :: 4.0 + 1.0`). The
|
||||
/// int folder's division arm consults this so a `/` with a float-const operand
|
||||
/// is recognised as float division (issue 0095 / F0.11-6). Comptime / generic
|
||||
/// is recognised as float division. Comptime / generic
|
||||
/// value bindings are always integer-valued, so only the module-const table
|
||||
/// can name a float.
|
||||
pub fn nameIsFloatTyped(self: *Lowering, name: []const u8) bool {
|
||||
@@ -894,7 +894,7 @@ pub const Lowering = struct {
|
||||
.identifier => |id| return self.resolveNominalLeaf(id.name, id.is_raw, node.span),
|
||||
// A non-spread tuple literal in a type position is a tuple-type
|
||||
// literal (`(s32, s32)`); validate its elements are types and reject
|
||||
// non-type elements loudly (issue 0067).
|
||||
// non-type elements loudly.
|
||||
.tuple_literal => return self.resolveTupleLiteralTypeArg(node),
|
||||
else => return type_bridge.resolveAstType(node, &self.module.types, &self.program_index.type_alias_map, &self.program_index.module_const_map),
|
||||
}
|
||||
@@ -912,7 +912,7 @@ pub const Lowering = struct {
|
||||
/// (`Vector(4, f32)`), a module/generic const (`Vector(N, f32)`), and a const
|
||||
/// expression (`Vector(M + 1, f32)`) all resolve identically, and the i64→u32
|
||||
/// narrowing is range-checked (an oversized lane diagnoses instead of
|
||||
/// panicking — issue 0087). A non-const lane (`Vector(get(), f32)`) or a
|
||||
/// panicking). A non-const lane (`Vector(get(), f32)`) or a
|
||||
/// non-positive one emits a clean diagnostic and returns null; the caller
|
||||
/// yields `.unresolved` rather than fabricating a `<0 x float>` lane count
|
||||
/// that crashes LLVM verification.
|
||||
@@ -1045,7 +1045,7 @@ pub const Lowering = struct {
|
||||
pub fn emitFieldError(self: *Lowering, obj_ty: TypeId, field: []const u8, span: ast.Span) Ref {
|
||||
// A field access on an already-`.unresolved` object is a cascade from an
|
||||
// upstream type-resolution failure that was ALREADY diagnosed (e.g. an
|
||||
// unresolvable / oversized array dimension — issue 0083). The
|
||||
// unresolvable / oversized array dimension). The
|
||||
// `.unresolved` sentinel never exists without an accompanying error, so
|
||||
// piling a second "field not found on unresolved" onto the real one is
|
||||
// pure noise; stay silent and return a placeholder so lowering finishes
|
||||
@@ -1173,7 +1173,7 @@ pub const Lowering = struct {
|
||||
}
|
||||
|
||||
/// Human-readable description of a typed module-const initializer, used in
|
||||
/// the issue-0088 type-mismatch diagnostic. A literal names its kind; a
|
||||
/// the typed-const type-mismatch diagnostic. A literal names its kind; a
|
||||
/// const-expression is described by its inferred type category, so the
|
||||
/// message is accurate for `N : string : M + 2` ("an integer expression")
|
||||
/// as well as for `N : string : 4` ("an integer literal").
|
||||
|
||||
@@ -34,7 +34,7 @@ pub fn lowerCall(self: *Lowering, c_in: *const ast.Call) Ref {
|
||||
// function. `TypeName(val)` is not a cast (casts are `cast(T, val)`), so
|
||||
// there is no ambiguity. Rewrite the callee to an identifier so the
|
||||
// normal call machinery resolves it, symmetric to the bare-value
|
||||
// reference that already resolves via scope/globals (issue 0089).
|
||||
// reference that already resolves via scope/globals.
|
||||
//
|
||||
// Scoped to RAW provenance: only a backtick (`is_raw`) or `#import c`
|
||||
// foreign fn declaration may legally carry a reserved-name spelling
|
||||
@@ -55,7 +55,7 @@ pub fn lowerCall(self: *Lowering, c_in: *const ast.Call) Ref {
|
||||
c = rewritten;
|
||||
};
|
||||
}
|
||||
// fix-0102 F2 / R5 §C: select the bare / value-UFCS same-name call author
|
||||
// R5 §C: select the bare / value-UFCS same-name call author
|
||||
// ONCE, via `CallResolver.selectedFreeAuthor` — the SINGLE producer of
|
||||
// this verdict, the exact same one `CallResolver.plan` consumes for typing.
|
||||
// The call-path consumers (default expansion, param typing, dispatch) all
|
||||
@@ -128,7 +128,7 @@ pub fn lowerCall(self: *Lowering, c_in: *const ast.Call) Ref {
|
||||
// If argument is a bare function name, create a proper closure from it
|
||||
if (arg.data == .identifier) {
|
||||
const fn_name = arg.data.identifier.name;
|
||||
// fix-0102d site 2: `closure(fn)` over a genuine flat same-name
|
||||
// `closure(fn)` over a genuine flat same-name
|
||||
// collision must capture the RESOLVED author's FuncId, not the
|
||||
// first-wins winner's. Plain bare name only; `.ambiguous`
|
||||
// → loud diagnostic; `.none` → existing first-wins path.
|
||||
@@ -188,7 +188,7 @@ pub fn lowerCall(self: *Lowering, c_in: *const ast.Call) Ref {
|
||||
}
|
||||
break :blk scoped;
|
||||
};
|
||||
// fix-0102 F2 / R5 §C: the early pack/comptime/generic dispatch reads
|
||||
// R5 §C: the early pack/comptime/generic dispatch reads
|
||||
// the SAME author the call resolver SELECTED — not the first-wins
|
||||
// winner — whenever a genuine flat same-name collision rerouted the
|
||||
// call (`sel_author != null`). The selector only ever returns a plain
|
||||
@@ -455,14 +455,14 @@ pub fn lowerCall(self: *Lowering, c_in: *const ast.Call) Ref {
|
||||
}
|
||||
}
|
||||
}
|
||||
// fix-0102c / R5 §C: a genuine flat same-name collision — bind the
|
||||
// R5 §C: a genuine flat same-name collision — bind the
|
||||
// author the call resolver selected (own-author-wins, or the single
|
||||
// flat-reachable author), or reject a bare call to a name ≥2
|
||||
// imported modules author. `selectedFreeAuthor` (computed once
|
||||
// above, and the exact verdict `plan` consumes for typing) is the
|
||||
// single producer; lowering CONSUMES it rather than re-resolving
|
||||
// the name, so typing and dispatch read the SAME author and can't
|
||||
// disagree (fix-0102 F2). Reached only for an identifier callee, so
|
||||
// disagree. Reached only for an identifier callee, so
|
||||
// `sel_author` / `author_ambiguous` here are the bare verdict.
|
||||
if (author_ambiguous) {
|
||||
if (self.diagnostics) |d|
|
||||
@@ -476,7 +476,7 @@ pub fn lowerCall(self: *Lowering, c_in: *const ast.Call) Ref {
|
||||
const params = func.params;
|
||||
// The RESOLVED author's decl drives variadic packing — not a
|
||||
// first-wins re-lookup by name, whose variadic shape may
|
||||
// differ (fix-0102c F1).
|
||||
// differ.
|
||||
self.packVariadicCallArgs(sf.decl, c, &args);
|
||||
const final_args = self.prependCtxIfNeeded(func, args.items);
|
||||
self.coerceCallArgs(final_args, params);
|
||||
@@ -956,13 +956,13 @@ pub fn lowerCall(self: *Lowering, c_in: *const ast.Call) Ref {
|
||||
// Try to resolve as bare function name (free-function UFCS:
|
||||
// `recv.fn(args)` → `fn(recv, args)`). Lazily lower the body —
|
||||
// a function reached ONLY via UFCS would otherwise be declared
|
||||
// but never emitted (issue 0063: undefined symbol at link).
|
||||
// but never emitted (undefined symbol at link).
|
||||
//
|
||||
// fix-0102d site 3 / R5 §C: a free-function UFCS target with a
|
||||
// R5 §C: a free-function UFCS target with a
|
||||
// genuine flat same-name collision dispatches to the author the
|
||||
// call PLAN selected for the receiver's source — the SAME author
|
||||
// plan typed the call's result as, so dispatch and typing can't
|
||||
// disagree (fix-0102 F2; without this, a string-typed winner over
|
||||
// disagree (without this, a string-typed winner over
|
||||
// an s64 shadow boxes a raw int as a string pointer → segfault).
|
||||
// The plan is the single producer; lowering consumes its verdict
|
||||
// (`sel_author` / `cplan.ambiguous_collision`, computed once above)
|
||||
@@ -991,7 +991,7 @@ pub fn lowerCall(self: *Lowering, c_in: *const ast.Call) Ref {
|
||||
// Same implicit address-of as a struct-defined method: if the
|
||||
// free function's first param is `*T` and the receiver is a
|
||||
// value `T`, pass its address instead of a by-value copy
|
||||
// (issue 0063).
|
||||
|
||||
self.fixupMethodReceiver(&method_args, func, effective_obj_node, obj_ty);
|
||||
const final_args = self.prependCtxIfNeeded(func, method_args.items);
|
||||
self.coerceCallArgs(final_args, params);
|
||||
@@ -1517,7 +1517,7 @@ pub fn tryLowerReflectionCall(self: *Lowering, name: []const u8, c: *const ast.C
|
||||
// Strict `$T: Type` guard for the type-introspection builtins. A
|
||||
// value argument (`6`, `true`, `5.2`, a struct) is rejected with a
|
||||
// diagnostic instead of being silently reinterpreted as a TypeId
|
||||
// index / sized via its `typeof` (issue 0090). One shared
|
||||
// index / sized via its `typeof`. One shared
|
||||
// classification covers all 7; it runs before dispatch.
|
||||
if (self.reflectionTypeArgGuard(name, c)) |sentinel| return sentinel;
|
||||
|
||||
@@ -1776,7 +1776,7 @@ pub fn reflectionArgIsType(self: *Lowering, arg: *const Node) bool {
|
||||
/// `field_count`, `type_name`, `type_eq`, `type_is_unsigned`,
|
||||
/// `is_flags`): every argument must denote a type. A value argument is
|
||||
/// rejected with a diagnostic rather than silently reinterpreted as a
|
||||
/// TypeId index or sized via its `typeof` (issue 0090).
|
||||
/// TypeId index or sized via its `typeof`.
|
||||
///
|
||||
/// Returns null when `name` is not a guarded builtin OR every argument
|
||||
/// is a type (→ fall through to normal dispatch). Returns a harmless
|
||||
@@ -1866,7 +1866,7 @@ pub fn expandCallDefaults(self: *Lowering, c: *const ast.Call, sel_author: ?*con
|
||||
}
|
||||
break :blk2 scoped;
|
||||
};
|
||||
// fix-0102d site 1 / R5 §C: for a genuine flat same-name
|
||||
// R5 §C: for a genuine flat same-name
|
||||
// collision the omitted trailing args are filled from the
|
||||
// author the call resolver selected — its `*FnDecl` defaults —
|
||||
// not the first-wins winner's. lowering consumes the ONE author
|
||||
@@ -2097,7 +2097,7 @@ pub fn resolveCallParamTypes(self: *Lowering, c: *const ast.Call, sel_author: ?*
|
||||
break :blk scoped;
|
||||
};
|
||||
|
||||
// fix-0102c F2 / R5 §C: a genuine flat same-name collision must type this
|
||||
// R5 §C: a genuine flat same-name collision must type this
|
||||
// call's args against the author the call resolver selected, not the
|
||||
// first-wins winner's params. lowering consumes the ONE author verdict
|
||||
// (`selectedFreeAuthor`, computed once in `lowerCall`) rather than
|
||||
|
||||
@@ -189,7 +189,7 @@ pub fn lowerLambda(self: *Lowering, lam: *const ast.Lambda) Ref {
|
||||
// `defer`s, not the enclosing function's. Open a fresh defer window
|
||||
// (like `lowerFunction`/`monomorphizeFunction`) and restore on exit —
|
||||
// otherwise lowering a closure literal inside a `defer` body re-enters
|
||||
// the enclosing function's defer drain (infinite recursion — issue 0073).
|
||||
// the enclosing function's defer drain (infinite recursion).
|
||||
const saved_func_defer_base = self.func_defer_base;
|
||||
const saved_defer_len = self.defer_stack.items.len;
|
||||
defer {
|
||||
|
||||
@@ -543,8 +543,8 @@ pub fn zeroValue(self: *Lowering, ty: TypeId) Ref {
|
||||
};
|
||||
}
|
||||
|
||||
/// Emit the unified non-integral float→int narrowing diagnostic (F0.11 /
|
||||
/// issue 0095). ONE wording, ONE place: every site that rejects an implicit
|
||||
/// Emit the unified non-integral float→int narrowing diagnostic (F0.11).
|
||||
/// ONE wording, ONE place: every site that rejects an implicit
|
||||
/// narrowing of a non-integral compile-time float to an integer type calls
|
||||
/// this, so the message + fix-it stay identical across the typed-binding
|
||||
/// coerce arm, the field/param-default sites, the typed-const path, and the
|
||||
|
||||
@@ -45,7 +45,7 @@ pub fn constArrayLiteral(self: *Lowering, elements: []const *const Node, array_t
|
||||
/// Try to convert a single AST expression into a compile-time ConstantValue.
|
||||
/// `expected_ty` is the destination element/field type — it lets aggregate
|
||||
/// leaves (struct literals, nested arrays) serialize with the correct shape
|
||||
/// rather than collapsing to null (issue 0080). Returns null if the
|
||||
/// rather than collapsing to null. Returns null if the
|
||||
/// expression is not constant-foldable here.
|
||||
pub fn constExprValue(self: *Lowering, expr: *const Node, expected_ty: TypeId) ?inst_mod.ConstantValue {
|
||||
return switch (expr.data) {
|
||||
@@ -66,7 +66,7 @@ pub fn constExprValue(self: *Lowering, expr: *const Node, expected_ty: TypeId) ?
|
||||
.undef_literal => .zeroinit,
|
||||
// A `null` in a pointer (or optional-pointer) field is a
|
||||
// compile-time constant: the zero pointer. Without this arm the
|
||||
// aggregate is wrongly rejected as non-constant (issue 0081).
|
||||
// aggregate is wrongly rejected as non-constant.
|
||||
.null_literal => .null_val,
|
||||
.unary_op => |uo| switch (uo.op) {
|
||||
.negate => switch (uo.operand.data) {
|
||||
@@ -80,7 +80,7 @@ pub fn constExprValue(self: *Lowering, expr: *const Node, expected_ty: TypeId) ?
|
||||
.struct_literal => |sl| self.constStructLiteral(&sl, expected_ty),
|
||||
// An enum tag as an aggregate leaf (`[2]Color = .[.green, .blue]`, or
|
||||
// an enum field inside a global struct) serializes to its tag int
|
||||
// against the leaf's declared enum type (issue 0082).
|
||||
// against the leaf's declared enum type.
|
||||
.enum_literal => |el| self.constEnumLiteral(&el, expected_ty, expr.span),
|
||||
else => null,
|
||||
};
|
||||
@@ -92,7 +92,7 @@ pub fn constExprValue(self: *Lowering, expr: *const Node, expected_ty: TypeId) ?
|
||||
/// (`enum { a; b :: 5; }`); the enum's backing width is applied by the
|
||||
/// const emitters via the destination type's LLVM type. Plain enums only —
|
||||
/// a tagged-union or non-enum destination is diagnosed loudly rather than
|
||||
/// silently zero-initialized (issue 0082).
|
||||
/// silently zero-initialized.
|
||||
pub fn constEnumLiteral(self: *Lowering, el: *const ast.EnumLiteral, ty: TypeId, span: ast.Span) ?inst_mod.ConstantValue {
|
||||
if (!ty.isBuiltin()) {
|
||||
const info = self.module.types.get(ty);
|
||||
@@ -543,7 +543,7 @@ pub fn lowerComptimeCall(self: *Lowering, fd: *const ast.FnDecl, call_node: *con
|
||||
// its return type + anything it `#insert`s, e.g. `build_format` / `out`
|
||||
// / `emit` inside `std.print` / `log.*`), so those bare names resolve
|
||||
// in the defining module's visibility context rather than the call
|
||||
// site's (issue 0106). The call-site ARGS above are deliberately lowered
|
||||
// site's. The call-site ARGS above are deliberately lowered
|
||||
// BEFORE this, in the caller's context. Mirrors `lowerFunctionBodyInto`,
|
||||
// which switches to `func.source_file`. The defining path is stamped on
|
||||
// the body node by `resolveImports`; a sourceless body keeps the
|
||||
@@ -848,7 +848,7 @@ const ConstAuthor = union(enum) {
|
||||
///
|
||||
/// - **own-wins**: the querying module's OWN const author is selected outright.
|
||||
/// - else the FLAT-import-reachable const authors: exactly one → it; ≥2 distinct
|
||||
/// → `.ambiguous` (issue 0105 / 0760 — never a silent first-/last-wins pick).
|
||||
/// → `.ambiguous` (never a silent first-/last-wins pick).
|
||||
/// - none visible → `.none` (a namespaced-only const must be qualified `ns.X`;
|
||||
/// a non-const name folds to `.none` too).
|
||||
///
|
||||
|
||||
@@ -779,7 +779,7 @@ pub fn lowerMatch(self: *Lowering, me: *const ast.MatchExpr) Ref {
|
||||
if (has_value_merge) {
|
||||
// Lower the arm body against the merge's result type so literals
|
||||
// (and negated literals) in the arm pick the right width — the
|
||||
// phi operands must all match `result_type` (issue 0066).
|
||||
// phi operands must all match `result_type`.
|
||||
const saved_arm_target = self.target_type;
|
||||
self.target_type = result_type;
|
||||
const maybe_v = self.lowerBlockValue(arm.body);
|
||||
@@ -790,7 +790,7 @@ pub fn lowerMatch(self: *Lowering, me: *const ast.MatchExpr) Ref {
|
||||
// Only materialize a value + branch to the merge when the arm
|
||||
// body did NOT diverge. A diverging arm (e.g. `return x`) has
|
||||
// already terminated its block; emitting the fallback const
|
||||
// here would land AFTER the terminator (the issue-0057 bug).
|
||||
// here would land AFTER the terminator .
|
||||
if (!self.currentBlockHasTerminator()) {
|
||||
var v = maybe_v orelse if (result_type == .string or !result_type.isBuiltin())
|
||||
self.builder.constUndef(result_type)
|
||||
|
||||
@@ -92,7 +92,7 @@ pub fn lowerRoot(self: *Lowering, root: *const Node) void {
|
||||
// any error before codegen.
|
||||
self.errorFlow().checkErrorFlow(decls);
|
||||
// Pass 1f: reject identifiers used in a type position that name no
|
||||
// declared type / primitive / in-scope generic param (issue 0064).
|
||||
// declared type / primitive / in-scope generic param.
|
||||
// Runs after scanning (so every real type name is registered) and
|
||||
// before body lowering, so the diagnostic halts via `core.zig`
|
||||
// `hasErrors()` before the empty-struct stub can reach codegen. Owned by
|
||||
@@ -447,11 +447,11 @@ pub fn scanDecls(self: *Lowering, decls: []const *const Node) void {
|
||||
// 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). A float-valued const resolves to a dimension only when
|
||||
// 0. A float-valued const resolves to a dimension only when
|
||||
// its value is integral (`floatToIntExact`); pre-registering it keeps the
|
||||
// forward-alias float path identical to the int path. The dimension only
|
||||
// needs the value, so a placeholder type is fine; pass 2 overwrites typed
|
||||
// consts with the resolved annotation type (issue 0070).
|
||||
// consts with the resolved annotation type.
|
||||
for (decls) |decl| {
|
||||
if (decl.data != .const_decl) continue;
|
||||
const cd = decl.data.const_decl;
|
||||
@@ -467,7 +467,7 @@ pub fn scanDecls(self: *Lowering, decls: []const *const Node) void {
|
||||
// A const whose RHS is an integer EXPRESSION over other consts
|
||||
// (`M :: 2; N :: M + 1`) is itself a usable count: register it so
|
||||
// `moduleConstInt` can fold the RHS through `evalConstIntExpr`
|
||||
// (issue 0083). Placeholder `.s64` type — the count consumers read
|
||||
//. Placeholder `.s64` type — the count consumers read
|
||||
// only the value; if the expression doesn't fold (references a
|
||||
// non-const), `moduleConstInt` yields null and the use diagnoses.
|
||||
.binary_op, .unary_op => {
|
||||
@@ -483,7 +483,7 @@ pub fn scanDecls(self: *Lowering, decls: []const *const Node) void {
|
||||
// self (`next: *Box`), or a forward / mutual ref to a shadow declared LATER
|
||||
// in the same module (`peer: *Node`) — then binds to its OWN nominal TypeId
|
||||
// via `type_decl_tids`, never the global findByName first-author fallback
|
||||
// (issue 0105).
|
||||
|
||||
//
|
||||
// "Genuine" = ≥2 DISTINCT decls of the SAME KIND in THIS scan author the name
|
||||
// (so it needs ≥2 distinct nominal TypeIds). Grouping by (kind, name) keeps a
|
||||
@@ -529,7 +529,7 @@ pub fn scanDecls(self: *Lowering, decls: []const *const Node) void {
|
||||
// re-introduces a same-named function (e.g. a second module
|
||||
// also exporting `parse`) must NOT clobber the AST while the
|
||||
// function table keeps the first — that split lowers one
|
||||
// signature against the other's body (issue 0100). The
|
||||
// signature against the other's body. The
|
||||
// shadowed function stays reachable via its qualified name.
|
||||
if (!self.program_index.fn_ast_map.contains(fd.name)) {
|
||||
self.program_index.fn_ast_map.put(fd.name, &decl.data.fn_decl) catch {};
|
||||
@@ -574,7 +574,7 @@ pub fn scanDecls(self: *Lowering, decls: []const *const Node) void {
|
||||
// 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). For a
|
||||
// sizeOf (no fabricated 0-length array). For a
|
||||
// top-level array alias, re-fold the dimension so an
|
||||
// oversized / negative constant emits the SAME precise
|
||||
// message as the direct form (`a : [N]T`) via the shared
|
||||
@@ -710,7 +710,7 @@ pub fn scanDecls(self: *Lowering, decls: []const *const Node) void {
|
||||
// Typed value constants (`AF_INET :s32: 2`) are registered in
|
||||
// pass 2 below — after the forward-alias fixpoint — so a
|
||||
// forward identifier alias in the annotation resolves to its
|
||||
// target instead of a fabricated stub (issue 0070). Untyped
|
||||
// target instead of a fabricated stub. Untyped
|
||||
// literal constants carry no annotation to resolve, so they
|
||||
// stay here (their type comes from the literal / inference).
|
||||
if (cd.type_annotation == null) {
|
||||
@@ -765,7 +765,7 @@ pub fn scanDecls(self: *Lowering, decls: []const *const Node) void {
|
||||
},
|
||||
// Top-level globals are registered in a second pass (below),
|
||||
// after the forward-alias fixpoint, so a forward identifier
|
||||
// alias used as a global's type annotation resolves (issue 0070).
|
||||
// alias used as a global's type annotation resolves.
|
||||
.var_decl => {},
|
||||
else => {},
|
||||
}
|
||||
@@ -773,7 +773,7 @@ pub fn scanDecls(self: *Lowering, decls: []const *const Node) void {
|
||||
self.resolveForwardIdentifierAliases(decls);
|
||||
// Pass 2: registrations that resolve a top-level type annotation run
|
||||
// after the alias fixpoint, so a forward identifier alias used as the
|
||||
// annotation resolves to its target (issue 0070).
|
||||
// annotation resolves to its target.
|
||||
for (decls) |decl| {
|
||||
self.setCurrentSourceFile(decl.source_file);
|
||||
switch (decl.data) {
|
||||
@@ -788,14 +788,14 @@ pub fn scanDecls(self: *Lowering, decls: []const *const Node) void {
|
||||
/// scanDecls pass 2 (after `resolveForwardIdentifierAliases`) so a forward
|
||||
/// identifier alias in the annotation (`A :: B; B :: s32; K : A : 42;`)
|
||||
/// resolves to its target rather than a fabricated empty-struct stub, which
|
||||
/// would otherwise mistype the constant (issue 0070).
|
||||
/// would otherwise mistype the constant.
|
||||
pub fn registerTypedModuleConst(self: *Lowering, cd: *const ast.ConstDecl) void {
|
||||
const ta = cd.type_annotation orelse return;
|
||||
// Only initializer shapes that pass 0 (binary_op / unary_op → placeholder
|
||||
// `.s64`) or the literal path register as a USABLE module const need
|
||||
// reconciling against the annotation. Every other shape (call,
|
||||
// struct/array literal, bare identifier) is never registered as a
|
||||
// foldable / emittable const, so it cannot manifest the issue-0088
|
||||
// foldable / emittable const, so it cannot manifest a
|
||||
// wrong-type fold/emit; a use-site diagnostic covers it.
|
||||
switch (cd.value.data) {
|
||||
.int_literal, .float_literal, .bool_literal, .string_literal, .undef_literal, .null_literal, .binary_op, .unary_op => {},
|
||||
@@ -901,8 +901,7 @@ pub fn typedConstInitFits(self: *Lowering, value: *const Node, dst_ty: TypeId) b
|
||||
// Const-EXPRESSION initializer (binary_op / unary_op — the only
|
||||
// non-literal kinds the caller admits): validate by the initializer's
|
||||
// INFERRED type so coverage is type-based, not a per-node-kind
|
||||
// allowlist where an unenumerated kind silently escapes (issue 0088,
|
||||
// attempt 2). The integer/float fit mirrors the literal arms above.
|
||||
// allowlist where an unenumerated kind silently escapes. The integer/float fit mirrors the literal arms above.
|
||||
else => self.constExprInitFits(self.inferExprType(value), dst_ty),
|
||||
};
|
||||
}
|
||||
@@ -930,7 +929,7 @@ pub fn constExprInitFits(self: *Lowering, init_ty: TypeId, dst_ty: TypeId) bool
|
||||
/// in the type annotation (`A :: B; B :: s32; g : A = 7;`) resolves to its
|
||||
/// target instead of a fabricated empty-struct stub, which would otherwise
|
||||
/// give the global a type that mismatches its initializer at LLVM
|
||||
/// verification (issue 0070). Globals can't be named in a type position, so
|
||||
/// verification. Globals can't be named in a type position, so
|
||||
/// deferring them past type/alias registration introduces no ordering hazard.
|
||||
pub fn registerTopLevelGlobal(self: *Lowering, vd: *const ast.VarDecl) void {
|
||||
// Use self.resolveType so type aliases like `Handle :: u32;` resolve
|
||||
@@ -967,7 +966,7 @@ pub fn registerTopLevelGlobal(self: *Lowering, vd: *const ast.VarDecl) void {
|
||||
/// Foreign globals (extern symbol) and value-less declarations carry no
|
||||
/// payload — they default to zero/extern at link, which is correct. An
|
||||
/// identifier initializer that names a module constant is materialized from
|
||||
/// the recorded constant (`K : A : 42; g : A = K;` → 42, issue 0071); a
|
||||
/// the recorded constant (`K : A : 42; g : A = K;` → 42); a
|
||||
/// global initialized from an identifier that resolves to no usable constant
|
||||
/// is rejected with a diagnostic rather than silently zero-initialized — a
|
||||
/// global has no run site for a dynamic initializer.
|
||||
@@ -1026,7 +1025,7 @@ pub fn globalInitValue(self: *Lowering, vd: *const ast.VarDecl, var_ty: TypeId)
|
||||
// Any other initializer shape (`.field_access` on a const, a call, an
|
||||
// arithmetic expression, …) is not a static constant the compiler can
|
||||
// evaluate here. Diagnose loudly rather than emit a null payload that
|
||||
// silently zero-initializes the global (issues 0071/0072).
|
||||
// silently zero-initializes the global.
|
||||
else => blk: {
|
||||
if (self.diagnostics) |d|
|
||||
d.addFmt(.err, v.span, "global '{s}' must be initialized by a compile-time constant", .{vd.name});
|
||||
@@ -1038,7 +1037,7 @@ pub fn globalInitValue(self: *Lowering, vd: *const ast.VarDecl, var_ty: TypeId)
|
||||
/// A global aggregate initializer (array/struct literal) that does not fully
|
||||
/// reduce to a compile-time constant is rejected loudly. Without this the
|
||||
/// `null` payload would fall through to a zero-initialized global, silently
|
||||
/// dropping the declared fields (issues 0071/0072/0080).
|
||||
/// dropping the declared fields.
|
||||
pub fn diagnoseNonConstGlobal(self: *Lowering, vd: *const ast.VarDecl, v: *const Node) ?inst_mod.ConstantValue {
|
||||
if (self.diagnostics) |d|
|
||||
d.addFmt(.err, v.span, "global '{s}' must be initialized by a compile-time constant", .{vd.name});
|
||||
@@ -1049,11 +1048,11 @@ pub fn diagnoseNonConstGlobal(self: *Lowering, vd: *const ast.VarDecl, v: *const
|
||||
/// file. The forward scan above only registers an alias (`A :: B`) when `B`
|
||||
/// is already resolved as a type author; a forward target isn't yet present,
|
||||
/// so `A` is left unregistered and its uses get falsely flagged as an unknown
|
||||
/// type (issue 0069). Re-resolve to a fixpoint now that every top-level name
|
||||
/// type. Re-resolve to a fixpoint now that every top-level name
|
||||
/// has been seen, so `A :: B; B :: s32;` converges the same as the ordered
|
||||
/// `B :: s32; A :: B;`. A value const is never an `.identifier` node
|
||||
/// (`NotAType :: 123` is an int literal), and an alias whose target is a value
|
||||
/// const stays unresolved, so neither this pass nor issue 0068 can register a
|
||||
/// const stays unresolved, so neither this pass nor the unknown-type suppression can register a
|
||||
/// non-type name.
|
||||
///
|
||||
/// SOURCE-AWARE (R5 §4, E1.5). The target `B` is resolved AS SEEN FROM `A`'s
|
||||
@@ -1116,8 +1115,7 @@ pub fn aliasResolvedInSource(self: *Lowering, src: []const u8, name: []const u8)
|
||||
/// Pass 2: Lower main function body and comptime side-effects.
|
||||
pub fn lowerMainAndComptime(self: *Lowering, decls: []const *const Node) void {
|
||||
for (decls) |decl| {
|
||||
// A `#run` body lowers in its OWN module's source context (fix-0102d
|
||||
// site 4): `NAME :: #run f()` written in an imported module must
|
||||
// A `#run` body lowers in its OWN module's source context: `NAME :: #run f()` written in an imported module must
|
||||
// resolve a bare `f` from that module's flat imports, not the main
|
||||
// file's. Without this, `selectPlainCallableAuthor` runs with the main
|
||||
// file's perspective and reports a genuine per-source author as
|
||||
@@ -1154,15 +1152,15 @@ pub fn lowerMainAndComptime(self: *Lowering, decls: []const *const Node) void {
|
||||
|
||||
/// Lower every SHADOWED same-name function author into its OWN FuncId with a
|
||||
/// real (non-extern) body — the identity-addressable lowering PATH this step
|
||||
/// adds (fix-0102b). It does NOT run during a default compile: the name path
|
||||
/// stays the sole resolver, so the suite is byte-for-byte unchanged. fix-0102c
|
||||
/// invokes it as part of routing bare flat calls to the right author; until
|
||||
/// adds. It does NOT run during a default compile: the name path
|
||||
/// stays the sole resolver, so the suite is byte-for-byte unchanged. The bare-call
|
||||
/// disambiguation invokes it as part of routing bare flat calls to the right author; until
|
||||
/// then it is exercised by the lower-test regression that asserts two distinct
|
||||
/// non-extern bodies for a same-name collision.
|
||||
///
|
||||
/// The first-wins flat/directory merge keeps exactly one author per name in
|
||||
/// the merged decl list; `scanDecls` declares that WINNER (lowered on demand
|
||||
/// through the name-keyed `lazyLowerFunction`). fix-0102a retained every
|
||||
/// through the name-keyed `lazyLowerFunction`). The merge retains every
|
||||
/// dropped same-name author in the `module_decls` raw facts (path → name →
|
||||
/// `RawDeclRef`) without touching resolution; this walks that index, filters
|
||||
/// each author to its `*FnDecl` (`fnDeclOfRaw`), and gives each shadowed
|
||||
@@ -1211,7 +1209,7 @@ pub fn lowerRetainedSameNameAuthors(self: *Lowering) void {
|
||||
}
|
||||
}
|
||||
|
||||
/// Result of bare-call disambiguation (fix-0102c, now over the Phase B
|
||||
/// Result of bare-call disambiguation (now over the Phase B
|
||||
/// author collector).
|
||||
pub const BareCallee = union(enum) {
|
||||
/// Bind the call to this specific author, carried as the shared
|
||||
@@ -1219,7 +1217,7 @@ pub const BareCallee = union(enum) {
|
||||
/// materialized on demand. Every callee-signature decision in the call
|
||||
/// path (variadic packing, param typing, default expansion) reads the
|
||||
/// RESOLVED author from this one object — never a first-wins re-lookup
|
||||
/// by name (fix-0102c F1).
|
||||
/// by name.
|
||||
func: SelectedFunc,
|
||||
/// ≥2 distinct flat authors are reachable from the caller and none is
|
||||
/// the caller's own — the bare call can't pick one; require a qualifier.
|
||||
@@ -1285,13 +1283,13 @@ pub const TypeHeadResolution = union(enum) {
|
||||
/// would mis-size it).
|
||||
not_visible,
|
||||
/// ≥2 DISTINCT same-name type authors are flat-visible from the querying
|
||||
/// source and none is its own (E2, issue 0105). The selection is genuinely
|
||||
/// source and none is its own (E2). The selection is genuinely
|
||||
/// ambiguous: `resolveNominalLeaf` emits a loud diagnostic and returns the
|
||||
/// `.unresolved` poison sentinel — never a silent first-/last-wins pick.
|
||||
ambiguous,
|
||||
};
|
||||
|
||||
/// THE plain bare-name call selector (fix-0102c, R5 §C). `resolveBareCallee`'s
|
||||
/// THE plain bare-name call selector. `resolveBareCallee`'s
|
||||
/// body verbatim, now over the Phase B author collector
|
||||
/// (`resolver.collectVisibleAuthors` — the ONE graph-walk) instead of a direct
|
||||
/// `module_decls` + `flat_import_graph` traversal. Routes a bare identifier
|
||||
@@ -1372,7 +1370,7 @@ pub fn selectPlainCallableAuthor(self: *Lowering, name: []const u8, caller_file:
|
||||
/// selected author's OWN source — never the global `findByName` first-match
|
||||
/// nor the global `type_alias_map`.
|
||||
///
|
||||
/// `raw` is the backtick raw-identifier escape (issue 0089): a raw reference
|
||||
/// `raw` is the backtick raw-identifier escape: a raw reference
|
||||
/// bypasses the builtin classifier and resolves only through the nominal
|
||||
/// author / alias path.
|
||||
///
|
||||
@@ -1454,7 +1452,7 @@ pub fn selectNominalLeaf(self: *Lowering, name: []const u8, from: []const u8, ra
|
||||
const author_set = res_walk.collectVisibleAuthors(name, from, .user_bare_flat);
|
||||
defer if (author_set.flat.len > 0) self.alloc.free(author_set.flat);
|
||||
|
||||
// 1a. Own type author wins outright (own-wins, issue 0105/0107).
|
||||
// 1a. Own type author wins outright (own-wins).
|
||||
if (author_set.own) |own| switch (own.raw) {
|
||||
.const_decl => {
|
||||
// Type alias: present in type_aliases_by_source → resolved.
|
||||
@@ -1462,7 +1460,7 @@ pub fn selectNominalLeaf(self: *Lowering, name: []const u8, from: []const u8, ra
|
||||
if (inner.get(name)) |tid| return .{ .resolved = tid };
|
||||
}
|
||||
// Own const_decl not yet resolved: pending (own takes priority
|
||||
// over any flat author — prevents issue 0107 flat-preemption).
|
||||
// over any flat author — prevents flat-preemption).
|
||||
return .pending;
|
||||
},
|
||||
else => if (isNamedTypeKind(own.raw)) {
|
||||
@@ -1559,7 +1557,7 @@ pub fn isNamedTypeKind(raw: resolver_mod.RawDeclRef) bool {
|
||||
/// mid-registration → the caller yields the legacy empty-struct stub). A
|
||||
/// STRUCT resolves first through its `type_decl_tids` nominal identity (E2)
|
||||
/// keyed by the raw-facts decl pointer, so two same-name struct authors in
|
||||
/// different sources resolve to their OWN distinct TypeIds (issue 0105). A
|
||||
/// different sources resolve to their OWN distinct TypeIds. A
|
||||
/// `type_decl_tids` MISS falls back to the global `findByName` — correct for a
|
||||
/// SINGLE-author struct registered via a non-`internNamedTypeDecl` path (a
|
||||
/// `struct #compiler`, a protocol-backed struct, a generic instance) or before
|
||||
@@ -1773,7 +1771,7 @@ pub fn selectedFuncId(self: *Lowering, sf: *SelectedFunc, name: []const u8) Func
|
||||
/// winner): the winner owns the name-keyed slot and lowers through the
|
||||
/// normal lazy path, so `selectPlainCallableAuthor` returns `.none` for it. A shadow
|
||||
/// is declared a fresh same-name FuncId in its OWN module's visibility
|
||||
/// context and its body lowered into that slot via fix-0102b's identity-
|
||||
/// context and its body lowered into that slot via the identity-
|
||||
/// addressable `lowerFunctionBodyInto`. Idempotent: `lowered_fids` tracks
|
||||
/// which slots already carry a body.
|
||||
pub fn bareAuthorFuncId(self: *Lowering, fd: *const ast.FnDecl, name: []const u8, path: []const u8) FuncId {
|
||||
@@ -1868,7 +1866,7 @@ pub fn declareFunction(self: *Lowering, fd: *const ast.FnDecl, name: []const u8)
|
||||
/// modules each exporting a top-level `parse` otherwise collide in the
|
||||
/// bare-name `fn_ast_map` / function table (last-wins) while `resolveFuncByName`
|
||||
/// picks the first declared, so `lazyLowerFunction` lowers one signature
|
||||
/// against the other's body and trips its param-count assert (issue 0100).
|
||||
/// against the other's body and trips its param-count assert.
|
||||
/// The bare recursion in `scanDecls` still registers intra-module bare calls;
|
||||
/// this adds the qualified identity the `pkg.fn(...)` resolution paths in
|
||||
/// `CallResolver.plan` / `lowerCall` already prefer.
|
||||
@@ -1894,7 +1892,7 @@ pub fn registerQualifiedFn(self: *Lowering, ns_name: []const u8, fd: *const ast.
|
||||
// comptime / pack functions (`Vector`, `print`, `any_to_string`) are
|
||||
// dispatched by monomorphization off their BARE template name, not the
|
||||
// plain `resolveFuncByName` / `lazyLowerFunction` path that trips the
|
||||
// collision assert (issue 0100); registering a qualified alias for them
|
||||
// collision assert; registering a qualified alias for them
|
||||
// would divert that machinery and strand a per-call type binding.
|
||||
if (fd.type_params.len > 0 or hasComptimeParams(fd) or isPackFn(fd)) return;
|
||||
// Foreign / builtin / #compiler bodies keep their literal name; a
|
||||
@@ -1911,7 +1909,7 @@ pub fn registerQualifiedFn(self: *Lowering, ns_name: []const u8, fd: *const ast.
|
||||
// `registerNamespaceQualifiedFns` pins `current_source_file` to the
|
||||
// decl's source before each call). `lazyLowerFunction`'s null-FuncId
|
||||
// path restores this so `ns.fn`'s body lowers in its own module's
|
||||
// visibility context, not the call site's (issue 0100 F1).
|
||||
// visibility context, not the call site's.
|
||||
if (self.current_source_file) |src| {
|
||||
self.program_index.qualified_fn_source.put(qualified, src) catch {};
|
||||
}
|
||||
@@ -2032,7 +2030,7 @@ pub fn lazyLowerFunction(self: *Lowering, name: []const u8) void {
|
||||
// Find the existing extern stub (from scanDecls), keyed by NAME — the
|
||||
// FIRST author of a name owns this slot. A shadowed same-name author is
|
||||
// not here (it has no name-keyed slot); it is lowered out-of-line into
|
||||
// its OWN FuncId by `lowerRetainedSameNameAuthors` (fix-0102b).
|
||||
// its OWN FuncId by `lowerRetainedSameNameAuthors`.
|
||||
const name_id = self.module.types.internString(name);
|
||||
var func_id: ?FuncId = null;
|
||||
for (self.module.functions.items, 0..) |func, i| {
|
||||
@@ -2048,7 +2046,7 @@ pub fn lazyLowerFunction(self: *Lowering, name: []const u8) void {
|
||||
}
|
||||
|
||||
// Function not yet declared — create it fresh via lowerFunction. A
|
||||
// module-qualified alias (`ns.fn`, issue 0100) is registered in
|
||||
// module-qualified alias (`ns.fn`) is registered in
|
||||
// `fn_ast_map` without an eager `declareFunction`, so there's no
|
||||
// `Function.source_file` to switch to. Restore the alias's OWN declaring
|
||||
// source before lowering its body, otherwise it lowers in the caller's
|
||||
@@ -2067,9 +2065,9 @@ pub fn lazyLowerFunction(self: *Lowering, name: []const u8) void {
|
||||
/// real function. Identity-addressable: the caller passes the exact FuncId,
|
||||
/// so a SHADOWED same-name author lowers into its OWN slot instead of
|
||||
/// colliding on the name-keyed `resolveFuncByName` (which returns the first
|
||||
/// author, the very split that trips issue 0100's param-count assert). Self-
|
||||
/// author, the very split that trips the param-count assert). Self-
|
||||
/// contained — the `FnBodyReentry` guard makes the nested lowering
|
||||
/// transparent to any in-progress caller body (issue 0100 F2) — so it serves
|
||||
/// transparent to any in-progress caller body — so it serves
|
||||
/// both `lazyLowerFunction`'s name-keyed found path and the out-of-line
|
||||
/// `lowerRetainedSameNameAuthors` pass.
|
||||
pub fn lowerFunctionBodyInto(self: *Lowering, fd: *const ast.FnDecl, fid: FuncId, name: []const u8) void {
|
||||
@@ -2086,7 +2084,7 @@ pub fn lowerFunctionBodyInto(self: *Lowering, fd: *const ast.FnDecl, fid: FuncId
|
||||
|
||||
// Re-use the existing function slot — switch builder to it. Pin the
|
||||
// function's OWN source BEFORE resolving the return type, so a same-name
|
||||
// shadowed type in the signature (issue 0105) resolves against THIS
|
||||
// shadowed type in the signature resolves against THIS
|
||||
// function's module rather than the caller's (which, importing two
|
||||
// same-name authors, would be ambiguous). Param types below already
|
||||
// resolve after this point.
|
||||
@@ -2229,7 +2227,7 @@ pub fn lowerFunction(self: *Lowering, fd: *const ast.FnDecl, name: []const u8, i
|
||||
// Record the declaring source so the function carries its own module
|
||||
// for diagnostics/emit and for any later `lazyLowerFunction` re-entry
|
||||
// that switches to `func.source_file`. The caller sets
|
||||
// `current_source_file` to the decl's source before lowering (issue 0100 F1).
|
||||
// `current_source_file` to the decl's source before lowering.
|
||||
self.builder.currentFunc().source_file = self.current_source_file;
|
||||
|
||||
// Set linkage. Default for fn defs is `internal` (LLVM DCE-friendly,
|
||||
|
||||
@@ -472,7 +472,7 @@ pub fn lowerFieldAccess(self: *Lowering, fa: *const ast.FieldAccess, span: ast.S
|
||||
// const of the queried type (sibling of the identifier-receiver
|
||||
// intercepts above). Placed AFTER `Struct.CONST` so a user const named
|
||||
// `min`/`max` wins on its own struct; a builtin type name can never
|
||||
// name a user struct (reserved — issue 0076), so they never collide.
|
||||
// name a user struct (reserved), so they never collide.
|
||||
if (self.lowerNumericLimit(fa, span)) |ref| return ref;
|
||||
|
||||
// M1.3 — `obj.class` on any Obj-C-class pointer lowers to
|
||||
@@ -558,9 +558,9 @@ pub fn lowerFieldAccess(self: *Lowering, fa: *const ast.FieldAccess, span: ast.S
|
||||
/// constants `module_const_map`. The numeric-limit intercept must defer to
|
||||
/// ordinary field access whenever ANY of the three binds the name, so a
|
||||
/// raw value field read is never hijacked into a numeric-limit fold
|
||||
/// (issues 0092 local / 0093 global + module-const). A single helper used
|
||||
/// (locals, globals, and module-consts alike). A single helper used
|
||||
/// by both lowering and inference keeps the two resolvers in lockstep
|
||||
/// (issue-0083 two-resolver defect class).
|
||||
/// (two-resolver defect class).
|
||||
pub fn identifierBindsValue(self: *Lowering, name: []const u8) bool {
|
||||
if (self.scope) |scope| {
|
||||
if (scope.lookup(name) != null) return true;
|
||||
@@ -600,7 +600,7 @@ pub fn lowerNumericLimit(self: *Lowering, fa: *const ast.FieldAccess, span: ast.
|
||||
// value is an ordinary field read, not a numeric-limit fold — defer to
|
||||
// the normal field-access path when the receiver identifier resolves to
|
||||
// a value binding through any of scope / globals / module consts
|
||||
// (issues 0092, 0093). A `.type_expr` receiver is unambiguously a type
|
||||
//. A `.type_expr` receiver is unambiguously a type
|
||||
// and can never be value-shadowed.
|
||||
if (fa.object.data == .identifier and self.identifierBindsValue(name)) return null;
|
||||
|
||||
@@ -684,7 +684,7 @@ pub fn lowerOptionalChain(self: *Lowering, obj: Ref, fa: *const ast.FieldAccess,
|
||||
/// aliases `.r`/`.g`/`.b`/`.a`) to its lane index. Returns null for any
|
||||
/// other field name so the read path (`lowerFieldAccessOnType`) and the
|
||||
/// write path (`lowerAssignment`) share one resolver and reject a
|
||||
/// non-lane field identically (issue 0086).
|
||||
/// non-lane field identically.
|
||||
pub fn vectorLaneIndex(field: []const u8) ?u32 {
|
||||
if (std.mem.eql(u8, field, "x") or std.mem.eql(u8, field, "r")) return 0;
|
||||
if (std.mem.eql(u8, field, "y") or std.mem.eql(u8, field, "g")) return 1;
|
||||
@@ -1079,7 +1079,7 @@ pub fn lowerArrayLiteral(self: *Lowering, al: *const ast.ArrayLiteral) Ref {
|
||||
// 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
|
||||
// header off of. 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);
|
||||
@@ -1570,7 +1570,7 @@ pub fn lowerExpr(self: *Lowering, node: *const Node) Ref {
|
||||
}
|
||||
// F2: emit the SOURCE-AWARE author's value (own-wins), not the
|
||||
// global last-wins `ci_global`. ≥2 flat-visible same-name const
|
||||
// authors → a loud ambiguity (issue 0105 / 0760), never a silent
|
||||
// authors → a loud ambiguity, never a silent
|
||||
// pick. `.none` after a visible name is the registration-only
|
||||
// author (no per-source partition) — emit its global value.
|
||||
switch (self.selectModuleConst(id.name)) {
|
||||
@@ -1607,7 +1607,7 @@ pub fn lowerExpr(self: *Lowering, node: *const Node) Ref {
|
||||
const str = self.builder.constString(sid);
|
||||
break :blk self.builder.boxAny(str, .string);
|
||||
}
|
||||
// fix-0102d site 2: taking a bare same-name fn as a VALUE
|
||||
// taking a bare same-name fn as a VALUE
|
||||
// (func_ref, fn-ptr / closure coercion) must capture the
|
||||
// RESOLVED author's FuncId for a genuine flat collision, not
|
||||
// the first-wins winner's. Plain bare name only; `.ambiguous`
|
||||
|
||||
@@ -73,7 +73,7 @@ pub fn monomorphizeFunction(self: *Lowering, fd: *const ast.FnDecl, mangled_name
|
||||
// — e.g. `List(T).append`'s `alloc: Allocator` default-param type, or a
|
||||
// body reference to a type visible only in the template's module —
|
||||
// resolves where it is visible, not at the (possibly cross-module) call
|
||||
// site. This is the issue-0100-F1 plain-fn pin extended to generic
|
||||
// site. This is the namespaced-fn-body plain-fn pin extended to generic
|
||||
// instantiation; without it the non-transitive bare-TYPE gate (E4) would
|
||||
// reject a 2-flat-hop library type the call site cannot see directly.
|
||||
// A synthesized / sourceless body keeps the caller's context.
|
||||
@@ -291,7 +291,7 @@ pub fn isStaticTypeRef(self: *Lowering, node: *const Node) bool {
|
||||
/// must itself denote a type; a non-type element — e.g. the `1` in
|
||||
/// `(s32, 1)` — is a user error. Emit a diagnostic pointing at the offending
|
||||
/// element and return `.unresolved`; never fabricate a tuple with a bogus
|
||||
/// field (issue 0067). type_bridge.resolveAstType builds the tuple only after
|
||||
/// field. type_bridge.resolveAstType builds the tuple only after
|
||||
/// this validation passes.
|
||||
pub fn resolveTupleLiteralTypeArg(self: *Lowering, node: *const Node) TypeId {
|
||||
for (node.data.tuple_literal.elements) |el| {
|
||||
@@ -806,7 +806,7 @@ pub fn hasComptimeParams(fd: *const ast.FnDecl) bool {
|
||||
/// A plain free function: no type params (not generic) and an ordinary sx
|
||||
/// body (not `#foreign` / `#builtin` / `#compiler`). Only these get an
|
||||
/// out-of-line identity-addressable slot — the bare-call disambiguation
|
||||
/// (fix-0102c) and the shadow-author lowering pass leave every other shape
|
||||
/// and the shadow-author lowering pass leave every other shape
|
||||
/// to the existing name-keyed dispatch.
|
||||
pub fn isPlainFreeFn(fd: *const ast.FnDecl) bool {
|
||||
if (fd.type_params.len > 0) return false;
|
||||
@@ -835,7 +835,7 @@ pub fn isPlainFreeFn(fd: *const ast.FnDecl) bool {
|
||||
pub fn resolveValueParamArg(self: *Lowering, arg_node: *const Node, param_name: []const u8, type_name: ?[]const u8) ?i64 {
|
||||
// Resolve an ALIASED integer constraint (`$K: Count` where `Count :: u32`,
|
||||
// `$K: Small` where `Small :: s8`) to its underlying builtin so the range
|
||||
// gate below treats it exactly like `$K: u32` / `$K: s8` (issue 0083 — an
|
||||
// gate below treats it exactly like `$K: u32` / `$K: s8` (an
|
||||
// alias previously slipped past `intTypeRange`, so `Box(5_000_000_000)`
|
||||
// with `$K: Count` bound a truncated value). A non-integer / unrecognised
|
||||
// constraint yields null → no range bound (fold only), as before.
|
||||
|
||||
@@ -68,7 +68,7 @@ pub fn stampNominalId(info: types.TypeInfo, nid: u32) types.TypeInfo {
|
||||
/// BEFORE any field resolves, so a self / forward / mutual reference to a shadow
|
||||
/// name (`next: *Box`; `peer: *Node` where Node is a shadow declared later)
|
||||
/// binds to ITS nominal TypeId via `type_decl_tids` instead of the global
|
||||
/// findByName first-author fallback (issue 0105 / F1). Called only from the
|
||||
/// findByName first-author fallback. Called only from the
|
||||
/// `scanDecls` genuine-shadow pass, which has already established that ≥2
|
||||
/// distinct struct decls author this name; ALL of them reserve — the FIRST at
|
||||
/// id 0, the rest at fresh nonzero ids — so none falls through to the name-only
|
||||
@@ -192,7 +192,7 @@ pub fn reserveShadowSlot(self: *Lowering, td: ShadowTypeDecl) void {
|
||||
/// orelse intern) — BYTE-IDENTICAL to pre-E2 registration. For a genuinely
|
||||
/// multi-authored name, the FIRST source keeps id 0 and later sources get
|
||||
/// fresh ids → DISTINCT TypeIds, so the authors no longer collapse last-wins
|
||||
/// (issue 0105). Idempotent per `decl_key`: a re-registration — OR an up-front
|
||||
///. Idempotent per `decl_key`: a re-registration — OR an up-front
|
||||
/// shadow reservation — reuses the recorded slot, refreshing its body via
|
||||
/// `updatePreservingKey` (key-stable because a struct's intern key is its
|
||||
/// name + nominal id, not its fields).
|
||||
@@ -593,7 +593,7 @@ pub fn registerStructDecl(self: *Lowering, sd: *const ast.StructDecl, source_fil
|
||||
// shadow author's slot was already reserved before fields resolved, so this
|
||||
// fills it (key-stable updatePreservingKey); a first / single author adopts
|
||||
// any forward-reference stub. Same-name structs in DIFFERENT sources get
|
||||
// distinct TypeIds instead of last-wins clobbering the first (issue 0105).
|
||||
// distinct TypeIds instead of last-wins clobbering the first.
|
||||
const info: types.TypeInfo = .{ .@"struct" = .{ .name = name_id, .fields = fields.items } };
|
||||
_ = self.internNamedTypeDecl(decl_key, name_id, info, nominal_id);
|
||||
|
||||
|
||||
@@ -627,7 +627,7 @@ pub fn lowerPackFnCall(self: *Lowering, fd: *const ast.FnDecl, call_node: *const
|
||||
// `target_type`) would cast to the stale target — e.g. `format("…", xx i)`
|
||||
// inside a `-> string` fn mis-typed the arg as `string`, monomorphizing
|
||||
// `__pack_string` and ABI-coercing the 4-byte int as a 16-byte fat
|
||||
// pointer → memory corruption (issue 0057).
|
||||
// pointer → memory corruption.
|
||||
const saved_pack_tt = self.target_type;
|
||||
self.target_type = null;
|
||||
var pack_refs = std.ArrayList(Ref).empty;
|
||||
@@ -895,7 +895,7 @@ pub fn monomorphizePackFn(
|
||||
// OWN module (E4), so a 2-flat-hop library type named in the signature is
|
||||
// bare-visible — mirrors the body pin further down and the
|
||||
// `monomorphizeFunction` pin. The comptime call-site args below are
|
||||
// lowered AFTER this restore, in the caller's context (issue 0106).
|
||||
// lowered AFTER this restore, in the caller's context.
|
||||
const saved_sig_src = self.current_source_file;
|
||||
if (fd.body.source_file) |src| self.setCurrentSourceFile(src);
|
||||
|
||||
@@ -1023,7 +1023,7 @@ pub fn monomorphizePackFn(
|
||||
// Pin to the metaprogram's OWN module for the BODY lowering only, so its
|
||||
// bare names (and anything it `#insert`s — e.g. `build_format` / `out` /
|
||||
// `emit` inside `std.print`) resolve in the defining module's visibility
|
||||
// context, not the call site's (issue 0106). The comptime-param call-site
|
||||
// context, not the call site's. The comptime-param call-site
|
||||
// args above were deliberately lowered FIRST, in the caller's context.
|
||||
// Mirrors `lowerFunctionBodyInto`, which switches to `func.source_file`;
|
||||
// the defining path is stamped on the body node by `resolveImports`. A
|
||||
|
||||
@@ -689,7 +689,7 @@ pub fn lowerAssignment(self: *Lowering, asgn: *const ast.Assignment) void {
|
||||
// the shared lvalue resolver — the same one the address-of
|
||||
// and multi-target store paths use — so the three never
|
||||
// resolve a field to a different slot or default field 0
|
||||
// (issue 0094 / issue-0083 two-resolver class). fl.ptr is
|
||||
// (two-resolver defect class). fl.ptr is
|
||||
// *field_ty (the store handler unwraps one pointer level);
|
||||
// fl.ty is the value type to coerce the rhs to.
|
||||
const src_ty = self.builder.getRefType(val);
|
||||
@@ -701,7 +701,7 @@ pub fn lowerAssignment(self: *Lowering, asgn: *const ast.Assignment) void {
|
||||
// diagnostic the read path uses (emitFieldError) and bail;
|
||||
// building a pointer with field_ty = .unresolved would
|
||||
// otherwise store through a pointer-to-.unresolved that
|
||||
// panics at LLVM emission (issue 0094).
|
||||
// panics at LLVM emission.
|
||||
_ = self.emitFieldError(obj_ty, fa.field, asgn.target.span);
|
||||
}
|
||||
},
|
||||
@@ -786,7 +786,7 @@ const FieldLvalue = struct { ptr: Ref, ty: TypeId };
|
||||
/// Single source of lvalue field resolution shared by all three store/
|
||||
/// address-of sites — lowerAssignment (single-target store), lowerExprAsPtr
|
||||
/// (address-of), and lowerMultiAssign (multi-target store) — so they never
|
||||
/// resolve a field to a different slot or default field 0 (issue 0094).
|
||||
/// resolve a field to a different slot or default field 0.
|
||||
pub fn fieldLvaluePtr(self: *Lowering, obj_ptr: Ref, obj_ty: TypeId, field: []const u8) ?FieldLvalue {
|
||||
if (obj_ty.isBuiltin()) return null;
|
||||
const field_name_id = self.module.types.internString(field);
|
||||
@@ -918,7 +918,7 @@ pub fn lowerExprAsPtr(self: *Lowering, node: *const Node) Ref {
|
||||
// field-not-found diagnostic (lowerFieldAccessOnType →
|
||||
// emitFieldError) instead of silently GEPing field 0 as .s64;
|
||||
// that bogus pointer reaches LLVM emission as ptrTo(.unresolved)
|
||||
// and panics (issue 0094).
|
||||
// and panics.
|
||||
if (self.fieldLvaluePtr(obj_ptr, obj_ty, fa.field)) |r| return r.ptr;
|
||||
return self.emitFieldError(obj_ty, fa.field, node.span);
|
||||
},
|
||||
@@ -1150,7 +1150,7 @@ pub fn lowerMultiAssign(self: *Lowering, ma: *const ast.MultiAssign) void {
|
||||
// the same one address-of uses — so a missing field emits a
|
||||
// diagnostic instead of defaulting to field 0 / field_ty
|
||||
// .unresolved, which silently corrupted a neighbouring field
|
||||
// (or panicked at LLVM emission) (issue 0094).
|
||||
// (or panicked at LLVM emission).
|
||||
if (self.fieldLvaluePtr(obj_ptr, obj_ty, fa.field)) |r| {
|
||||
const val_ty = self.builder.getRefType(val);
|
||||
const store_val = if (val_ty != r.ty and val_ty != .void and r.ty != .void)
|
||||
|
||||
@@ -392,7 +392,7 @@ test "moduleConstInt gates the fold on the declared type, not the initializer no
|
||||
|
||||
// An `int_literal` value node folds to an integer ONLY when the declared
|
||||
// type is numeric. A `string`/`bool`-typed const carrying an integer-looking
|
||||
// initializer must never be folded into a count (issue 0088): the count path
|
||||
// initializer must never be folded into a count: the count path
|
||||
// consults `ModuleConstInfo.ty`, not just the node shape.
|
||||
var int_val = nLit(4);
|
||||
try map.put("OK", .{ .value = &int_val, .ty = .s64 });
|
||||
@@ -405,8 +405,8 @@ test "moduleConstInt gates the fold on the declared type, not the initializer no
|
||||
|
||||
// The same gate holds for a const-EXPRESSION value node (`M + 2`), not just
|
||||
// a bare literal: a `string`-typed const whose initializer is a foldable
|
||||
// integer expression must still never fold as a count (issue 0088 attempt 2 —
|
||||
// the const-expression leak). `KEXPR : s64 : M + 2` (numeric type) folds; the
|
||||
// integer expression must still never fold as a count (the
|
||||
// const-expression leak). `KEXPR : s64 : M + 2` (numeric type) folds; the
|
||||
// same expression declared `string` does not.
|
||||
var m_lit = nLit(2);
|
||||
var add2 = nLit(2);
|
||||
@@ -471,7 +471,7 @@ test "evalConstFloatExpr folds comptime float expressions, halts on runtime leav
|
||||
// an expression like `F + 0.25` (= 2.75) is now recognised as a compile-time
|
||||
// float and rejected by the narrowing rule instead of silently truncating;
|
||||
// `F + 1.5` (= 4.0) is integral and folds. This completes the evaluator for
|
||||
// float-const-leaf expressions (issue 0095, attempt 3).
|
||||
// float-const-leaf expressions.
|
||||
var f = nIdent("F");
|
||||
var quarter = nFloat(0.25);
|
||||
var three_half = nFloat(1.5);
|
||||
@@ -486,7 +486,7 @@ test "evalConstFloatExpr folds comptime float expressions, halts on runtime leav
|
||||
// `type_resolver.floatLimitFor`. It folds as a direct leaf AND inside an
|
||||
// expression: `f64.max - f64.max` = 0.0 (integral → folds), `f64.true_min +
|
||||
// 0.5` = 0.5 (non-integral → the narrowing rule rejects it). A non-limit
|
||||
// field on a float type is not a leaf → null (issue 0095, attempt 5 parity).
|
||||
// field on a float type is not a leaf → null.
|
||||
var f64ty = nIdent("f64");
|
||||
var f32ty = nIdent("f32");
|
||||
var fmax = nField(&f64ty, "max");
|
||||
@@ -542,7 +542,7 @@ test "a backtick raw-shadow receiver is a field read, not a numeric-limit fold (
|
||||
// field access is an ordinary runtime field READ, so it is NOT a compile-time
|
||||
// leaf in either evaluator (→ null), exactly as the sibling `isFloatValuedExpr`
|
||||
// already treats it. The whole point: a value-shadow can never be misread as
|
||||
// the builtin limit (issue 0095 / F0.11-7).
|
||||
// the builtin limit.
|
||||
var f64raw = nIdentRaw("f64");
|
||||
var s8raw = nIdentRaw("s8");
|
||||
var raw_feps = nField(&f64raw, "epsilon");
|
||||
@@ -594,7 +594,7 @@ test "foldCountI64 / foldDimU32 fold an integral float count, reject a non-integ
|
||||
try std.testing.expectEqual(pi.DimU32{ .below_min = -4 }, pi.foldDimU32(&negf, ctx, 0));
|
||||
}
|
||||
|
||||
test "the int folder refuses a FLOAT division (issue 0095 / F0.11-6)" {
|
||||
test "the int folder refuses a FLOAT division" {
|
||||
const eval = pi.evalConstIntExpr;
|
||||
const ctx = DimCtx{}; // K : f64 : 4.0 (integral float const), M = 4 (int const)
|
||||
|
||||
|
||||
@@ -146,7 +146,7 @@ pub fn isFloatConstType(ty: TypeId) bool {
|
||||
/// division apart from an integer one even when both operands fold to integers
|
||||
/// (`K / 3`, `ME / 3`). `frame` cycle-guards a const whose value references
|
||||
/// another const; a name already on the chain has no compile-time value → not
|
||||
/// float-valued (issue 0095 / F0.11-6).
|
||||
/// float-valued.
|
||||
fn moduleConstFloatValuedFramed(consts: *const std.StringHashMap(ModuleConstInfo), table: *const types.TypeTable, name: []const u8, parent: ?*const ModuleConstFrame) bool {
|
||||
if (moduleConstFrameContains(parent, name)) return false;
|
||||
const ci = consts.get(name) orelse return false;
|
||||
@@ -160,7 +160,7 @@ fn moduleConstFloatValuedFramed(consts: *const std.StringHashMap(ModuleConstInfo
|
||||
/// int via `floatToIntExact`). `moduleConstIntFramed` consults this so a count
|
||||
/// is gated on `ModuleConstInfo.ty`, not just the shape of the initializer node:
|
||||
/// a `string`/`bool`/pointer/struct-typed const can never be folded into a count
|
||||
/// off an integer-looking initializer (issue 0088 — the second symptom, where
|
||||
/// off an integer-looking initializer (the second symptom, where
|
||||
/// `N : string : 4` folded `[N]s64` to 4 by reading the `int_literal` node and
|
||||
/// ignoring the `string` annotation).
|
||||
pub fn isCountableConstType(table: *const types.TypeTable, ty: TypeId) bool {
|
||||
@@ -187,7 +187,7 @@ fn moduleConstIntFramed(consts: *const std.StringHashMap(ModuleConstInfo), table
|
||||
/// 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.
|
||||
/// than the direct form (`a : [N]T`, stateful) — that miscompile class.
|
||||
/// Every const's RHS is folded through the shared `evalConstIntExpr`, so an
|
||||
/// untyped (`N :: 16`) / typed (`N : s64 : 16`) literal, an integral float
|
||||
/// (`N : f64 : 4.0` → 4, via `floatToIntExact`; `4.5` → null), AND an expression
|
||||
@@ -225,7 +225,7 @@ pub fn moduleConstFloat(consts: *const std.StringHashMap(ModuleConstInfo), table
|
||||
/// (`ME :: 4.0 + 1.0`, whose placeholder type is `s64`), and a non-integral float
|
||||
/// const (`F : f64 : 2.5`). SINGLE source for the stateful (`Lowering`) and
|
||||
/// stateless (`type_bridge`) division-arm float checks, so they agree on which
|
||||
/// const-leaf divisions are float (issue 0095 / F0.11-6).
|
||||
/// const-leaf divisions are float.
|
||||
pub fn moduleConstIsFloatTyped(consts: *const std.StringHashMap(ModuleConstInfo), table: *const types.TypeTable, name: []const u8) bool {
|
||||
return moduleConstFloatValuedFramed(consts, table, name, null);
|
||||
}
|
||||
@@ -235,8 +235,8 @@ pub fn moduleConstIsFloatTyped(consts: *const std.StringHashMap(ModuleConstInfo)
|
||||
/// numeric-limit (`f64.max`), or arithmetic over any of those. THE predicate the
|
||||
/// int folder's division arm consults: `/` with a float operand is FLOAT division
|
||||
/// (`5.0 / 2.0` = 2.5), and folding it with integer truncating division would
|
||||
/// silently accept a non-integral float at a count / typed binding (issue 0095 /
|
||||
/// F0.11-6). `+ - *` agree between int and float arithmetic for the integral
|
||||
/// silently accept a non-integral float at a count / typed binding.
|
||||
/// `+ - *` agree between int and float arithmetic for the integral
|
||||
/// operands the int folder ever sees (a non-integral operand folds to null first),
|
||||
/// so ONLY `/` needs this guard. A leaf name resolves through `ctx.nameIsFloatTyped`
|
||||
/// — the same ctx that supplies `lookupDimName`/`lookupFloatName` — so an INTEGRAL
|
||||
@@ -257,8 +257,7 @@ pub fn isFloatValuedExpr(node: *const Node, ctx: anytype) bool {
|
||||
.field_access => |fa| blk: {
|
||||
// A backtick RAW receiver (`` `f64.epsilon ``) is an ordinary field
|
||||
// READ on a value whose spelling shadows a builtin type, NOT the
|
||||
// numeric-limit accessor — so it is not a float leaf (issues 0092 /
|
||||
// 0093). Only a BARE type receiver folds to a float limit.
|
||||
// numeric-limit accessor — so it is not a float leaf. Only a BARE type receiver folds to a float limit.
|
||||
const obj_name: ?[]const u8 = switch (fa.object.data) {
|
||||
.identifier => |id| if (id.is_raw) null else id.name,
|
||||
.type_expr => |te| if (te.is_raw) null else te.name,
|
||||
@@ -279,7 +278,7 @@ pub fn isFloatValuedExpr(node: *const Node, ctx: anytype) bool {
|
||||
/// integer-expression folder for the compiler — array dimensions (`[N]T`,
|
||||
/// `[M + 1]T`), Vector lane counts (`Vector(N, f32)`), generic value-param
|
||||
/// args (`Vec(N, f32)`), and `inline for 0..M` bounds all route here so they
|
||||
/// cannot disagree on what a given expression evaluates to (the issue-0083
|
||||
/// cannot disagree on what a given expression evaluates to (the
|
||||
/// two-resolver class of bug). Folds integer `+ - * / %` and unary negate over
|
||||
/// int literals, integral float literals (`[4.0]T` → 4, via `floatToIntExact`),
|
||||
/// and named module / comptime consts — recursively, so nested and parenthesised
|
||||
@@ -290,7 +289,7 @@ pub fn isFloatValuedExpr(node: *const Node, ctx: anytype) bool {
|
||||
/// lhs/rhs is float-valued (`5.0 / 2.0`, `K / 3` with `K : f64 : 4.0`) is FLOAT
|
||||
/// division, NOT integer truncation, so this folder refuses it (`isFloatValuedExpr`)
|
||||
/// and lets `evalConstFloatExpr` + the unified narrowing rule see the true value
|
||||
/// (issue 0095 / F0.11-6). `+ - *` need no such guard — they agree between int and
|
||||
///. `+ - *` need no such guard — they agree between int and
|
||||
/// float arithmetic for the integral operands this folder ever sees.
|
||||
///
|
||||
/// Leaves resolve through the ctx, so each call site shares the SAME folding
|
||||
@@ -319,7 +318,7 @@ pub fn evalConstIntExpr(node: *const Node, ctx: anytype) ?i64 {
|
||||
// ordinary field READ on a value whose spelling shadows a builtin
|
||||
// type name, NOT a numeric-limit / pack-arity accessor — so it is
|
||||
// never a compile-time leaf here; its field is a runtime value
|
||||
// (issues 0092/0093, F0.11-7). Only a BARE type/name receiver folds a
|
||||
//. Only a BARE type/name receiver folds a
|
||||
// `<pack>.len` / `<IntType>.min`/`.max`. Mirrors the same `is_raw`
|
||||
// guard `isFloatValuedExpr` already applies, so the const cluster
|
||||
// (this folder, `evalConstFloatExpr`, `isFloatValuedExpr`) agrees.
|
||||
@@ -360,7 +359,7 @@ pub fn evalConstIntExpr(node: *const Node, ctx: anytype) ?i64 {
|
||||
// truncating division — refuse to fold it here so the value
|
||||
// surfaces through `evalConstFloatExpr` + the unified float→int
|
||||
// rule (integral folds, non-integral errors) instead of silently
|
||||
// truncating to an integer (issue 0095 / F0.11-6). A genuine
|
||||
// truncating to an integer. A genuine
|
||||
// integer `/` (both operands integer-valued) still truncates.
|
||||
.div => if (isFloatValuedExpr(b.lhs, ctx) or isFloatValuedExpr(b.rhs, ctx))
|
||||
null
|
||||
@@ -425,7 +424,7 @@ pub fn evalConstFloatExpr(node: *const Node, ctx: anytype) ?f64 {
|
||||
// A backtick RAW receiver (`` `f64.epsilon ``) is an ordinary field
|
||||
// READ on a value that shadows a builtin float type name, NOT the
|
||||
// numeric-limit accessor — its field is a runtime value, never a
|
||||
// compile-time leaf (issues 0092/0093, F0.11-7). Mirrors the `is_raw`
|
||||
// compile-time leaf. Mirrors the `is_raw`
|
||||
// guard `isFloatValuedExpr` already applies; only a BARE type receiver
|
||||
// folds a float limit.
|
||||
const obj_name: ?[]const u8 = switch (fa.object.data) {
|
||||
@@ -465,10 +464,10 @@ pub fn evalConstFloatExpr(node: *const Node, ctx: anytype) ?f64 {
|
||||
}
|
||||
|
||||
/// The outcome of folding a compile-time COUNT expression to an `i64` under the
|
||||
/// unified float→int narrowing rule (F0.11 / issue 0095). THE single int-or-
|
||||
/// unified float→int narrowing rule. THE single int-or-
|
||||
/// integral-float count fold: `foldDimU32` (array dim / Vector lane / u32 value-
|
||||
/// param) and the non-`u32` value-param gate both route through `foldCountI64`,
|
||||
/// so no count site can disagree on which floats fold (the issue-0083 unify-or-
|
||||
/// so no count site can disagree on which floats fold (the unify-or-
|
||||
/// diverge rule extended to floats).
|
||||
pub const CountFold = union(enum) {
|
||||
/// An integer expression, or an INTEGRAL compile-time float (`[F + 1.5]` → 4).
|
||||
@@ -500,7 +499,7 @@ pub fn foldCountI64(node: *const Node, ctx: anytype) CountFold {
|
||||
/// SINGLE place a folded integer becomes a `u32`, so the i64→u32 narrowing is
|
||||
/// range-checked exactly once and no call site does a bare `@intCast` that could
|
||||
/// panic the compiler on a valid-but-oversized fold (a literal `5_000_000_000`
|
||||
/// is a valid `i64` yet `> maxInt(u32)` — issue 0087). Each call site maps a
|
||||
/// is a valid `i64` yet `> maxInt(u32)`). Each call site maps a
|
||||
/// non-`.ok` variant onto its own clean diagnostic + `.unresolved` / abort.
|
||||
pub const DimU32 = union(enum) {
|
||||
/// Folded to a `u32` in `[min, maxInt(u32)]`.
|
||||
@@ -520,7 +519,7 @@ pub const DimU32 = union(enum) {
|
||||
/// integral-float fold), then range-check against `[min, maxInt(u32)]`. THE single
|
||||
/// fold-to-u32 for every array dimension, Vector lane, and value-param count —
|
||||
/// routing all of them here guarantees the narrowing is checked once and can never
|
||||
/// abort the compiler (issue 0087). The fold itself stays in `i64`; only this one
|
||||
/// abort the compiler. The fold itself stays in `i64`; only this one
|
||||
/// conversion is the `u32` gate.
|
||||
pub fn foldDimU32(node: *const Node, ctx: anytype, min: u32) DimU32 {
|
||||
const v = switch (foldCountI64(node, ctx)) {
|
||||
@@ -539,7 +538,7 @@ pub fn foldDimU32(node: *const Node, ctx: anytype, min: u32) DimU32 {
|
||||
/// `type_bridge.foldArrayDim`) — emit through here, so an oversized / negative /
|
||||
/// non-const dimension reports the SAME message regardless of whether it was
|
||||
/// written directly (`a : [N]T`) or via a type alias (`Arr :: [N]T`). Folding
|
||||
/// the wording into one place is the diagnostic-accuracy half of the issue-0083
|
||||
/// the wording into one place is the diagnostic-accuracy half of the
|
||||
/// unify-or-diverge story: `foldDimU32` is the single fold, this is the single
|
||||
/// message map. Only call with a non-`.ok` result (the `.ok` arm is a no-op).
|
||||
pub fn reportDimError(diag: *errors.DiagnosticList, span: ?ast.Span, result: DimU32) void {
|
||||
@@ -593,8 +592,8 @@ pub const GlobalInfo = struct { id: inst.GlobalId, ty: TypeId };
|
||||
/// read-only views and are never freed here.
|
||||
///
|
||||
/// Every owned map allocates through the compilation allocator passed to
|
||||
/// `init` (arena-backed in both the driver and the tests — KB-9 removed the
|
||||
/// old `page_allocator` field defaults). Written only by the
|
||||
/// `init` (arena-backed in both the driver and the tests;
|
||||
/// no `page_allocator` field defaults). Written only by the
|
||||
/// declaration scan / registration code in `Lowering`; read everywhere else.
|
||||
pub const ProgramIndex = struct {
|
||||
/// The lowering/compilation allocator (`module.alloc`), retained so the
|
||||
@@ -612,7 +611,7 @@ pub const ProgramIndex = struct {
|
||||
import_graph: ?*std.StringHashMap(std.StringHashMap(void)) = null,
|
||||
/// Module path → set of directly FLAT-imported paths — the subset of
|
||||
/// `import_graph` edges from a bare `#import` (never a namespaced
|
||||
/// `ns :: #import`). fix-0102c's bare-name disambiguation walks this to
|
||||
/// `ns :: #import`). The bare-name disambiguation walks this to
|
||||
/// decide which same-name authors a flat importer can reach. Borrowed view.
|
||||
flat_import_graph: ?*std.StringHashMap(std.StringHashMap(void)) = null,
|
||||
/// Per-module scalar raw-decl index (`path → name → RawDeclRef`), built by
|
||||
@@ -636,7 +635,7 @@ pub const ProgramIndex = struct {
|
||||
/// null-FuncId `lowerFunction` path with no `Function.source_file` to
|
||||
/// restore. This carries the alias's OWN module source so its body lowers
|
||||
/// in the right visibility context — its intra-module / own-import callees
|
||||
/// resolve (issue 0100 F1). Keyed/allocated with the lowering allocator.
|
||||
/// resolve. Keyed/allocated with the lowering allocator.
|
||||
qualified_fn_source: std.StringHashMap([]const u8),
|
||||
/// sx alias → ForeignClassDecl (jni_class / objc_class / swift_class / ... — registered in scan pass).
|
||||
foreign_class_map: std.StringHashMap(*const ast.ForeignClassDecl),
|
||||
|
||||
@@ -14,12 +14,12 @@ const TypeResolver = type_resolver.TypeResolver;
|
||||
/// Declaration-name / type-position diagnostic pass. Two checks, before
|
||||
/// lowering:
|
||||
///
|
||||
/// 1. Unknown-type diagnostic (issue 0064), extracted from `Lowering`
|
||||
/// 1. Unknown-type diagnostic, extracted from `Lowering`
|
||||
/// (architecture phase A2.4): an identifier used in a type position that
|
||||
/// names no declared type, primitive, or in-scope generic type parameter.
|
||||
/// Main-file decls only — imported / library modules are trusted, matching
|
||||
/// `checkErrorFlow`.
|
||||
/// 2. Reserved-type-name binding (issues 0076, 0077): a value binding
|
||||
/// 2. Reserved-type-name binding: a value binding
|
||||
/// (local/global `var`, a typed-local, or a parameter) spelled as a
|
||||
/// reserved/builtin type name. See `isReservedTypeName`. Runs over EVERY
|
||||
/// compiled module (no main-file filter): such a binding mis-lowers the same
|
||||
@@ -47,7 +47,7 @@ pub const UnknownTypeChecker = struct {
|
||||
main_file: ?[]const u8,
|
||||
|
||||
pub fn run(self: UnknownTypeChecker, decls: []const *const Node) void {
|
||||
// Reserved-type-name binding diagnostic (issues 0076, 0077): rejects any
|
||||
// Reserved-type-name binding diagnostic: rejects any
|
||||
// parameter name or `var` / `:=` / typed-local binding name spelled as a
|
||||
// reserved/builtin type name. Runs over EVERY compiled module — imported
|
||||
// user modules and the stdlib `library/` included — because such a
|
||||
@@ -57,10 +57,10 @@ pub const UnknownTypeChecker = struct {
|
||||
// type / scope context — rejection is purely on spelling. The walk
|
||||
// tracks each module's source file (via the diagnostic list's
|
||||
// `current_source_file`, saved/restored per node) so an imported-module
|
||||
// diagnostic renders against that module's text (issue 0077).
|
||||
// diagnostic renders against that module's text.
|
||||
for (decls) |decl| self.checkBindingNames(decl);
|
||||
|
||||
// Unknown-type diagnostic (issue 0064): main-file decls only; imported
|
||||
// Unknown-type diagnostic: main-file decls only; imported
|
||||
// and library modules are trusted, matching `checkErrorFlow`.
|
||||
var declared = std.StringHashMap(void).init(self.alloc);
|
||||
defer declared.deinit();
|
||||
@@ -84,7 +84,7 @@ pub const UnknownTypeChecker = struct {
|
||||
}
|
||||
}
|
||||
|
||||
/// Reserved-type-name binding walk (issues 0076, 0077). Visits every node
|
||||
/// Reserved-type-name binding walk. Visits every node
|
||||
/// reachable from `node` and rejects each *binding name* — `var` / `:=` /
|
||||
/// typed-local declarations, destructure names, function / lambda / method
|
||||
/// parameters, `if` / `while` optional bindings, `for` capture + index
|
||||
@@ -109,7 +109,7 @@ pub const UnknownTypeChecker = struct {
|
||||
/// main-file-scoped unknown-type walk. A node carrying its own
|
||||
/// `source_file` (every module's top-level decls do) becomes the emit file
|
||||
/// for its whole subtree, restored on exit so a sibling in another module
|
||||
/// isn't rendered against it (issue 0077).
|
||||
/// isn't rendered against it.
|
||||
fn checkBindingNames(self: UnknownTypeChecker, node: *const Node) void {
|
||||
const saved_file = self.diagnostics.current_source_file;
|
||||
defer self.diagnostics.current_source_file = saved_file;
|
||||
@@ -222,7 +222,7 @@ pub const UnknownTypeChecker = struct {
|
||||
// so a binding nested anywhere below is still reached. ──
|
||||
// A namespaced import (`mod :: #import "..."`) is wrapped here, its
|
||||
// module decls held inline; descend so an imported module's
|
||||
// reserved-name binding is rejected too (issue 0077).
|
||||
// reserved-name binding is rejected too.
|
||||
.namespace_decl => |nd| {
|
||||
self.checkDeclName(node, nd.name, nd.is_raw);
|
||||
for (nd.decls) |d| self.checkBindingNames(d);
|
||||
@@ -305,7 +305,7 @@ pub const UnknownTypeChecker = struct {
|
||||
.insert_expr => |ins| self.checkBindingNames(ins.expr),
|
||||
.spread_expr => |se| self.checkBindingNames(se.operand),
|
||||
// ── Named type / alias / import declarations: a bare reserved
|
||||
// spelling as the declared name is rejected (issue 0089). These
|
||||
// spelling as the declared name is rejected. These
|
||||
// have no nested binding sites, so only the name is checked. A
|
||||
// flat `#import`/`#import c` (name == null) binds nothing. ──
|
||||
.enum_decl => |ed| self.checkDeclName(node, ed.name, ed.is_raw),
|
||||
@@ -359,7 +359,7 @@ pub const UnknownTypeChecker = struct {
|
||||
fn checkParamNames(self: UnknownTypeChecker, params: []const ast.Param) void {
|
||||
for (params) |p| {
|
||||
// A backtick raw param (`` (`s2: T) ``) or a `#import c` foreign
|
||||
// param is exempt from the reserved-type-name rule (issue 0089) —
|
||||
// param is exempt from the reserved-type-name rule —
|
||||
// the exemption is honored inside `checkBindingName` via `p.is_raw`.
|
||||
self.checkBindingName(p.name, p.name_span, p.is_raw);
|
||||
if (p.default_expr) |de| self.checkBindingNames(de);
|
||||
@@ -378,7 +378,7 @@ pub const UnknownTypeChecker = struct {
|
||||
// Only a const whose VALUE introduces a type (a type decl or
|
||||
// type-expression alias) declares a type name. A value const
|
||||
// like `NotAType :: 123` must NOT satisfy the unknown-type
|
||||
// check (issue 0068).
|
||||
// check.
|
||||
if (constValueIntroducesType(cd.value)) out.put(cd.name, {}) catch {};
|
||||
if (cd.value.data == .fn_decl) self.harvestScopeDecls(cd.value.data.fn_decl.body, out);
|
||||
},
|
||||
@@ -443,7 +443,7 @@ pub const UnknownTypeChecker = struct {
|
||||
.const_decl => |cd| {
|
||||
// Local type decl (`T :: struct/enum/union/error/alias`) — add
|
||||
// its name; a local VALUE const (`x :: 5`) does not declare a
|
||||
// type (issue 0068). Recurse regardless, to harvest nested decls
|
||||
// type. Recurse regardless, to harvest nested decls
|
||||
// (e.g. type decls inside a `f :: () { ... }` body).
|
||||
if (constValueIntroducesType(cd.value)) out.put(cd.name, {}) catch {};
|
||||
self.harvestScopeDecls(cd.value, out);
|
||||
@@ -529,7 +529,7 @@ pub const UnknownTypeChecker = struct {
|
||||
defer type_vals.shrinkRetainingCapacity(save_v);
|
||||
for (type_params) |tp| in_scope.append(self.alloc, tp) catch {};
|
||||
// Value params declared `: Type` (no `$`) — using one in a type position
|
||||
// is the issue-0064 misuse; track them for the tailored hint.
|
||||
// is the $-prefix-in-cast-position misuse; track them for the tailored hint.
|
||||
for (params) |p| {
|
||||
if (p.type_expr.data == .type_expr) {
|
||||
const cn = p.type_expr.data.type_expr.name;
|
||||
@@ -653,7 +653,7 @@ pub const UnknownTypeChecker = struct {
|
||||
}
|
||||
|
||||
/// A `cast(T)` target naming a value-`Type` parameter (the otherwise-silent
|
||||
/// issue-0064 case in cast position) gets the tailored `$T` hint.
|
||||
/// case in cast position) gets the tailored `$T` hint.
|
||||
fn checkCastTarget(self: UnknownTypeChecker, arg: *const Node, in_scope: []const ast.StructTypeParam, type_vals: []const []const u8) void {
|
||||
const name = switch (arg.data) {
|
||||
.identifier => |id| id.name,
|
||||
@@ -804,7 +804,7 @@ pub const UnknownTypeChecker = struct {
|
||||
// type — explicitly NOT the builtin/reserved spelling — so it must
|
||||
// resolve to a `` `s2 ``-declared type, else a normal "unknown type"
|
||||
// error. Skip the builtin-name exemption that would otherwise wave a
|
||||
// bare `s2` through (issue 0089).
|
||||
// bare `s2` through.
|
||||
if (!is_raw and isBuiltinTypeName(name)) return;
|
||||
for (in_scope) |tp| {
|
||||
if (!std.mem.eql(u8, tp.name, name)) continue;
|
||||
@@ -838,7 +838,7 @@ pub const UnknownTypeChecker = struct {
|
||||
}
|
||||
|
||||
/// Reject a value binding (local/global `var` or a parameter) spelled as a
|
||||
/// reserved/builtin type name (issue 0076). The parser turns such a spelling
|
||||
/// reserved/builtin type name. The parser turns such a spelling
|
||||
/// into a `.type_expr` rather than an `.identifier` (`parser.zig`, via
|
||||
/// `name_class.Type.fromName`), so the address-of family in `lower.zig`
|
||||
/// (`@x`, the autoref `x.method(...)` receiver, a bare `f(x)` at a `*T`
|
||||
@@ -852,7 +852,7 @@ pub const UnknownTypeChecker = struct {
|
||||
/// honoring the backtick / `#import c` foreign exemption. This is what keeps
|
||||
/// the check and the exemption from desyncing — the recurring failure of the
|
||||
/// earlier attempts, where each site threaded an `if (!is_raw)` guard
|
||||
/// separately and one was forgotten (issue 0089).
|
||||
/// separately and one was forgotten.
|
||||
fn checkBindingName(self: UnknownTypeChecker, name: []const u8, span: ?ast.Span, is_raw: bool) void {
|
||||
if (is_raw) return;
|
||||
if (isReservedTypeName(name))
|
||||
@@ -862,7 +862,7 @@ pub const UnknownTypeChecker = struct {
|
||||
/// Reserved-name check for a `::` declaration whose own name binds an
|
||||
/// identifier but carries no dedicated `name_span` field — struct / enum /
|
||||
/// union / error-set / protocol / foreign-class type decls, ufcs aliases,
|
||||
/// and namespaced imports (issue 0089). Each such node begins at its name
|
||||
/// and namespaced imports. Each such node begins at its name
|
||||
/// token (`createNode(name_start, …)`), so the name's length isolates the
|
||||
/// caret onto the name — a single source for the span, no separate stored
|
||||
/// field to drift from `node.span`. `is_raw` is REQUIRED, exactly as in
|
||||
@@ -908,7 +908,7 @@ fn isBuiltinTypeName(name: []const u8) bool {
|
||||
/// (`struct`/`enum`/`union`/`error`) or a type-expression alias (`Alias :: u32`,
|
||||
/// `Ptr :: *u8`, `Cb :: (s32) -> s32`, …). Only these belong in the declared-
|
||||
/// type-name set; a value const (`NotAType :: 123`) does NOT declare a type and
|
||||
/// must stay subject to the unknown-type check (issue 0068).
|
||||
/// must stay subject to the unknown-type check.
|
||||
///
|
||||
/// `.identifier` / `.call` aliases (`B :: A`, `Vec3 :: Vec(3, f32)`) are
|
||||
/// deliberately NOT matched here: the scan registers the type-valued ones into
|
||||
|
||||
@@ -24,7 +24,7 @@ pub const AliasMap = ?*const std.StringHashMap(TypeId);
|
||||
/// 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).
|
||||
/// way to evaluate a named const and silently fabricated a 0 length.
|
||||
/// `null` for contexts with no const table (e.g. unit tests).
|
||||
pub const ConstMap = ?*const std.StringHashMap(ModuleConstInfo);
|
||||
|
||||
@@ -48,7 +48,7 @@ const StatelessInner = struct {
|
||||
/// so a dimension resolves to one length on every registration-time path
|
||||
/// (aliases, inline union/enum fields) and matches the direct form (issue
|
||||
/// 0083), and an oversized-but-valid `i64` dim returns null instead of
|
||||
/// panicking the `@intCast` (issue 0087). Returns null when the dimension
|
||||
/// panicking the `@intCast`. Returns null when the dimension
|
||||
/// isn't a compile-time integer (a runtime value / non-comptime call, or a
|
||||
/// name not bound to an integer const), is negative, or doesn't fit a `u32`.
|
||||
/// Null propagates to `resolveCompound`, which yields the `.unresolved`
|
||||
@@ -65,7 +65,7 @@ const StatelessInner = struct {
|
||||
/// to a 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). The non-negative check is applied once, on the final
|
||||
/// to. The non-negative check is applied once, on the final
|
||||
/// dimension value in `resolveArrayLen` — not here, so an intermediate
|
||||
/// operand may legitimately be negative.
|
||||
pub fn lookupDimName(self: StatelessInner, name: []const u8) ?i64 {
|
||||
@@ -84,7 +84,7 @@ const StatelessInner = struct {
|
||||
/// the stateful body-lowering path uses, so a float-const-leaf dimension
|
||||
/// (`Arr :: [F + 1.5]T`, `F : f64 : 2.5` → len 4) folds to the SAME count on
|
||||
/// the registration-time alias path as on the direct form `a : [F + 1.5]T`
|
||||
/// (issue 0083 unify-or-diverge). Integer / integral-float leaves are already
|
||||
/// (unify-or-diverge). Integer / integral-float leaves are already
|
||||
/// resolved by the `evalConstIntExpr` delegation inside `evalConstFloatExpr`;
|
||||
/// this surfaces a non-integral float const so the unified rule rejects it.
|
||||
pub fn lookupFloatName(self: StatelessInner, name: []const u8) ?f64 {
|
||||
@@ -95,7 +95,7 @@ const StatelessInner = struct {
|
||||
/// of `Lowering.nameIsFloatTyped`, routed through the SAME
|
||||
/// `program_index.moduleConstIsFloatTyped` so the int folder's division arm
|
||||
/// classifies a const-leaf division identically on the alias-registration path
|
||||
/// as on the direct form (issue 0095 / F0.11-6, the issue-0083 unify-or-diverge
|
||||
/// as on the direct form (the unify-or-diverge
|
||||
/// rule extended to the division guard).
|
||||
pub fn nameIsFloatTyped(self: StatelessInner, name: []const u8) bool {
|
||||
const consts = self.consts orelse return false;
|
||||
@@ -109,8 +109,8 @@ const StatelessInner = struct {
|
||||
/// alias-registration site calls this so an unresolved `Arr :: [N]T` alias can
|
||||
/// emit the PRECISE dim diagnostic (oversized `[5_000_000_000]` / negative /
|
||||
/// non-const) that matches the stateful direct form, rather than one generic
|
||||
/// "not a compile-time integer constant" message for every failure (issue 0083 —
|
||||
/// the stateful/stateless diagnostic divergence).
|
||||
/// "not a compile-time integer constant" message for every failure (the
|
||||
/// stateful/stateless diagnostic divergence).
|
||||
pub fn foldArrayDim(len_node: *const Node, table: *TypeTable, alias_map: AliasMap, consts: ConstMap) program_index_mod.DimU32 {
|
||||
const si = StatelessInner{ .table = table, .alias_map = alias_map, .consts = consts };
|
||||
return program_index_mod.foldDimU32(len_node, si, 0);
|
||||
@@ -196,7 +196,7 @@ pub fn resolveAstType(node: ?*const Node, table: *TypeTable, alias_map: AliasMap
|
||||
/// Resolve a bare type name. The algorithm lives in `type_resolver.zig`
|
||||
/// (`TypeResolver.resolveNamed`, the single source); `type_bridge` forwards the
|
||||
/// caller-threaded `alias_map` (the single-source `ProgramIndex.type_alias_map`).
|
||||
/// `skip_builtin` carries the backtick raw escape (issue 0089).
|
||||
/// `skip_builtin` carries the backtick raw escape.
|
||||
fn resolveTypeName(name: []const u8, table: *TypeTable, alias_map: AliasMap, skip_builtin: bool) TypeId {
|
||||
return type_resolver.TypeResolver.resolveNamed(name, table, alias_map, skip_builtin);
|
||||
}
|
||||
@@ -251,7 +251,7 @@ fn resolveTupleSpreadShape(tt: *const ast.TupleTypeExpr, table: *TypeTable, alia
|
||||
// every element is itself a type expression. A non-type element (e.g. the `1`
|
||||
// in `(s32, 1)`) means this literal is NOT a type: refuse to fabricate a tuple
|
||||
// and return the `.unresolved` sentinel (never `.s64`, which would silently lie
|
||||
// about the size — issue 0067). type_bridge is stateless and has no diagnostics;
|
||||
// about the size). type_bridge is stateless and has no diagnostics;
|
||||
// the user-facing diagnostic is emitted by the stateful caller
|
||||
// (`Lowering.resolveTupleLiteralTypeArg`), which validates before delegating
|
||||
// here, so the valid path below builds the tuple and the invalid path never
|
||||
@@ -317,7 +317,7 @@ fn resolveParameterizedType(pt: *const ast.ParameterizedTypeExpr, table: *TypeTa
|
||||
// The lane count is a literal or a named module-const integer — the
|
||||
// same dimension forms a fixed array accepts. An unresolvable count
|
||||
// is NOT a 0-lane vector (which would silently mis-size every load /
|
||||
// store); yield `.unresolved` so the failure surfaces (issue 0083).
|
||||
// store); yield `.unresolved` so the failure surfaces.
|
||||
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);
|
||||
|
||||
@@ -72,7 +72,7 @@ pub const TypeResolver = struct {
|
||||
/// null. THE single width parser — `resolveBuiltinName` (to intern the
|
||||
/// `TypeId`) and the numeric-limit accessors (`.min`/`.max`, via
|
||||
/// `integerWidthSign`) both classify through here, so the recognized width
|
||||
/// set cannot diverge (the issue-0083 two-resolver defect class).
|
||||
/// set cannot diverge (the two-resolver defect class).
|
||||
pub const WidthInt = struct { width: u8, signed: bool };
|
||||
pub fn parseWidthInt(name: []const u8) ?WidthInt {
|
||||
if (name.len < 2) return null;
|
||||
@@ -211,7 +211,7 @@ pub const TypeResolver = struct {
|
||||
// produce the same length. `resolveArrayLen` returns null when the
|
||||
// dimension can't be resolved to a compile-time integer; that is
|
||||
// never a 0-length array (which gives a 0-byte alloca and OOB
|
||||
// element access — issue 0083). Yield the `.unresolved` sentinel
|
||||
// element access). 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.
|
||||
@@ -289,7 +289,7 @@ pub const TypeResolver = struct {
|
||||
/// unregistered names.
|
||||
///
|
||||
/// `skip_builtin` is the backtick raw-identifier escape (`` `s2 `` in type
|
||||
/// position, issue 0089): a raw reference is the LITERAL name used as a
|
||||
/// position): a raw reference is the LITERAL name used as a
|
||||
/// type, so it bypasses the builtin/reserved classifier and resolves only
|
||||
/// through registered-type → alias → stub. A bare `s2` keeps the default
|
||||
/// (`false`) and resolves to the builtin int type. The string-prefix
|
||||
@@ -334,7 +334,7 @@ pub const TypeResolver = struct {
|
||||
|
||||
/// Resolve a bare type name through the canonical alias source
|
||||
/// (`ProgramIndex.type_alias_map`). `skip_builtin` carries the backtick raw
|
||||
/// escape (issue 0089) — see `resolveNamed`.
|
||||
/// escape — see `resolveNamed`.
|
||||
pub fn resolveName(self: TypeResolver, name: []const u8, skip_builtin: bool) TypeId {
|
||||
return resolveNamed(name, self.types, &self.index.type_alias_map, skip_builtin);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user