Commit Graph

369 Commits

Author SHA1 Message Date
agra
7f2b8b5cde fix(0123): wrong arg counts to fixed-arity fns error at the call site
checkCallArity compares the supplied count against the declared params
(min = params without trailing defaults, max = params.len, unbounded
past a variadic) at the five plain dispatch sites in lowerCall — bare
selected-author + lazy, namespace alias-gate + qualified, struct
method, ufcs. Pack / comptime / generic / #compiler / #builtin callees
keep their own dispatch. The method/ufcs sites also gain the
appendDefaultArgs fill the generic-instance leg already had, so
trailing defaults work on dot-calls instead of emitting under-arity
calls. lowerStmt's local fn_decl arm now registers a pointer into the
AST node in fn_ast_map, not a stack temporary that aliased every later
local fn.
2026-06-12 01:42:59 +03:00
agra
721369a711 lang: fn aliases dispatch like their target (fix 0121) — scan-time registration through the shared alias-chain walk
Renamed fn aliases failed for EVERY kind (the filed pack-only scope was
a same-name confound: same-name re-exports already resolved through the
name-keyed fn_ast_map). scanDecls now follows ident-/ns.X-RHS const
alias chains (aliasedFnDecl; 0120's hop walk extracted as
followAliasChain) and registers the alias name in fn_ast_map
(absent-only), so every dispatch path — early pack/comptime/generic,
plain lazy-lower, plan-side typing — sees the target decl unchanged.
my_print :: s.print; / my_format :: s.format; now work (the std.sx
re-export shape). Regression: examples/0546 (+rich). Gates: zig build
test 0, suite 588/588.
2026-06-11 18:47:16 +03:00
agra
f2db8ecc53 lang: generic struct head aliases bind the template (fix 0120) — alias-follow from each author's source in head selection; loud unknown-type on the .call type tail
BoxAlias :: Box; / Box :: r.Box; now resolve instantiation, methods,
annotations, and chains through the aliased template, and re-export one
flat-import level as ordinary own decls (the facade shape the std.sx
restructure needs). selectGenericStructHead consults aliasedStructTemplate
(nominal.zig) before the global template map — own-wins/single-flat alias
author, each hop pinned to the alias author's source, ns.X RHS through
namespaceAliasVerdictFrom, depth-capped. resolveTypeCallWithBindings'
silent .unresolved tail (panicked in LLVM emission) now diagnoses
"unknown type". Also aligns the stale pre-existing calls.test.zig UFCS
plan test with the opt-in model (a47ea14). Regression: examples/0211
(+rich/+facade). Gates: zig build test 426/426, suite 587/587.
2026-06-11 18:09:01 +03:00
agra
40805e08ec lang: inline for element form over packs — multi-iterable parity 2026-06-11 14:42:59 +03:00
agra
679653fda8 lang: struct consts migrate to const globals, inline fallback (PLAN-CONST-AGG step 4)
A struct constant whose every field serializes — literals, enum tags,
nested aggregates, and (new) const EXPRESSIONS over named consts /
const-aggregate leaves ('r = K + 1', 'g = LIT.r', 'b = A[1]') — becomes
an immutable global: one storage, reads load/GEP it, '@LIT' is
addressable, dead-global elimination drops unused ones. constExprValue
gained a fold-through tail (evalConstIntExpr/evalConstFloatExpr,
source-aware), which also enables const-expression ELEMENTS in array
consts.

A const with a NON-serializable field (a call, a runtime read) keeps
inline re-lowering, and that per-use evaluation is now the documented
contract for the class (pinned: 'CALL.r' reads 1 then 2, side effects
run per use; '#run' is the evaluate-once tool).

Examples: 0180 (migrated shapes + @ptr + copy independence),
0181 (the inline-fallback contract). m3te (23/23) + game rebuilt green.
2026-06-11 12:58:17 +03:00
agra
c23b76c7d6 lang: const-aggregate comptime folds (PLAN-CONST-AGG step 3)
An array const's '.len' and 'K[<const idx>]' element reads, and a
struct const's field ('LIT.r'), fold as compile-time integer leaves —
usable in array dimensions and other constants' initializers. All
source-aware (the SELECTED author's elements, folded in the author's
context with the cyclic-definition frame); a const out-of-range index
diagnoses at fold time, never wraps.

- evalConstIntExpr gains the three ctx hooks (lookupConstAggLen /
  lookupConstArrayElem / lookupConstStructField) + an index_expr arm;
  all five ctx implementations extended (stateless tiers fold null).
- Array consts dual-register in module_const_map (value = the literal
  node) so the folders see elements; bare reads still hit the GLOBAL
  arm first, so no double emission.
- Untyped consts whose RHS is a const-aggregate leaf ('L :: K.len',
  'E :: K[1]', 'R :: LIT.r') register in a pass 2b AFTER aggregates,
  gated on the receiver naming a const aggregate — a namespaced member
  ('F :: m.PI_ISH') is never mis-typed by the count placeholder.

Examples: 0179 (folds in dims + const exprs), 1163 (OOB diagnostic).
2026-06-11 12:44:43 +03:00
agra
8908e78943 lang: array-typed '::' consts as immutable globals (PLAN-CONST-AGG step 1)
K : [4]s64 : .[...] and the untyped A :: .[1, 2, 3] register as
is_const globals: one storage, reads GEP it, dead-global elimination
drops unused ones, source-aware reads come free via selectGlobalAuthor.

- registerConstArrayGlobal (scanDecls pass 2): typed via the annotation
  (array-ness + dimension/count checked), untyped via element-type
  unification — all ints s64; ANY float promotes the element type to
  f64 with ints converting exactly; bool/string homogeneous; a
  non-numeric mix or non-inferable element asks for an annotation.
- constExprValue converts int elements into float destinations exactly
  (the int+float promotion rule, element-wise).
- emitGlobals marks is_const globals LLVMSetGlobalConstant — also flips
  the comptime-backed #run globals and __sx_default_context to
  'constant' (37 pinned IR snapshots regenerated; runtime unchanged).
- Element shapes: nested arrays, struct elements, strings, bools.
  Non-constant elements / dim mismatch / mixed types diagnose loudly.

Examples: 0177 (feature matrix incl. @K reads through *[4]s64 — needs
the 0117 fix), 1159/1160/1161 (diagnostics), 0837 repointed to values.
2026-06-11 12:26:26 +03:00
agra
82d6b8da0e fix(0117): pointer-to-array indexing auto-derefs
A '*[N]T' receiver in an index expression reached LLVM emission with an
unresolved element type and tripped the panic sentinel — no read or
write spelling worked. ptrToArrayElem on Lowering recognises the shape;
the index READ path GEPs the pointee array through the pointer value
and loads the element; the write / compound-assign / lvalue /
addr-of-element paths and the expression typer resolve the element type
through the same helper (their GEP machinery already handled a pointer
base). Kept out of getElementType so slice paths don't half-accept a
raw pointer base.

Regression: examples/0176 (read, write, compound, element ptr + deref).
2026-06-11 12:15:45 +03:00
agra
0b13498e25 fix(0115): source-aware global selection — own-wins for module globals
The globals registry (global_names) was last-wins across modules with no
per-importer gate: any module's bare K could read/write/type against an
unrelated module's same-named global (hash.sx's K table hijacked every
user K once std's namespace tail pulled hash into the program), and an
own const of an unsupported shape borrowed another module's const and
panicked at the unresolved-type tripwire.

- var_decl joins RawDeclRef: module globals are selectable raw authors.
- selectGlobalAuthor (the globals analogue of F2's selectModuleConst):
  own author wins, one flat-visible author resolves, >=2 distinct flat
  authors diagnose loudly, authored-but-not-visible diagnoses, and a
  compiler-synthesized global (no raw author) emits untracked. A var_decl
  author whose per-source registration was deduped at flat-merge (two
  modules declaring the same extern symbol) serves the symbol's
  registration.
- All bare-identifier global sites route through it: value read, addr-of,
  assignment (store + compound), lvalue address, fn-ptr call, call param
  typing, and expression type inference.
- selectModuleConst gains .own_opaque: an own const author with no
  materialized per-source value (e.g. an array '::' const) blocks
  borrowing another module's same-named const — the read diagnoses
  cleanly instead of panicking.
- The fn-as-VALUE arm admits raw-facts-only authors: an own fn whose name
  a flat-merge collision dropped from the global decl list (first-wins)
  now resolves via author selection for func_ref/closure/Any shapes too.

Regressions: examples 0835 (own const vs flat array global), 0836 (main
const vs namespaced array global, incl. inference), 0837 (own array
const never borrows cross-module — clean unresolved).
2026-06-11 10:47:30 +03:00
agra
fbbfcb268c fix(0114): gate alias-qualified calls to one-level carry, pin to target
The lowerCall namespace branch routed alias.fn() through the global
qualified registration (first-wins) at any import depth, and through the
global last-wins bare map for comptime/generic members. Plain-identifier
alias roots now resolve via the carry-aware namespaceAliasVerdict:

- visible alias (own edge or ONE flat hop): the member dispatches the
  TARGET module's own fn (namespaceFnMember + fd-keyed bareAuthorFuncId),
  so two modules' same-named aliases each call their own target.
- two direct flat imports carrying the alias to distinct targets:
  loud ambiguity diagnostic.
- alias only reachable beyond one hop: "namespace 'X' is not visible".
- foreign / builtin / #compiler members keep the literal-symbol path.

Regressions: examples 0832 (two-hop), 0833 (carried collision),
0834 (own-target pin / first-wins repair).
2026-06-11 09:16:03 +03:00
agra
ee00db849c lang: qualified namespace members in value position + alias carry
Two coupled capabilities on the road to the std restructure
(current/PLAN-STDLIB.md, issue 0114):

1. alias.Type.method() / alias.Type as a call head, alias.CONST, and
   alias.Enum.variant now resolve — previously only alias.fn() and
   type-position alias.Type worked. objectIsValue treats an
   alias-rooted field_access as a type head; the call path strips the
   alias to the existing Type.method machinery; lowerFieldAccess
   resolves alias.CONST pinned to the target module and alias.Enum.x
   as a typed enum literal; resolveTypeWithBindings resolves qualified
   type_exprs pinned to the target.

2. The carry rule: namespaceAliasTarget resolves an alias from the
   file's own edges first, then from DIRECT flat imports (one level),
   diagnosing two distinct carried targets as ambiguous. All qualified
   shapes work through a carried alias — the std.sx namespace tail
   (mem.GPA.init() etc.) is now expressible.

Regression: examples/0831-modules-namespace-alias-carry.sx (direct +
carried, all seven shapes).
2026-06-11 05:52:10 +03:00
agra
67313e1dad fix(0112): out-of-range int literals error instead of silently wrapping
checkIntLiteralFits range-checks a literal against its integer target
(builtins + custom widths via intLiteralRange; width-64 types skip —
every representable literal is a legal bit pattern there) and diagnoses
with the type's range and an xx/cast hint. Wired into the .int_literal
arm (covers decls, assignments, call args, struct-literal fields),
lowerStructConstant, and globalInitValue.

A negated literal now folds to a single constant so -128 range-checks
as -128 rather than as an out-of-range +128 intermediate. An explicit
xx operand skips the check — truncation stays available on request
(cast(T) was already exempt: its value arg lowers without the target).

examples/0300-closures-lambda.sx pinned 133 wrapping to -3 through an
s3 param — the exact class this outlaws; updated to a fitting value.

Found during the fix and filed separately: issue 0113 (negated-literal
global initializers rejected as non-constant; pre-existing).

Regressions: examples/1156-diagnostics-int-literal-out-of-range.sx,
examples/0174-types-int-literal-boundaries.sx.
2026-06-10 22:28:24 +03:00
agra
116af2359e lang: multi-iterable for loops — drop ':', add '..=', open ranges, arrow bodies
The for header is now a comma-separated list of iterables with a
positional capture group and no ':' separator:

    for xs (x) { }                    // collection
    for 0..n (i) { }                  // range (end exclusive)
    for 1..=5 (a) { }                 // ..= inclusive end
    for xs, 0.. (x, i) { }            // index idiom (replaces (x, i))
    for xs, ys (x, y) { }             // parallel (zip) iteration
    for xs (x) => sum += x;           // arrow body (full statement)

First-iterable-wins: the first iterable's length drives the loop and
must be bounded; the other positions follow by their own cursors (a
non-first range's end is not consulted or evaluated; a shorter
non-first collection is read past its length on mismatch). The old
single-iterable index capture is replaced by the trailing open range.

Capture/call disambiguation is positional: the paren group immediately
before '{' or '=>' is the capture, every earlier top-level group is a
call. 'for zip(a, b) (x, y)' calls zip; 'for f(n) { }' reads (n) as
the capture and errors with a parenthesize/add-capture hint. The old
':' form errors with a migration hint.

Lowering is unified across forms: one cursor slot per position (ranges
start at their start, collections at 0), all advanced together, the
first position's bound terminating. inline for keeps the single
bounded comptime range.

Migrated the full corpus (examples, library modules, issue repros,
in-source test strings). New coverage: examples/0050 (the full feature
surface) and examples/1149-1155 (seven diagnostic faces). specs.md For
Loop section + grammar rewritten; readme teaser updated.
2026-06-10 20:30:55 +03:00
agra
3cc34d54c1 fix(0108): break/continue run the loop body's pending defers
lowerBreak/lowerContinue emitted a bare br, and the enclosing block's
emitBlockDefers — seeing the terminator — discarded the pending entries
on the assumption a return had already drained them. The breaking
iteration's defers were silently skipped, leaking whatever the cleanup
released.

Lowering.loop_defer_base records the defer-stack height at each loop's
body start (while / for / range-for, saved and restored alongside
break_target); break/continue drain non-onfail entries down to it in
LIFO order via the non-truncating emitLoopExitDefers before branching.
Truncation stays with the lexical block exits — the same entries still
belong to the fall-through path after the branch containing the break.
break/continue outside a loop now diagnose instead of no-op'ing.

Regression: examples/0049-basic-defer-break-continue.sx (for and while,
break and continue, nested-block LIFO drain).
2026-06-10 17:43:58 +03:00
agra
2b8041a828 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.
2026-06-10 16:34:17 +03:00
agra
5b304a29c1 fix(KB-9): move Lowering/ProgramIndex maps off page_allocator defaults
16 Lowering map fields and 8 ProgramIndex map fields were declared with
`= ....init(std.heap.page_allocator)` field defaults that init() never
replaced — every instance really allocated page-at-a-time outside the
compilation allocator, invisible to leak checking and never reclaimed.

All 24 now init explicitly with the compilation allocator (module.alloc
/ the init alloc param), which is arena-backed in both the driver
(main's arena) and the test suites (per-test arenas), so backing is
reclaimed at teardown. ProgramIndex's struct doc no longer claims the
page_allocator defaults.

Six lower.test.zig tests that constructed Module with bare
std.testing.allocator leaked once the checker could finally see these
maps; they now use the same per-test ArenaAllocator idiom as the rest
of the file and the facade test suites.

Gate: zig build OK; zig build test 426/426 (6/6 steps, leak-clean);
run_examples 541/0; zero expected/ snapshot churn.
2026-06-10 16:19:52 +03:00
agra
82500931ce cleanup(B-review): fix stale section banners, merge alias block, prune imports
Review follow-up to the ARCH-B split (comment/import hygiene only, no
code changes):

- Section banners that travelled to the wrong file with the B1-B8 cuts
  are reworded to describe the section that actually follows (e.g.
  stmt.zig's trailing "Expression lowering", expr.zig's "Control flow"
  before lowerChainedComparison) or deleted where nothing follows
  (4 trailing-at-EOF banners). ffi.zig's facade note no longer claims
  the IMP builders "stay here" (they live in lower/objc_class.zig);
  protocol.zig's namespace-lookup banner now points at
  pack.zig:resolvePackProjection for the orchestrator.
- lower.zig's two lower/expr.zig alias blocks (B8.1 + B8.2 appends)
  merged into one.
- 448 unused header decls pruned from the 15 lower/*.zig files (each
  had inherited lower.zig's full import block; pruned to fixpoint so
  cascading type-extraction consts went too).

Gate: zig build OK; zig build test 426/426; run_examples 541/0; zero
expected/ snapshot churn.
2026-06-10 16:09:24 +03:00
agra
89b3789973 refactor(B8.3): move lambdas + captures to lower/closure.zig
Verbatim relocation of the 5-method closure cluster (lowerLambda,
bare-fn trampoline, closure-to-bare-fn adapter, capture collection, env
sizing) into src/ir/lower/closure.zig. 5 aliases on Lowering keep all
call sites unchanged. Method pub-flip: typeAlignBytes.

Resolves the B7.1 flag: CaptureInfo relocates from lower/call.zig to
lower/closure.zig (its domain home, next to collectCaptures); the
Lowering type alias is repointed so external references are unchanged.

Gate: zig build OK; zig build test 426/426; run_examples 541/0; zero
expected/ snapshot churn.
2026-06-10 14:43:04 +03:00
agra
35e35adb0a refactor(B8.2): move expr core + operators to lower/expr.zig
Verbatim relocation of the 8-method core expression cluster (lowerExpr
dispatch, ref-capture pointee, binary ops, tuple ops/lex-compare/
membership, chained comparison, emitCmp) appended to
src/ir/lower/expr.zig. 8 aliases on Lowering keep all call sites
unchanged.

Method pub-flips: isArithOperand, isBitwiseOperand, isOrderingOperand,
lowerLambda, binOpSymbol. expr.zig reaches arithResultType,
exprIsFailable, binOpSymbol via Lowering-namespace alias consts.

Gate: zig build OK; zig build test 426/426; run_examples 541/0; zero
expected/ snapshot churn.
2026-06-10 14:40:44 +03:00
agra
0592c9dc97 refactor(B8.1): move expression literals/field/index to lower/expr.zig
Verbatim relocation of the 30-method expression cluster (struct/array/
tuple/enum/tagged-enum literals, init blocks, field access on values and
types, optional chains, numeric limits, indexing, slicing, deref, force
unwrap, null coalesce) into src/ir/lower/expr.zig — one contiguous
1,372-line cut. 30 aliases on Lowering keep all call sites unchanged.

Nested StructConstInfo stays on Lowering (field type of
struct_const_map), flipped pub and reached via an alias const, alongside
headNameOfCallee.

Gate: zig build OK; zig build test 426/426; run_examples 541/0; zero
expected/ snapshot churn.
2026-06-10 14:38:11 +03:00
agra
ef4059bdde refactor(B7.3): move generics emission to lower/generic.zig
Verbatim relocation of the 39-method generics cluster (function/struct/
union monomorphisation + instantiation, type-arg resolution and
matching, type-name formatting/mangling, type-category tags, head-gate
author selection, value-param resolution + diagnostics) plus four
single-home nested types (HeadTemplate, HeadName, HeadTypeGate,
GenericStructMethod) into src/ir/lower/generic.zig. 39 aliases on
Lowering keep all call sites unchanged.

Method pub-flips: packResolver, resolveVectorLane. generic.zig reaches
five relocated/static helpers via Lowering-namespace alias consts
(inferExprType, isNamedTypeKind, resolveBuiltin, structMethodFn,
typeFnAuthor).

Gate: zig build OK; zig build test 426/426; run_examples 541/0; zero
expected/ snapshot churn.
2026-06-10 14:35:52 +03:00
agra
6b07346d98 refactor(B7.2): move pack/variadic lowering to lower/pack.zig
Verbatim relocation of the 19-method pack cluster (pack element/value
projection, spread refs, pack indexing + diagnostics, pack-to-slice,
variadic arg lowering/packing, pack-slice materialisation, pack-fn
calls + monomorphisation, pack projection resolution, isPackFn/
isPackParam predicates) into src/ir/lower/pack.zig. 19 aliases on
Lowering keep all call sites unchanged.

ProjectionPosition and PackProjection move from lower/protocol.zig to
lower/pack.zig (their domain home, next to resolvePackProjection);
the Lowering type aliases are repointed, so external references
(lower.test.zig) are unchanged.

Method pub-flip: findReturnValueType.

Gate: zig build OK; zig build test 426/426; run_examples 541/0; zero
expected/ snapshot churn.
2026-06-10 14:30:34 +03:00
agra
54db29e60a refactor(B7.1): move call lowering to lower/call.zig
Verbatim relocation of the 18-method call cluster (lowerCall moved
whole, context diagnostics, foreign-call helper, builtin/function
resolution, generic + runtime-dispatch calls, reflection calls + guards,
default-arg expansion, call param typing) into src/ir/lower/call.zig.
18 fn aliases keep all call sites unchanged.

CaptureInfo (closure-domain type that sat inside the run) travelled and
is re-exposed via a Lowering type alias; candidate to relocate to
lower/closure.zig in B8.3.

Method pub-flips: callResolver, createBareFnTrampoline,
ensureGenericInstanceMethodLowered, fixupMethodReceiver,
getStructTypeName, isStaticTypeArg, lowerPackFnCall, packSpreadRefs,
packVariadicCallArgs, refCapturePointee, resolveParamTypeInSource,
typeSizeBytes, headNameOfCallee.

Gate: zig build OK; zig build test 426/426; run_examples 541/0; zero
expected/ snapshot churn.
2026-06-10 14:27:20 +03:00
agra
b163c4a3fc refactor(B6.2): move Obj-C defined-class emission to lower/objc_class.zig
Verbatim relocation of the 23-method defined-class cluster (IMP/property
emission: class/alloc/static/dealloc IMPs, property getters/setters +
ARC runtime decls, defined-state field access, property/method chain
lookup, string-constant globals) plus the single-home
ObjcDefinedStateField type into src/ir/lower/objc_class.zig. 23 aliases
on Lowering keep all call sites (incl. expr_typer.zig facade and
lower/stmt.zig) unchanged. Zero pub-flips — all callees were already
public from earlier steps.

Gate: zig build OK; zig build test 426/426; run_examples 541/0; all 37
.ir snapshots byte-identical, zero expected/ churn.
2026-06-10 14:14:49 +03:00
agra
638a048cc5 refactor(B6.1): move Obj-C/JNI call lowering to lower/ffi.zig
Verbatim relocation of the 24-method FFI cluster (selector/class-object
interning, FFI intrinsic + JNI calls, foreign instance/static method
lowering, super calls, foreign-class registration, Obj-C defined-class
method registration, JNI env TLS fids, JNI main-stub synthesis) plus
the file-scope jniMapParamType (no alias needed — all callers moved)
into src/ir/lower/ffi.zig. 24 aliases on Lowering keep all call sites
unchanged.

Method pub-flips: emitObjcDefinedAllocAndInit, findForeignMethodInChain.

Gate: zig build OK; zig build test 426/426; run_examples 541/0; all 37
.ir snapshots byte-identical, zero expected/ churn.
2026-06-10 14:03:21 +03:00
agra
e884e87f80 refactor(B5.2): move coercions to lower/coerce.zig
Verbatim relocation of the 19-method coercion cluster (lowerXX, user
conversions, protocol erasure, default-value construction, zero values,
coerceToType implicit/explicit ladder, C-variadic promotion, call-arg
coercion) plus the nested single-home CoerceMode enum into
src/ir/lower/coerce.zig. 19 aliases on Lowering keep all call sites
unchanged.

Method pub-flip: prependCtxIfNeeded. ParamImplEntry stays a Lowering
nested type (field type of param_impl_map) and is reached via an alias
const.

Gate: zig build OK; zig build test 426/426; run_examples 541/0; zero
expected/ snapshot churn.
2026-06-10 13:58:09 +03:00
agra
8990bd4978 refactor(B5.1): move protocol emission to lower/protocol.zig
Verbatim relocation of the 13-method protocol cluster (protocol decl
registration, param-protocol instantiation, thunk creation, vtable
globals, protocol-value construction, dispatch emission, impl lookup)
into src/ir/lower/protocol.zig. 13 fn aliases on Lowering keep all call
sites unchanged.

Two pub nested types travelled with the run (ProjectionPosition,
PackProjection) and are re-exposed via Lowering type aliases; they are
pack-domain types and may relocate to lower/pack.zig in B7.2.

Method pub-flips: allocViaContext, callForeign, genericInstanceMethod,
monomorphizeFunction.

Gate: zig build OK; zig build test 426/426; run_examples 541/0; zero
expected/ snapshot churn.
2026-06-10 13:54:59 +03:00
agra
5928d9f067 refactor(B4.2): move nominal-type registration to lower/nominal.zig
Verbatim relocation of the 23-method nominal-type cluster (struct/enum/
union/error-set registration, anon-type qualification, nominal-id
stamping, shadow-slot reservation, named-type interning, generic struct
templates + alias registration) plus the nested ShadowTypeDecl union
into src/ir/lower/nominal.zig. 23 aliases on Lowering keep all call
sites unchanged.

Method pub-flip: instantiateGenericStruct. nominal.zig reaches
VisibleStructAuthor and structDeclOfRaw (both relocated to decl.zig in
B4.1) via Lowering-namespace alias consts.

Gate: zig build OK; zig build test 426/426; run_examples 541/0; zero
expected/ snapshot churn.
2026-06-10 13:51:15 +03:00
agra
13f5fc57c1 refactor(B4.1): move decl scan + fn-lowering entry to lower/decl.zig
Verbatim relocation of the 52-method declaration cluster (lowerRoot,
scanDecls, entry-point checks, const/global registration, forward-alias
resolution, declareFunction + qualified registration, visibility,
lazy/deferred function lowering, same-name author selection) plus four
nested types (BareCallee, SelectedFunc, TypeHeadResolution,
VisibleStructAuthor) and the file-scope isExportedEntryName (all callers
moved with it) into src/ir/lower/decl.zig. 52 fn aliases + 3 type
aliases on Lowering keep all call sites (incl. calls.zig facade)
unchanged.

Method pub-flips: assertInstanceMapsCoincide, emitDefaultContextGlobal,
headFnLeak, initializerDescription, instantiateTypeFunction,
lookupObjcDefinedClassForMethod, lowerObjcDefinedClassMethods,
registerForeignClassDecl, registerGenericStructAlias,
registerNamespacedForeignClasses, reserveShadowSlot,
resolveTypeCallWithBindings, selectGenericStructHead,
synthesizeJniMainStubs, typeResolver, hasComptimeParams, isPlainFreeFn,
topLevelTypeDecl, isPackFn. Nested-type exposure: FnBodyReentry (+
enter/restore), ShadowTypeDecl.key/name/isGeneric.

Gate: zig build OK; zig build test 426/426; run_examples 541/0; zero
expected/ snapshot churn.
2026-06-10 13:47:41 +03:00
agra
8adfc1dd50 refactor(B3.2): move control flow to lower/control_flow.zig
Verbatim relocation of the 14-method control-flow cluster (if/while/
for/match lowering incl. comptime-inline variants, break/continue, block
plumbing: freshBlock, freshBlockWithParams, currentBlockHasTerminator,
ensureTerminator) into src/ir/lower/control_flow.zig. 14 aliases on
Lowering keep all call sites unchanged.

Method pub-flips: computeHasImpl, headTypeGate, inferMatchResultType,
resolveTypeCategoryTags, isTypeCategoryMatch. Unqualified references in
moved bodies (ComptimeValue nested type, isTypeCategoryMatch static)
resolved via file-scope alias consts — bodies stay verbatim.

Gate: zig build OK; zig build test 426/426; run_examples 541/0; zero
expected/ snapshot churn.
2026-06-10 13:18:07 +03:00
agra
3dbd3ce072 refactor(B3.1): move statement lowering to lower/stmt.zig
Verbatim relocation of the 24-method statement cluster (block/stmt
dispatch, var/const/local-fn decls, return, assignment + compound ops,
multi-assign/destructure, push, defer/onfail/cleanup) plus the nested
single-home FieldLvalue type into src/ir/lower/stmt.zig. 24 aliases on
Lowering keep all call sites unchanged.

Method pub-flips: buildDefaultValue, buildProtocolErasure,
diagPackAsValue, diagnoseMissingContext, emitError, emitFieldError,
ensureTerminator, getExprAlloca, getJniEnvTlFids, isPackName,
lazyLowerFunction, lowerObjcDefinedStateForObj, lowerObjcPropertySetter,
recordLocalTypeName, registerEnumDecl, registerErrorSetDecl,
registerStructDecl, registerUnionDecl, zeroValue.

Gate: zig build OK; zig build test 426/426; run_examples 541/0; zero
expected/ snapshot churn.
2026-06-10 13:14:51 +03:00
agra
856299ce36 refactor(B2.1): move comptime hooks + const folding to lower/comptime.zig
Verbatim relocation of the 26-method comptime cluster (comptime eval
hooks, #insert, comptime calls/deps/substitution, source-const folding
and module-const selection) plus the three nested const-selection types
(SelectedConst, ConstAuthor, ConstSourcePin) into src/ir/lower/
comptime.zig. 26 fn aliases + SelectedConst type alias on Lowering keep
all call sites unchanged.

Shared file-scope helpers stay in lower.zig per the helpers-stay-home
rule, now pub: ConstFoldFrame, constFoldFrameContains, SourceConstCtx.
Method pub-flips: findVariantIndex, putGlobal, tryLowerAsExpr,
lowerVariadicArgs, resolver, setCurrentSourceFile, diagNonIntegralNarrow,
lowerStmt, stampCallerSource, resolveParamType, resolveReturnType.

Gate: zig build OK; zig build test 426/426; run_examples 541/0; zero
expected/ snapshot churn.
2026-06-10 13:11:33 +03:00
agra
b240810b6f refactor(B1.1): move error-channel lowering to lower/error.zig
Verbatim relocation of the 53-method error cluster (error typing,
raise/failable, try/catch/or, inferred-set convergence, trace runtime
hooks) out of the Lowering struct into src/ir/lower/error.zig as free
functions taking *Lowering. Each gets a pub-const alias on Lowering, so
every call site compiles unchanged (decl-alias method resolution).

Pub-flips (callees now referenced cross-file): lowerExpr, coerceToType,
freshBlock, freshBlockWithParams, emitErrorCleanup,
currentBlockHasTerminator, lowerBlock, lowerBlockValue.

Gate: zig build OK; zig build test 426/426; run_examples 541/0; zero
expected/ snapshot churn.
2026-06-10 12:58:46 +03:00
agra
2d68beb053 refactor(R3): migrate headTypeGate + bareVisibleStructDecl to single walk
headTypeGate and bareVisibleStructDecl were using the same
moduleTypeAuthor + flatTypeAuthorCount pattern that selectNominalLeaf
used before R2. Migrated both to a single collectVisibleAuthors call
with inline type-specific resolution, matching the R2 pattern.

Deleted now-unused helpers: moduleTypeAuthor, FlatTypeAuthor,
moduleTypeAuthorTid, FlatTypeAuthorCount, flatTypeAuthorCount.
Net: -76 lines.

541/541 regression tests pass. 426/426 unit tests pass.
2026-06-09 22:37:18 +03:00
agra
3b4df4ab8d refactor(R2): selectNominalLeaf uses single collectVisibleAuthors walk
Replaces the 3 separate author-collection calls inside selectNominalLeaf
(moduleTypeAuthor + ownConstDeclIsPendingAlias + flatTypeAuthorCount +
forwardAliasOrUndeclared) with a single collectVisibleAuthors call plus
inline type-specific resolution. The flat walk now handles:
- own named type: resolved or forward (slot not yet interned)
- own const_decl: resolved alias or pending (own wins over flat)
- flat named types: ambiguous / resolved / forward
- flat const_decl pending aliases: pending (for forward aliases in imports)

Deletes 3 now-unused helpers: forwardAliasOrUndeclared, constAuthor,
ownConstDeclIsPendingAlias. Net: -17 lines.

541/541 regression tests pass. Issue 0107 repro still outputs 300.
2026-06-09 22:31:12 +03:00
agra
010e644897 fix(R1): own const_decl pending alias wins over flat imports (issue 0107)
When a module declares `A :: B; B :: u64;` and both a flat import and a
namespaced import export `B :: u8`, the flat import's B was discovered by
flatTypeAuthorCount before the own B :: u64 was processed — binding A to
u8 and silently truncating values.

Fix: ownConstDeclIsPendingAlias guard added to selectNominalLeaf between
the own-alias check and the flat-import walk. If the querying module has
an own const_decl for the name that is not yet in type_aliases_by_source,
return .pending so the forward-alias fixpoint resolves it correctly.

Regression: examples/0830-modules-flat-ns-same-name-forward-alias.sx
(x : A = 300 prints 300, not 44). 541/541 tests pass.
2026-06-09 22:16:47 +03:00
agra
cd7510067f refactor(stdlib/S1.2): delete module_fns; dissolve legacy_direct_any [additive]
Delete module_fns as a separate function-author fact source. Its authors
already live in the module_decls raw facts, so lowerRetainedSameNameAuthors now
reads function authors straight out of module_decls (filtered to *FnDecl via
fnDeclOfRaw) — the same path → name → RawDeclRef store, fn-filtered. Remove
imports.ModuleFns / FnIndex / indexModuleFns / buildModuleFns / fnDeclOf, the
Compilation.module_fns field + its build + wiring, and ProgramIndex.module_fns.

Remove VisibilityMode.legacy_direct_any (the quarantined own-scope-plus-full-
import_graph mode): no production caller passed it, so the collectVisibleAuthors
and isVisible switch arms that handled it are dead and go too, collapsing
VisEdgeSet to the single flat-import walk. No semantic fallback is introduced;
import_graph stays the transitive-visibility source for findVisibleImpls.

Additive: the old maps stay active and lowering still consumes them — no
lowering consumer is cut over to the DeclTable (that is S3), and no resolution
behavior changes. Tests that drove the removed symbols are rerouted through
module_decls / the flat-edge walk.

Gate over the baseline-green corpus: zig build, zig build test (424/424),
bash tests/run_examples.sh (540 passed) — all exit 0; single-author output
byte-identical; multi-author 0722–0740 stdout/exit unchanged.
2026-06-09 11:36:04 +03:00
agra
8058be2538 feat(stdlib/S1.1): DeclId for every declaration — additive DeclTable [additive]
Build a DeclTable in parallel with the import facts: every RawDeclRef
(source / imported / namespaced / C-imported) gets a stable DeclId carrying
source path, display name, AST node identity, span, and DeclKind. Namespace
targets record their members' DeclIds (NamespaceTarget.member_ids). A generic
struct's template is keyed by DeclId in a parallel struct_template_by_decl
store, written alongside the live name-keyed struct_template_map.

A Debug-only round-trip cross-check (RawDeclRef -> DeclId -> AST node ptr)
asserts the table identifies the same node across the corpus, run from
buildDeclTable and pinned by a unit test.

Additive (S0.1 class: mirror): the old maps stay active and lowering still
consumes them; nothing reads the DeclTable / struct_template_by_decl for
selection yet (the S4 cutover does). Generated IR + output bytes are unchanged
by construction.

Gate over the baseline-green corpus: zig build, zig build test (424/424),
bash tests/run_examples.sh (540 passed) — all exit 0; single-author output
byte-identical (37 .ir snapshots unchanged).
2026-06-09 11:25:04 +03:00
agra
b9a67d1042 fix(stdlib/E6a): adopt forward struct stub for recursive enum/union (E6A-1)
attempt-1's per-decl enum/union register path panicked on any valid
self- or mutually-referential top-level enum/union: a `*Name` field in
the body is resolved through the stateless `type_resolver.resolveNamed`,
which has no kind context and forward-stubs an as-yet-unregistered name
as a STRUCT. `internNamedTypeDecl` then `findByName`-adopted that struct
stub and called `updatePreservingKey`, whose kind-stability assert tripped
on struct -> enum/union (types.zig:446). The corpus had no recursive
enum/union, so the gate missed it.

Fix: when the slot `findByName` returns is a wrong-kind forward struct
placeholder (empty-fields struct) for an enum/union/tagged_union
registration, re-key it in place (`replaceKeyedInfo`) under the same
TypeId instead of `updatePreservingKey`. This mirrors how a self-ref
struct adopts its own (same-kind) forward stub; the new helper
`adoptsForwardStructStub` gates the re-key precisely to that case, so a
struct adopting a struct stub and every non-recursive enum/union stay on
the byte-identical `updatePreservingKey`/fresh-intern path.

Regression 0799 (single-author): self-ref union linked cells
(`next: *Node`), self-ref enum/tagged-union (`branch: *Tree`), and a
mutual-ref pair (A holds *B, B holds *A); builds and walks each recursive
link. Fail-before: panic at registerUnionDecl on eed2f99. Pass-after:
exit 0, "union=7 enum=42 mutual=99".

Gate: zig build && zig build test && run_examples.sh all exit 0
(538 passed, 0 failed; 0795-0798 + 0752-0794 + FFI byte-identical);
m3te ios-sim build via the main binary exit 0.
2026-06-08 23:55:46 +03:00
agra
eed2f99f76 feat(stdlib/E6a): per-decl nominal identity for enum + union decls
Give top-level ENUM and UNION decls per-decl nominal identity so two
same-name flat enums/unions intern DISTINCT nominal TypeIds instead of
collapsing to one global last-wins entry. Establishes the reusable
non-struct register path the later E6 kind-steps (E6b error-set, E6c
protocol, E6d foreign-class) extend.

Registration side (was: stateless `type_bridge.resolveInlineEnum/Union`
`findByName` last-wins short-circuit, no Lowering access):
- Split the type_bridge inline builders into a body-BUILDER
  (`buildEnumInfo` / `buildUnionInfo`) + the existing thin interner
  wrappers (field-type positions keep the legacy single-slot path).
- Add `Lowering.registerEnumDecl` / `registerUnionDecl` mirroring
  `registerStructDecl`: build the TypeInfo, intern via
  `internNamedTypeDecl(decl_key, name_id, info, nominal_id)` under the
  per-decl nominal identity (reserved slot id, else `shadowNominalId`).
- Reroute all six enum/union registration dispatch sites (scanDecls
  const-wrapped + top-level, lowerDecls/comptime, block-local, local
  const) to the new path.

Shared infra generalized ONCE:
- Pass-0b genuine-shadow pre-pass now reserves struct/enum/union shadow
  slots of the MATCHING kind, grouped by (kind, name), via a kind-generic
  `topLevelTypeDecl` / `reserveShadowSlot`. A forward/self/mutual ref to a
  shadow name binds to the reserved nominal TypeId.
- `namedRefTid` consults `type_decl_tids` for `.enum_decl`/`.union_decl`
  before the global `findByName`.

No new per-kind resolution path: selectNominalLeaf / headTypeGate /
flatTypeAuthorCount already gate every kind. Single-author /
phantom-double-spelling names keep nominal_id 0 (byte-identical corpus).

Regressions 0795-0798 (enum + union: ambiguity over every bare-type form,
and own-wins with distinct nominal TypeIds), fail-before/pass-after:
0795/0797 exit 0 -> exit 1 with the loud "type is ambiguous" diagnostic;
0796 silently printed `own=.east` -> correct `own=.north`; 0798 hard
`field 'm' not found` error -> correct `own=5 dep=9`.

Gate: zig build && zig build test (423/423) && run_examples.sh (537/537)
all exit 0; m3te ios-sim build via the main binary exit 0.
2026-06-08 23:18:29 +03:00
agra
919c7bd855 fix(stdlib/E5): source-aware value-const TYPE inference (F4)
Value-const SELECTION was source-aware for emission/folding (F2/R1/F1), but
expression TYPE inference still read the global last-wins `module_const_map`,
so an inferred return type / coercion on a same-name const borrowed another
module's const TYPE (mixed-type same-name consts were never exercised by the
attempt-1 same-typed goldens).

- expr_typer.zig: the `.identifier` const path now selects via the source-aware
  `selectModuleConst` (own-wins / one-flat-visible) instead of the global
  `module_const_map`. The global map still gates "is this a const name?"; an
  unpartitioned registration-only author emits its global type, and an ambiguous
  bare reference yields `.unresolved` (the emission path diagnoses loudly).
- lower.zig: expose `selectModuleConst` so the type-inference path shares the one
  author selector emission/folding already use.

Audited every `module_const_map` read: emission (4102) and global-init copy
(1447) were already source-aware (attempt-1); the binds-a-value predicate (6400)
is a boolean, not a type read; the in-`selectModuleConst` read (13842) is the
unwired fallback. No sibling inference site leaks.

examples: 0793 mixed-type own-wins inference (A's `K:s32` yields `1`, not the
global `f64`'s `1.000000`); 0794 mixed-type bare → loud ambiguous (exit 1), the
inference change does not mask the ambiguity. Prior E5 surfaces (0786-0792), the
0105 set (0752-0758), E1-E4 type surfaces (0763-0785) and FFI byte-identical;
533 markers green.
2026-06-08 22:07:12 +03:00
agra
5df4ac61a7 fix(stdlib/E5): source-aware same-name VALUE consts (own-wins / ambiguous / cross-module expr-chains)
Re-land the value-const analog of the E1-E4 type work, reconciled onto the
current source-keyed resolver and hardened. A same-name VALUE const declared in
multiple flat-imported modules is now resolved per declaring source, not the
global last-wins `module_const_map`.

- imports.zig: `isPerSourceDecl` retains every non-function `const_decl`
  per-source (value consts + type aliases), so each same-name author reaches
  registration as a distinct author of its own module. Functions and var_decls
  keep first-wins.
- lower.zig:
  * `selectModuleConst` over `module_consts_by_source` — own-wins; exactly one
    flat-visible resolves; >=2 flat-visible bare -> loud ambiguous (consistent
    with the 0755 type / 0724 fn / 0782 generic ambiguities). Rewires every
    consumer: `comptimeIntNamed`, the runtime-id read, the global-init read,
    and the float-name path (`lookupFloatName` / `nameIsFloatTyped`).
  * `SourceConstCtx` + `foldSourceConstInt`/`Float` + `sourceConstIsFloatTyped`
    fold a selected const's RHS with nested same-name leaves re-selected in
    their own author source, so VALUE and array-DIMENSION results are coherent.
  * `pinConstAuthorSource` pins each fold level to the SELECTED const's author
    (F1), including multi-level cross-module chains.
  * cycle guard keyed on (name, author-source), not name alone (F3), so
    same-name nested consts across modules do not trip a false cycle.
  * `emitModuleConst` takes the author source and pins while folding/lowering.
  Registration-time struct/inline-type field dimensions route through the now
  source-aware stateful reader; the type-alias dimension path resolves each
  alias against its own author's consts.
- program_index.zig: expose `isFloatConstType` / `isCountableConstType` for the
  source-aware folds.

examples: 0786 own-wins, 0787 ambiguous (exit 1), 0788 expr-chain value+dim
coherent, 0789 leaf-author-pin, 0790 cross-module cycle-guard (F3), 0791
multi-level cross-module chain, 0792 struct-field registration-time dim.
Single-author corpus byte-identical (524 prior markers green); 531 total.
2026-06-08 21:29:31 +03:00
agra
6406d0fb1f fix(stdlib/E4): collapse generic-struct author matrix into four choke-points
The generic-struct author-selection matrix {bare,qualified} × {site} × {layout,
body} drifted per-site across 12 attempts because method bodies were resolved by
bare template name in `fn_ast_map["Box.method"]`, independent of which author
produced the instance's layout. Collapse it into four choke-points so
layout-author ≡ body-author by construction:

  CP-1 `selectGenericStructHead` — the single layout-head selector every generic
       struct head site funnels through (alias-RHS .call/.parameterized, array-
       literal, static head, resolveTypeCall/ParameterizedWithBindings). Emits the
       visibility / missing-member diagnostics inline; returns a control-flow-only
       union. No head site reads `struct_template_map` for selection directly.
  CP-2 author stamp — non-optional `decl: *StructDecl` on `StructTemplate` (set at
       the sole producer `buildGenericStructTemplate`) + `struct_instance_author`
       written at `instantiateGenericStruct` from the SAME `tmpl` that builds the
       layout; re-stamped on the dedup fast-path so an instance is never returned
       without an author.
  CP-3 alias metadata copy — mirror template/bindings/author from the mangled
       instance onto the alias display name, so an `ABox`-typed receiver is a
       first-class dispatch instance (Counter-2).
  CP-4 `genericInstanceMethod` / `ensureGenericInstanceMethodLowered` — the single
       body reader: inline methods select via the stamped author (`structMethodFn`,
       source-pin follows for free); impl-block methods fall back to the template-
       keyed `fn_ast_map` entry. Routes the four bespoke body sites (static head,
       instance dispatch, param typing, protocol thunk) + the new qualified static
       head (`a.Box(s64).make(7)`, finding #2).

A debug assert locks `struct_instance_author` / `struct_instance_template` keyset
coincidence so a future third writer that forgets the author trips a test.

Goldens 0777/0778/0780 (bare instance method — ptr/by-value/param-typed, finding
#1), 0779/0785 (qualified static head + missing member, finding #2), 0783 (alias
instance dispatch, Counter-2), 0782 (ambiguity containment). 0414/0415/0543 and
the FFI suites stay green.
2026-06-08 20:34:53 +03:00
agra
7ba64d5756 fix(stdlib/E4): bare generic static-method head selects the visible author (type + method)
The static-method-call head `Box(s64).make(7)` was the last uncovered bare-
generic-head instantiation site: it gated visibility with `headTypeLeak` but
then instantiated the global last-wins `struct_template_map` entry and ran the
name-keyed `Box.make` from `fn_ast_map`, so a NON-visible 2-flat-hop same-name
template (and its method) won. `size_of(Box(s64))` picked the visible `b.Box`
(8) while `Box(s64).make(7)` returned a `c.Box`-shaped (16) value.

Route the static-method head through the single bare-VISIBLE author for BOTH
the instantiated type layout AND the method body: split the existing visible-
author selection into `bareVisibleStructDecl` (returns the StructDecl + source;
single selection point, `bareVisibleStructTemplate` now delegates to it — no
drift) and source-pin the method body via the author's own `sd.methods`
(`structMethodFn`) instead of the last-wins `fn_ast_map`. Ambiguity (>1 visible
author) is already diagnosed by the pre-existing `headTypeLeak` gate.

Exhaustive bare-head instantiation-site audit (all callers reaching
`instantiateGenericStruct` / `struct_template_map` for a bare head): .call alias,
.parameterized_type_expr alias, resolveType .call, resolveTypeCallWithBindings,
resolveParameterizedWithBindings — all already route through the visible-author
selection; the static-method head was the only remaining one and is now covered.

Regression 0776: bare generic static-method head with a 2-hop same-name template
asserts the visible author's layout (xtype=8, x reachable); fail-before xtype=16.
2026-06-08 19:06:13 +03:00
agra
246883073c fix(stdlib/E4): bare generic head selects visible author; qualified missing-member diagnoses
E4 non-transitive type rule had two generic-head author-selection holes:

#1 A BARE generic struct head / alias with a single bare-VISIBLE author still
   instantiated a NON-visible 2-flat-hop same-name template, because the
   `.unregistered` gate arm fell through to the global last-wins
   `struct_template_map` winner. Add `bareVisibleStructTemplate`: after the
   visibility gate passes, select the source-keyed template authored by the
   single bare-visible author (own-wins, else the one 1-hop flat author) and
   instantiate THAT instead of the global map's last-wins entry. Null (→ the
   global map, byte-identical) when the visible author IS the canonical one
   (the common single-author case) or the picture isn't a clean single author.
   Applied at every bare generic-struct head/alias site (annotation `.call` /
   `.parameterized_type_expr`, alias-registration `.call` /
   `.parameterized_type_expr`, array-literal head).

#2 A QUALIFIED head `a.Box(..)` whose namespace `a` authors no member `Box`
   silently fell back to the bare global template, instantiating an unrelated
   module's `Box`. Add `qualifiedMemberMissing`: a qualified head whose known
   namespace lacks the member now emits "namespace 'a' has no member 'Box'" and
   poisons with `.unresolved`; a qualified head NEVER reaches the bare global map.

Regressions: 0774 (bare head + bare alias, 2-hop same-name → size=8 alias=8,
fail-before 16 16); 0775 (qualified missing member → diagnostic + exit 1,
fail-before size=16 exit 0).
2026-06-08 18:39:53 +03:00
agra
8c59acbd25 fix(stdlib/E4): qualified generic alias head a.Box(..) selects the namespace author
The const-decl alias-registration path treated a qualified generic head
(`ABox :: a.Box(s64)`) only as a gate exemption, then read the bare last-wins
`struct_template_map` — so `ABox` and `BBox` both instantiated whichever
same-name template won globally (both size 16). attempt-9 routed the annotation
head sites through `qualifiedStructTemplate`; this applies the same selection to
the two alias-registration branches (.call and .parameterized_type_expr) before
the bare fallback, and extracts the shared instantiate-and-register logic into
`registerGenericStructAlias`.

ABox :: a.Box(s64) now resolves to a's template (size 8); BBox :: b.Box(s64) to
b's (size 16). Regression 0773 pins it (fail-before alias a=16 b=16, after a=8
b=16).
2026-06-08 17:56:29 +03:00
agra
eb7636d0f3 fix(stdlib/E4): qualified generic head ns.Box(..) selects the namespace author
A qualified generic type head `ns.Box(args)` was stripped to its bare name and
read from the last-wins `struct_template_map`, so the namespace qualifier never
selected the template author: `a.Box(s64)` and `b.Box(s64)` (two namespaces each
authoring a same-name `Box($T)` with different layouts) both instantiated the
global same-name template. The documented ambiguity escape hatch ("qualify it as
ns.Box") silently produced the wrong layout.

Select the template via the namespace edge (importer -> alias -> NamespaceTarget)
instead of the bare map, at both the .call and parameterized-type-expr head
sites. Two same-name templates instantiated with the same args would also collide
on the mangled name `Box__s64`, so tag the non-canonical author's mangled name
with its source (the canonical bare-map author keeps the untagged name -> no
churn for single-author generics).

Extract `buildGenericStructTemplate` so the bare registration and the new
namespace-qualified selection share one template builder.

Regression: examples/0772 — two namespaces each authoring Box($T) with different
layouts; ns_a.Box(s64) and ns_b.Box(s64) resolve to their own module's template
(sizes 8 and 16). Fail-before on 566de96 (a=16 b=16), pass-after (a=8 b=16).
2026-06-08 17:19:41 +03:00
agra
566de96821 fix(stdlib/E4): type-fn head gate selects the TYPE-FUNCTION author (ordinary fn must not vouch)
attempt-7 made the type-fn head gate kind-aware (a non-function no longer
vouches), but it still accepted ANY function author: a directly-visible
ORDINARY function (`Make :: () -> s32`, zero `$`-params) authorized a hidden
2-flat-hop type-function head (`Make :: ($T) -> Type`), so `size_of(Make(s64))`
silently instantiated the 2-hop type-fn and printed `size=8` at exit 0.

Narrow the author view from "any fn_decl" to "a TYPE-FUNCTION" via a new
`typeFnAuthor` predicate (`fnDeclOfRaw` + `type_params.len > 0`), the same
discriminator every instantiation site uses to recognize a type-fn head. Both
`flatFnAuthorVisible` and `flatFnAuthorAmbiguous` now count only type-fn
authors, so a same-name ordinary function — which cannot be the type head being
instantiated — does not vouch for a 2-hop type-fn head.

Regression 0771: main -> b (`Make :: () -> s32` ordinary fn + flat-imports c)
-> c (`Make :: ($T) -> Type`); `size_of(Make(s64))` -> "type 'Make' is not
visible", exit 1 (fail-before on 94c3cd7: size=8 exit 0). 0770 (non-fn vouch),
0769 (type-fn ambiguity), 0768/0767/0766-0763, 0208/0210 (valid type-fn heads),
0544/0706/0105 and FFI all green & byte-identical.
2026-06-08 16:43:01 +03:00
agra
94c3cd7507 fix(stdlib/E4): kind-aware type-fn head gate (non-fn must not vouch)
The type-fn head visibility check (`headFnLeak`) used the module-scope
NAME predicate `isNameVisible`, so a same-name 1-hop NON-function (a value
const `Make :: 123`) reported the name "visible" and let the global
`fn_ast_map` type-fn — whose real author is 2 flat hops away — silently
instantiate. `size_of(Make(s64))` printed 8 at exit 0 instead of a
visibility diagnostic.

Decide visibility from the ELIGIBLE FUNCTION authors directly reachable
from the use site (`flatFnAuthorVisible`, mirroring `flatFnAuthorAmbiguous`'s
fn-only author view): visible iff the own author or a 1-hop flat-import
author is a `fn_decl`. A non-function does not vouch. Guarded to fall open
when the import facts aren't wired (comptime / directory imports), mirroring
`headTypeGate`. Own / scope-local / 1-hop / directly-imported type-fn heads
still resolve; 0769 ambiguity unchanged.

Regression: examples/0770-modules-type-fn-head-non-transitive (main → b
[`Make :: 123` + flat-imports c] → c [`Make :: ($T) -> Type`]); the bare
`Make(s64)` head emits "type 'Make' is not visible", exit 1.
2026-06-08 16:03:23 +03:00
agra
cb9ef381b5 fix(stdlib/E4): own-wins at non-leaf bare-type sites + type-fn head ambiguity
attempt-6: address Adi's two in-scope findings (#3 deferred to E6).

#1 E4-own-author-type-arg (silent-wrong): the bare-TYPE gate returned
`.proceed` for the querying source's OWN author, so the non-leaf sites
(reflection / type-arg / array-literal / type-value / match arm) dropped it
and re-resolved a same-name flat import via global `findByName`. headTypeGate
now resolves the own author to ITS per-source TypeId (mirroring
selectNominalLeaf's own-wins, 0754); the type-as-value and type-match sites,
which only consumed the poison bit and re-resolved globally, now route through
the gate and use the `.resolved` author. size_of(Widget) with an own + imported
Widget now yields main's own size, not the import's.

#2 E4-type-fn-head-ambiguity (silent-wrong): headFnLeak only checked
isNameVisible, so two flat same-name type-returning functions both reported
"visible" and one was silently instantiated. It now diagnoses >=2 distinct
direct flat type-fn authors (no own author) as ambiguous before the
isNameVisible short-circuit, consistent with the parameterized struct /
protocol heads and the leaf (0755/0767). Own / single / diamond-collapse
type-fn heads still resolve.

Regressions: 0768 (own-wins at every non-leaf bare-type site, fail-before
reflection=16 -> pass-after 8) and 0769 (two flat Make type-fns -> ambiguity
diagnostic exit 1). README: own-wins + type-fn-head ambiguity at every bare-type
site.
2026-06-08 15:22:10 +03:00