Skip to content

Fixed arrays as structural types: Type::tFixedArray replaces TypeDecl dim/dimExpr#3095

Merged
borisbat merged 23 commits into
masterfrom
bbatkin/fixed-array-type
Jun 11, 2026
Merged

Fixed arrays as structural types: Type::tFixedArray replaces TypeDecl dim/dimExpr#3095
borisbat merged 23 commits into
masterfrom
bbatkin/fixed-array-type

Conversation

@borisbat

Copy link
Copy Markdown
Collaborator

Replaces the flattened dim/dimExpr qualifier vectors on TypeDecl with a structural Type::tFixedArray node, so fixed arrays resolve through the same recursive firstType machinery as every other container. Full plan, settled design decisions, and per-stage implementation notes live in-repo in FIXED_ARRAY_REWORK.md (every stage was review-gated on the branch).

Why

On master, dim sat in the qualifier set next to constant/ref, with verified consequences:

  • auto(TT)int[4] bound TT = int — array-ness silently stripped
  • auto(TT)[]float[4][4] — no match (exact dim-count required)
  • var a : M4[3] (M4 = float[4][4]) flattened to float[3][4][4] — typedef identity destroyed
  • daslib carried ~25 workaround overloads (table<K;V[N]>/array<T[N]> families) reconstructing the thrown-away type, each covering only one [] level

What changes for users

  • Natural generic binding: auto(TT) binds the whole array (TT = int[4]); auto(TT)[] peels one level (TT = float[4] from float[4][4]) and inherits parameter constness (non-var binds const, var binds mutable); TT - [] removes one level (was: all levels); default<TT> is a zeroed fixed array.
  • Typedef preservation: M4[3] stays 3 × M4; a[0] is an M4.
  • Multi-dim now matches auto[] overloads (previously fell through to plain auto).
  • ~25 redundant container-FA overloads deleted from builtin.das — base generics cover them via natural binding; default<T[N]> fixed (was broken-unreachable); get_key(table<K;V[N]>) fixed; sort-with-comparator over rows fixed (the transform routed rows to the workhorse-only path); no-comparator multi-dim sort is now a clean error instead of a silent flat-sort; >6-D archive serialization works (was a 6-deep overload stack).
  • RST updated: fixed-array section in arrays.rst; binding rules + one-level -[] in generic_programming.rst.

Invariants kept

  • Memory layout, mangled-name text, and emitted SimNodes unchanged — Stage 2 proved the 801-file generated-AOT corpus byte-identical (modulo hash-iteration emission order); runtime TypeInfo stays flattened forever (it describes layout).
  • TypeDecl shrinks ~12 bytes/node and every isX() predicate drops its dim.size() branch.

Compatibility notes

  • AST serialization version 90 → 91; LLVM_JIT_CODEGEN_VERSION → 0x24.
  • Stale .shared_module DLLs built before this change crash daslang at startup (the dyn-module descriptor scan dlopens them; TypeDecl layout changed). Rebuild external module stacks after pulling.
  • External repos using arg->type->dim.size()==0 guards port to !arg->type->isFixedArray() (dasImgui/nodeEditor/implot edits staged locally, pushed after this merges).
  • Version bump deliberately not included — to be decided against the 0.6.3 release flow.

Gates (local, Windows/MSVC; this PR is the branch's first full-matrix CI)

interp 10920/10920 · AOT 10240/10240 (two-pass, content-stable regen) · JIT 10475/10475 · tests-cpp 56/56 (1.1M assertions) · dasSQLITE 904/904 · lint 34 changed .das files clean · Sphinx clean · zero GC leaks all lanes.

Closes #3077 (AOT emitter native-dim field fix is Stage 2 of this train).
Related: #3090 (LINT003 follow-up, filed during the rework), #3094 (pre-existing das-fmt converter self-test failures, proven not rework-caused via 3-way baseline).

🤖 Generated with Claude Code

borisbat and others added 19 commits June 10, 2026 23:07
Adds the structural fixed-array node and all core TypeDecl support, gated on
baseType==tFixedArray throughout. Nothing produces FA nodes yet (parsers flip
in 1b), so this stage is behavior-neutral: describe/typename text, mangled
names, semantic/lookup hashes and AOT hashes are unchanged for every type
constructible today. dim/dimExpr stay live until the end of Stage 1.

- Type::tFixedArray appended at the END of the enum (AST-only; runtime
  TypeInfo stays flattened forever). Registered in das_to_string /
  das_to_cppCTypeString / the rtti-side Type enum binding. The C API
  das_base_type deliberately does NOT get the value - it describes runtime
  TypeInfo, which never carries it.
- TypeDecl fields: fixedDim / fixedDimExpr (one size per node, element in
  firstType, dimAuto/dimConst sentinels) + typeMacroExpr (dormant until 1b
  moves the typeMacro/typeDecl/tag payload off dimExpr).
- Lifecycle: copy ctor, static clone, gc_collect, visit cover the new fields.
- Identity: isSameType gets a tFixedArray case (fixedDim + element recursion
  mirroring the tArray arm) with the canonical-form debug assert - settled:
  ref/const/temporary live on the OUTERMOST FA node only. isSameExactType
  compares fixedDim.
- Text: describe emits the flattened element-first form ("float[3][4][4]"),
  byte-matching the dim-vector output. Mangled name emits by NATURAL
  RECURSION (settled): per-node [d] + Y<alias>, element inline - unaliased
  chains byte-identical to master; mid-chain aliases shift the Y<> slot to
  the level it labels ("[3][4]Y<M4>[4]f"), covered by the planned one-time
  version bumps. Mangled-name PARSE flip rides 1b - the [N] text is identical
  in both worlds, so the parser must build whichever representation the
  program uses. describeCppType reproduces TDim<TDim<...>,N> nesting exactly.
- Size family: getCountOf64 / getStride64 / getBaseSizeOf64 / getAlignOf /
  getSizeOf64-failed chain-walking arms preserve the dim-vector meanings.
- Classifier sweep: FA is transparent to classification exactly like the old
  dim vector (peel/recurse arms beside every tArray arm). Deliberate calls:
  gcFlags recurses WITHOUT gcFlag_heap (inline storage); hasNonTrivialCtor
  DOES recurse (FA elements are live at init, unlike empty array<T>);
  isCircularType descends FA (no heap indirection to break cycles);
  isVecPolicyType guard extended; canDelete peels FA on both self and
  pointee; collectAliasing recursion is slightly conservative vs the old
  form (extra bare-element append, noted in-source for the 1b review).
- tests-cpp/small/test_fixed_array_typedecl.cpp: hand-built FA nodes proven
  against equivalent dim-vector nodes (gc_guard cleanup) - describe/mangled/
  cppType text equality, size family incl. float3[4]=48, isSameType matrix,
  deep clone of fixedDimExpr, semantic/lookup hash discrimination,
  classification parity, and the settled mid-chain-alias mangling golden.

Gate: full local Release build green (incl. utils -exe steps + AOT objects),
tests-cpp-small 50/50 (1.1M assertions, leak-check clean), dastest
10784/10784, test_aot 10123/10123 run exactly as CI does. Note for local
setups: the TypeDecl layout change requires rebuilding external
.shared_module binaries (dasImgui / dasImguiImplot junctions rebuilt here).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…eMacroExpr

Atomic payload flip (FIXED_ARRAY_REWORK.md, Stage 1b first commit; CI-green
standalone — arrays stay on dim vectors until 1b-ii):

- parsers: gen2 x6 / gen1 x4 payload writes go to typeMacroExpr (typeMacro
  name+args, typedecl(expr), $t(expr) tag on its autoinfer firstType)
- C++ reads: typeMacroName, describe (tag/typeDecl/typeMacro arms), mangled
  emit (^<name;hash> — text byte-identical), moreSpecialized typeMacro
  comparison, inferPartialAliases, inferTypeExpr typeDecl resolution
- validator: tracks typeMacroExpr (incl. tag payload on non-payload baseTypes,
  which the standard visitor never walks) + resolved-FA fixedDimExpr analog
- serializer: typeMacroExpr serialized unconditionally after the baseType
  switch (covers payload nodes AND tag-on-autoinfer); payload arms drop
  dimExpr; tFixedArray arm added; version 89 -> 90. Note: no in-tree test
  lane exercises AstSerializer — symmetry of the shared read/write path +
  version bump is the safety here
- das binding (pulled forward from 1f, settled): .dimExpr field becomes a
  read-only compat property dimExprCompat() — non-empty typeMacroExpr wins,
  "whatever dimExpr used to hold for this node" — so typemacro_boost, clargs,
  ast_match, templates_boost keep working unmodified through Stage 1
- remaining dimExpr references classified: array semantics (infer erase/
  dimConst resolution -> 1d) or still-live-field infrastructure (visit/clone/
  gc/hash/serializer arms — die with the field at end of Stage 1)

Gates (all green): full Release build incl. utils -exe + AOT regen;
tests-cpp-small 50/50 (1,103,300 asserts); dastest 10784/10784;
test_aot 10123/10123 (CI invocation).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…verter) is fixed last

Per review: the conversion util's vendored gen1 grammar (writes real TypeDecl
dim/dimExpr, same 10 site-shapes as the in-tree gen1 parser) ports at the very
end of the train, not Stage 5. The dim/dimExpr deletion commit carries only the
minimal mechanical compile-flip.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The FA parser flip (FIXED_ARRAY_REWORK.md, Stage 1b second commit). Both grammars
and the mangled-name parser now produce structural tFixedArray chains instead of
dim/dimExpr vectors; nothing downstream understands them yet — the world stays
dark until 1c/1d (commit-as-is settled with Boris, class burndown in the plan).

- parser_impl: appendDimExpr becomes the FA-chain builder (3-arg, returns chain
  head; const-int shortcut preserved, fixedDimExpr always retained);
  attachDimChain wraps the dim chain around the element and hoists the qualifier
  flags the old world fused onto the dim-carrying node (const/ref/temporary,
  their remove*, implicit/explicitConst/explicitRef/isExplicit/autoToAlias) to
  the chain head — alias NEVER hoists; appendAutoDim covers gen1's push-at-end
  `[]` arm (wrap-innermost when already FA). The legacy 2-arg appendDimExpr
  stays for THE THIRD PARSER (utils/dasFormatter) and dies with the fields.
- ds2/ds parser: dim_list arms build chains, splice arms attachDimChain (the
  `$dimlist->dimExpr.clear()` ownership hack disappears), gen1 `{{ }}` table
  literal synthesizes FA(dimAuto, autoinfer). Grammar errors kept verbatim.
  Old-world quirks reproduce structurally: late-dim front-splice
  (`int[3] const [4]` = [4][3]) and gen1 push-at-end (`int[][]` auto innermost).
- mangled-name PARSE case '[' builds an FA node and claims the fullName
  remove-suffixes plus an immediately-following Y<name> for THAT node
  ([3][4]Y<M4>[4]f labels the two-dim node; known cosmetic re-parse asymmetry
  for element-side labels, structural identity unaffected).
- tests-cpp/small/test_fixed_array_parser.cpp: parse-shape suites for both
  grammars (parse-only via a deliberate trailing parse error — infer cannot
  digest FA until 1d), {{ }} makeType shape, and mangled emit<->parse round
  trips. Suite also pins a master fact: `int[3][]` is a gen1 syntax error
  (dim_list shift preference), so push-at-end only composes via int[]/int[][].

Gates: full Release build green; tests-cpp-small 53/54 (the 1 red compiles
`let arr : int[5]` through infer — recorded 1d burndown). dastest runs ZERO
tests: builtin.das bulk-push signatures with `[]` are FA now, make-array arg
types are still infer-made dim-vectors, and every .das_module initialize meets
the mismatch at to_array_move — daslang.exe cannot boot the in-repo project
until 1d. test_aot not attempted (AOT regen needs a booting daslang).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… flattens

Stage 1c of FIXED_ARRAY_REWORK.md: the C++ interop side now produces FA chains.

- typeFactory<TDim<TT,size>> and typeFactory<TT[dim]> wrap via the new
  makeFixedArrayTypeDecl header helper, which hoists the canonical
  ref/const/temporary trio off the element (mirrors the parser's
  attachDimChain). isNativeDim moves onto the FA node — its only consumer
  is aot_cpp.das reading the indexed type's head, unchanged.
- SETTLED (discussed): the natural recursion fixes the latent multi-dim
  order bug — old typeFactory<int[3][4]> produced inner-first dims [4,3]
  (das int[4][3], row stride 12 instead of 16); believed unexercised
  in-tree. New shape FA(3, FA(4, int)) is correct by construction and
  pinned by tests (describe "int[3][4]", stride 16).
- makeTypeInfo gains the FA-flatten arm, pulled forward from 1f by
  necessity: typeFactory feeds handled-struct FIELD types and builtin
  signatures whose runtime TypeInfo must keep the exact flattened shape —
  without it a C-array field silently produces dimSize=0 TypeInfo. The
  arm collects chain dims onto a scratch element clone (head qualifiers
  ride along); mangled-name cache key unaffected since chain and
  flattened text are identical. ManagedVector's walk scratch
  (ast_handle.h) flips to FA(1, clone) and exercises the arm.
- makeArgumentType needs no edit — composes via the FA-aware isRefType.
  Builtin mangled names unchanged (unaliased FA chains byte-identical to
  the old text); semantic hashes shift, moot while the world is dark.
- tests-cpp/small/test_fixed_array_interop.cpp: typeFactory shapes (incl.
  multi-dim fix + const-hoist canonical form), makeArgumentType
  composition, and makeTypeInfo flatten parity (dimSize/dim/size/flags/
  hash byte-equal vs dim-vector input, shared cache key).

Gates: full Release build green; tests-cpp-small 55/56 (the 1 red is the
known 1d burndown item — int[5] through infer). World stays dark until 1d.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… flipped

Matching core: inferGenericType FA arm (fixedDim match, dimAuto wildcard,
firstType recursion; plain auto(TT) binds whole chains via the clone path),
applyAutoContracts FA recursion. updateAliasMap untouched by design (auto(TT)[]
rides firstType recursion; the dim.clear() root-cause line is now a dead no-op).

Alias/verify: inferAlias/inferPartialAliases FA arms with the FA-only label
rule (typedef name kept as display label only when the resolved type is an FA
chain); per-node dimConst eval in inferTypeExpr; verifyType FA arm.

Expression typing: ExprAt/ExprSafeAt FA peel (direct + pointer-to-FA), for-loop
source peel, ExprNew chain rewrap around the pointer (dead dim-copy remnants
removed), typeinfo is_dim/dim/dim_table_value/is_iterable, finalize_dim gate,
ExprAscend FA-of-handle error, ExprWith, each-promotion gate.

Make-literals: make-variant/make-struct/make-array convert to the element-walk
(mkBaseT) + makeFixedArrayTypeDecl result-wrap pattern; structToTuple walks FA
at entry; where-block passT wraps. makeStructWhereBlock wraps too, fixing a
latent double-dim append when makeType carried an explicit dim. gen2
fixed_array of an already-dim'd element now wraps OUTER-most (old push_back
appended inner-first - same latent order-bug family as the 1c typeFactory fix).

Singles: moreSpecialized ranks via fixedDim + FA subtype recursion;
allocate_stack ExprNew gate; escape-analysis stack-alloc gate + element walks
(persistent flag was missed through the wrapper); tryPromoteConstInt and
clone_dim dispatch gates; needAvoidNullPtr sees through FA when allowDim;
isLocalOrGlobal ExprAt gate.

No-edits proven: dim-GATED classifiers (isStructure/isVariant/isString/
isPointer/isVoid/isWorkhorseType) return false on the FA wrapper exactly as
they did on a dim'd node, so baseType-gated call sites keep master behavior
for free. Intentional divergence: [[EnumT[2]]] no longer collapses to a single
const (master quirk - isEnumT ignored dim).

Gates: build green; tests-cpp 55/56 - the known-red int[5] test now passes
compile and fails in simulate, i.e. the failure crossed the 1d/1e boundary on
schedule. World stays dark until 1e (simulate lowering); 1d+1e push as a train.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
ast_simulate.cpp: make-variant/make-struct lowering get element views (the
findArgumentIndex/getVariantFieldOffset/structType/annotation reads went
through the wrapper; getStride needs NO walk - FA head getStride equals the
old dim'd getStride by construction); fakeVariable flatten hack wraps via
makeFixedArrayTypeDecl (ref hoists to the head); ExprAscend/ExprNew dispatch
walks to the element (persistent flag was silently missed through the
wrapper) and ExprNew reads the pointee size via a pointer-node walk;
sv_trySimulate_At and both ExprSafeAt branches take range from fixedDim;
for-loop fixedSize + the FixedArrayIterator arm flip. ExprDelete untouched:
infer routes FA delete to finalize_dim, and the total==1 assert proves a
dim'd type never reached it in the old world either.

Boot-gate fallout fixed en route:
- src/builtin: expect_dim contract and the sort transformCall read fixedDim
  (innermost node = old dim.back() semantics).
- 1a-gap FA arms in ast_typedecl: isAliasOrA2A (isAlias() returned false on
  FA-of-alias, so ExprCast never resolved `TT -const[N]` - the to_array_move
  body), applyRefToRef, collectAliasList, isAotAlias.
- inferAlias FA arm now applies+clears the head's hoisted remove* contracts,
  the way the dim'd alias leaf used to.
- AST-GC discipline (new failure class, cdb-proven): a fresh node held only
  in a C++ local across inferFunctionCall gets collected - ExprNew now
  re-derives the pointer node from the rooted expr->type after the call.
  Related: gc_local frees ONLY the head while TypeDecl's copy ctor
  deep-clones children, so a guarded deep clone leaks its subtree - the
  makeTypeInfo FA-flatten arm now uses a shallow borrow scratch, and the
  ManagedVector walk scratch guards its element node (master parity).
- daslib/ast_boost: walk_and_convert_array/table built dim-vector types via
  the das-side .dim push (made my make-array infer double-wrap); they now
  wrap via the new public make_fixed_array_type helper (canonical qualifier
  hoist), walk_and_convert dispatches on tFixedArray, and fixedDim /
  fixedDimExpr are exposed on the das-side TypeDecl (1f slice pulled forward
  by necessity - dastest -> clargs -> $v hits this path at boot).

Gates (train tip): tests-cpp 56/56 - the int[5] known-red flipped; daslang
boots and runs fixed arrays end-to-end; tests/fixed_array characterization
86/86; zero GC leaks; full-tree dastest 10632 tests, 10621 passed, 11
file-level compile errors = the 1f burndown (dasPUGIXML/test_serial_dim,
decs/dim_test, json/types, language/invalid_types, match x2, stbimage x4,
typemacro/test_basic).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…mpat

Stage 1f of FIXED_ARRAY_REWORK.md. The 11-file burndown collapsed into
four classes:

1) Generic alias dim semantics = MASTER-COMPAT PEEL (settled). Master's
   rule, pinned empirically against a saved pre-flip build: a generic
   alias use-site's own dims REPLACE the bound type's dims - bare TT
   bound from int[3] is the ELEMENT int; TT[4] is int[4]; the strip
   applies even through array<auto(TT)>. Module typedefs append
   naturally (both worlds already agreed). peelFixedArrayAliasBinding
   at the alias leaf of inferAlias + inferPartialAliases (head
   qualifiers transfer to the element; the partial-alias arm keeps the
   binding's label). The FA recursion arm now HOISTS the resolved
   element's ref/const/temporary to the chain head (canonical form) -
   without it TT[4] kept const on the element and var-decl
   const-stripping missed it. daslib's `TT[typeinfo dim(x)]`
   reconstruct pattern (json_boost/PUGIXML_boost/decs) works unmodified.

2) das-side `.dim` is now a READ-ONLY flattened compat property
   (TypeDecl::dimCompat -> per-node transient dimCompatCache; NOT the
   legacy dim field - warming that would nondeterministically revive
   dead C++ dim-vector loops on FA nodes). 59 daslib read sites ride it
   unmodified. das writers break at compile by design; in-tree writers
   ported here: match.das (pop -> firstType), typemacro test fixture +
   tutorial twin (-> make_fixed_array_type), dasGlsl produce_zero,
   dasLLVM llvm_jit x7 (clear/erase/shift-resize -> element walks; the
   `new Handle[N]` tHandle gate now walks the chain;
   LLVM_JIT_CODEGEN_VERSION 0x22 -> 0x23).

3) aot_cpp.das had four FA gaps (the 1d+1e train's AOT lane was red
   from these): describeCppTypeEx panicked on FA nodes ("Missed type
   tFixedArray") and emission swallows macro panics, so struct fields
   and local declarations silently vanished from generated C++; dim'd
   make-variant dropped its das_get_variant_field::set wrapper; dim'd
   make-struct tHandle gate + tuple/variant field helpers, same class;
   dim'd ExprNew emitted the POINTER type where das_new_dim<> wants the
   POINTEE (TDim<T**> conversion error) and mis-branched handle news
   past das_new_dim_handle. All fixed via the fa_element clone-walk
   helper (clone-walk keeps the result non-const whatever flavor the
   caller holds); dims still emit from the head's .dim view.

4) verifyType's FA too-big check now multiplies element COUNTS across
   the chain (master parity: 32767^2 passes infer; lint owns byte-size
   with the site-specific exceeds_* codes). decs get_ro declared its
   dim'd return as TT[typeinfo sizeof(value)] - bytes - and worked on
   master only because an unresolvable result dim was silently
   discarded; fixed to typeinfo dim(value).

Known gap (deliberate): -[] (removeDim) on FA-typed generic params
still erases the legacy vector only - zero in-tree users; revisit at
Stage 4/5.

Gates: alias probes match master 100%; all 11 burndown files green;
tests-cpp 56/56; tests/fixed_array 86/86; full tree 10784/10784
interpreted and 10123/10123 under test_aot -use-aot; zero GC leaks in
both modes; lint 0 errors on every changed file.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…weep

Pre-push lint on the 1f-touched files surfaced two pre-existing issues:

- STYLE030 false-flagged `require daslib/functional` in decs.das as
  unused: repeat_ref is called only inside an UNINSTANCED generic body,
  where calls stay unresolved (func==null) and leave no used_modules
  trace - dropping the require breaks instantiation in every decs dim
  test. STYLE030 now collects unresolved generic-body call names
  (qualification stripped) and treats a require as used when it exports
  a function or generic matching one of them. Genuinely-unused requires
  still flag (probed).

- modules/dasGlsl/glsl_internal.das: 148 pre-existing STYLE028 hits
  (self-> receivers on class method calls); mechanical sweep, since
  touching the file for the .dim writer port put it under the hook's
  changed-file lint.

Gates: lint 0 issues on all touched files; full tree 10784/10784, zero
GC leaks.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Revises the alias decision in the previous commit, per review: auto(TT)
matched against int[4] binds TT = int[4]; TT[2] is int[2][4] (an array
of 2 TT, natural nesting); array<auto(TT)> against array<int[3]> binds
TT = int[3] - inexpressible on master; def two(a : auto(TT); b : TT)
accepts (int[3], int[3]). The flattened world's strip/replace rule
(bare TT was the ELEMENT; use-site dims REPLACED the bound ones) is
deliberately dead - it made generics clumsy and unsupportable, and
existed only because a single node carried both element and dims. The
master-compat peel is reverted.

The invariant that keeps most of daslib unchanged: auto(TT)[] - the
explicit [] suffix - still binds TT to the ELEMENT (the [] eats one dim
level during matching), so the builtin clone/to_array/table-[] families
and decs get_ro's TT[typeinfo dim(value)] reconstruct remain correct as
written.

Ported the bare-auto(TT) dim arms to the new binding: json_boost
from_JV and PUGIXML_boost from_XML (`var ret : TT -const -&` - TT IS
the array type), and seven decs functions (decs_array / get /
get_default_ro / get_component / ComponentMap get / make_callbacks /
make_component) where every `static_if (typeinfo is_dim(...))` arm
collapsed into its else arm - decs.das alone nets -160 lines, which is
the supportability win this flip buys.

External code using the TT[typeinfo dim(x)] reconstruct on bare
auto(TT) params breaks loudly at compile; bodies using bare TT as the
element type change meaning silently - test suites are the net.

Gates: probe matrix = natural binding everywhere; tests-cpp 56/56;
tests/fixed_array 86/86; json/PUGIXML/decs green; full tree 10784/10784
interpreted + 10123/10123 under test_aot -use-aot; zero GC leaks in
both modes; lint clean.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…hive recursion; default<T[N]>

Stage 4's builtin.das half, pulled forward per review: natural alias binding
makes the base generics cover every dedicated container-FA overload with
identical bodies. Deleted 22 defs (array<T[N]> push x2/emplace/push_clone x2;
table<K;V[N]> get x4/get_value/insert x2/insert_clone x2/emplace/values x2/
get_key/clone x2; one-arg clone x2). Capability gains: push_clone of clone-only
rows (old gate was can_copy; the const-src body was un-instantiable rot),
at-index push of FA rows, size mismatch is now a type error.

archive.das: 6 stacked [][]..[] serialize overloads -> ONE natural recursion
(auto(TT)[] peels a level per call); bulk-memcpy gate is_raw && (is_workhorse
|| is_dim) keeps exact wire parity; >6-D now works.

default<T[N]> fixed (broken-but-unreachable on master): 0-element FA make-struct
now zero-inits the whole declared shape - infer tolerates 0 provided elements,
simulate zero-fills getSizeOf() instead of one element's stride.

Target spec enabled: _target_inference.das -> test_target_inference.das incl.
the get_key subtest (Stage 4 gate). Settled decoration: auto(TT) <- int[4]
binds "int const[4]"; auto(TT)[] <- float[4][4] binds "float[4]" (the [] ate
the qualifier-bearing chain head).

style_lint STYLE030: unresolved generic-body call names now match against the
require's PUBLIC RE-EXPORT CLOSURE too (daslib/rtti re-exporting builtin rtti
- archive.das serializes function<> by mangled-name hash in a generic body).

Gates: fixed_array 95/95, full tree 10793/10793 interpreted, 10132/10132 under
test_aot -use-aot (two-pass build), lint 0 issues, formatter clean. Net -313 LOC.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…fix #3077 native-dim field pun

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… auto, master parity)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…tion-pass fix; JIT codegen version 0x24

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…s deleted; stdlib generics pinned multi-dim

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…posed+renamed; -[] peel; rst/glsl FA render fixes

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
… makeTypeInfo chain-native; dasFormatter grammar flip; serialization v91

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…tural generic binding + one-level -[] peel in generic_programming.rst

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…s (post-rebase)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Copilot AI review requested due to automatic review settings June 11, 2026 06:34

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR completes the fixed-array type rework by replacing TypeDecl’s flattened dim/dimExpr qualifiers with a structural Type::tFixedArray node, so fixed arrays participate in the same recursive type machinery as other container types and preserve array-ness through generic binding, typedefs, and codegen/tooling.

Changes:

  • Introduces structural fixed-array representation (Type::tFixedArray) across parser, AST, inference, simulation, RTTI/serialization, and AOT/JIT tooling.
  • Migrates type-macro payload storage from TypeDecl.dimExpr to TypeDecl.typeMacroExpr, updating grammars, serialization, and macro helpers.
  • Updates/extends test coverage (das + C++) for parsing, text/mangling, interop, inference semantics, and stdlib generic behavior over multi-dim/aliased fixed arrays.

Reviewed changes

Copilot reviewed 76 out of 76 changed files in this pull request and generated 3 comments.

Show a summary per file
File Description
utils/fix-lint-errors/main.das Updates fixer logic to detect fixed arrays via Type.tFixedArray.
utils/dasFormatter/ds_parser.ypp Updates formatter grammar actions to build tFixedArray chains and use typeMacroExpr.
utils/dasFormatter/ds_parser.cpp Regenerates formatter parser output reflecting the grammar changes.
tutorials/macros/type_macro_mod.das Ports tutorial typemacro example from dimExpr to typeMacroExpr and uses fixed-array constructors.
tests/typemacro/test_basic.das Updates typemacro tests/docs to reference typeMacroExpr.
tests/typemacro/_typemacro_mod.das Updates raw typemacro module to consume typeMacroExpr and construct fixed arrays structurally.
tests/fixed_array/test_target_inference.das Enables/updates acceptance tests for new generic binding and -[] semantics.
tests/fixed_array/test_stdlib_generics.das Adds coverage for stdlib generics operating over multi-dim/typedef’d fixed arrays.
tests/fixed_array/test_interop.das Makes interop test AOT-included and expands coverage for native C-array field surfaces.
tests/aot/CMakeLists.txt Re-includes fixed-array interop test into the AOT glob.
tests-cpp/small/test_fixed_array_typedecl.cpp Adds C++ tests for tFixedArray text/mangle/size/identity/hash/classification.
tests-cpp/small/test_fixed_array_parser.cpp Adds C++ parse-shape tests for both grammars and mangled-name round-trips.
tests-cpp/small/test_fixed_array_interop.cpp Adds C++ tests for typeFactory fixed-array chains and flattened TypeInfo.
src/simulate/debug_info.cpp Adds human-readable name for Type::tFixedArray.
src/parser/parser_impl.h Replaces old dim helpers with appendDimExpr/attachDimChain/appendAutoDim for FA chains.
src/parser/parser_impl.cpp Implements FA-node construction and chain attach/auto-dim behavior (incl gen1 quirks).
src/parser/ds2_parser.ypp Updates gen2 parser to build FA chains and use typeMacroExpr.
src/parser/ds2_parser.cpp Regenerated gen2 parser output.
src/parser/ds_parser.ypp Updates gen1 parser to build FA chains and use typeMacroExpr.
src/parser/ds_parser.cpp Regenerated gen1 parser output.
src/builtin/module_builtin_runtime.cpp Updates expect_dim compatibility check to use tFixedArray.
src/builtin/module_builtin_runtime_sort.cpp Updates sort call rewrite to treat fixed arrays structurally (outermost dimension).
src/builtin/module_builtin_rtti.h Exposes Type::tFixedArray via builtin RTTI enum binding.
src/builtin/module_builtin_ast_serialize.cpp Updates AST TypeDecl serialization for FA nodes + moves typemacro payload to typeMacroExpr; bumps version.
src/builtin/module_builtin_ast_annotations_1.cpp Exposes new TypeDecl fields (fixedDim, fixedDimExpr, typeMacroExpr) to scripts.
src/ast/ast.cpp Updates “local/global” checks to treat fixed arrays via tFixedArray.
src/ast/ast_validate.cpp Ensures validation/GC-root tracking walks typeMacroExpr and fixedDimExpr.
src/ast/ast_simulate.cpp Updates simulation paths that need “element view” when makeType is a fixed-array chain.
src/ast/ast_program.cpp Adjusts constant creation logic post-dim removal.
src/ast/ast_lint.cpp Updates null/annotation pointer heuristics to treat fixed arrays as “dim’d”.
src/ast/ast_infer_type_op.cpp Updates inference ops to detect fixed arrays via tFixedArray.
src/ast/ast_infer_type_helper.cpp Adds FA verification + updates alias inference to preserve fixed-array structure and new -[] behavior.
src/ast/ast_infer_type_function.cpp Updates overload specialization comparisons for FA vs non-FA and typemacro arg storage.
src/ast/ast_generate.cpp Updates generated helper types to wrap counts via FA nodes instead of dim-vectors.
src/ast/ast_escape_analysis.cpp Updates stack allocation heuristics and type unwrap logic for FA chains.
src/ast/ast_debug_info_helper.cpp Flattens FA chains into runtime TypeInfo.dim[] while keeping element metadata correct.
src/ast/ast_allocate_stack.cpp Updates new[] stack-top sizing detection for FA nodes.
modules/dasSQLITE/src/dasSQLITE.main.cpp Updates uint8-pointer-to-string fixup logic to no longer depend on dim-vectors.
modules/dasSQLITE/daslib/sqlite_linq.das Updates scalar renderability gating for FA via Type.tFixedArray.
modules/dasSQLITE/daslib/sqlite_boost.das Ports typemacro payload access to typeMacroExpr and applies minor cleanups.
modules/dasPUGIXML/daslib/PUGIXML_boost.das Updates fixed-array generic binding usage to new “TT is the whole array” semantics.
modules/dasLLVM/daslib/llvm_jit.das Updates LLVM JIT codegen/interop for FA nodes and element-type extraction.
modules/dasLLVM/daslib/llvm_jit_run.das Bumps JIT codegen cache version.
modules/dasLLVM/daslib/llvm_jit_common.das Reworks LLVM type construction to recurse through FA nodes.
modules/dasLLVM/daslib/llvm_exe.das Updates extern collection logic for new T[N] where head is tFixedArray.
include/daScript/simulate/debug_info.h Adds tFixedArray to runtime debug Type enum (documenting AST-only nature).
include/daScript/ast/ast_typedecl.h Adds fixedDim/fixedDimExpr/typeMacroExpr, introduces makeFixedArrayTypeDecl, updates classifier helpers.
include/daScript/ast/ast_infer_type.h Extends alias lookup APIs to propagate constUnderDim.
include/daScript/ast/ast_handle.h Fixes makeTypeInfo scratch construction for FA head + element lifetime under GC guards.
examples/flatten/backend/shader_dsl_boost.das Updates shader DSL validation to reject fixed arrays via Type.tFixedArray.
doc/source/reference/language/generic_programming.rst Documents new fixed-array generic binding + one-level -[] behavior.
doc/source/reference/language/arrays.rst Documents multi-dim fixed arrays, alias composition, iteration semantics, and typeinfo dim.
daslib/typemacro_boost.das Ports typemacro helper to typeMacroExpr and FA detection.
daslib/templates_boost.das Ports tag payload access to typeMacroExpr.
daslib/style_lint.das Updates array/dim checks and adds unresolved-call tracking to reduce false “unused require” flags.
daslib/sort_boost.das Updates qsort macro to route fixed arrays via FA detection.
daslib/rst.das Updates type description printing to emit FA chains structurally.
daslib/perf_lint.das Updates perf lint array detection logic to include Type.tFixedArray.
daslib/match.das Updates pattern matching helpers to treat fixed arrays structurally (fixedDim + element recursion).
daslib/json_boost.das Updates fixed-array decode path for new generic binding semantics (TT is full FA).
daslib/is_local.das Updates local/scope helpers to treat fixed arrays via Type.tFixedArray.
daslib/decs_boost.das Updates comments/logic references from dim-vectors to FA nodes.
daslib/contracts.das Updates contract checks to treat fixed arrays as array-like via Type.tFixedArray.
daslib/clargs.das Ports Option typemacro payload access to typeMacroExpr.
daslib/builtin.das Removes redundant fixed-array overload stacks now covered by natural binding.
daslib/ast_match.das Ports tag payload access and updates dim-field matching for FA nodes.
daslib/ast_boost.das Adds make_fixed_array_type helper and updates conversion logic to build FA nodes.
daslib/archive.das Generalizes fixed-array serialization to arbitrary depth via structural recursion.
daslib/apply.das Drops obsolete dim-vector assertions for apply macros.
daslib/aot_cpp.das Updates AOT C++ type description and fixes native C-array field access punning to TDim view.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/ast/ast_infer_type_helper.cpp
Comment thread src/ast/ast_program.cpp
Comment thread src/builtin/module_builtin_runtime_sort.cpp
das2rst validates handmade docs positionally against reflection:
- enumeration-rtti-Type.rst: add line for the new tFixedArray value
- structure_annotation-ast-TypeDecl.rst: dim/dimExpr lines replaced by
  fixedDim/fixedDimExpr/typeMacroExpr; firstType line mentions the
  fixed-array element
- new handmade doc for ast_boost::make_fixed_array_type (das2rst
  generated a // stub, which the docs workflow rejects)

Verified locally: das2rst clean, no stubs, no untracked generated files
(generated/ is gitignored), sphinx-build -W html succeeds.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 79 out of 79 changed files in this pull request and generated 2 comments.

Comment thread src/ast/ast_infer_type_helper.cpp
Comment thread src/builtin/module_builtin_runtime.cpp
borisbat and others added 2 commits June 11, 2026 00:35
- makeConst: restore the fixed-array early-return dropped in stage 6
  (Copilot review; unreachable today but master returned nullptr
  gracefully, and the structural-world spelling of the old dim guard
  is the baseType check)
- expect_dim: compatibility error message says "fixed size array"
  instead of the legacy "dim []" terminology (Copilot review)
- tests-cpp: wrap bit-field operands in bool() inside CHECK macros.
  doctest's expression decomposition binds operands by reference,
  which C++ forbids for bit-fields; MSVC tolerated it, every clang/gcc
  CI lane failed to compile. Noted in skills/writing_cpp_tests.md.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
In the flattened world, plain auto(T) binding did not carry dims, so
safe_addr(arr) where arr : bool[4] bound T=bool and produced bool?# —
an element pointer that matched C-interop params like
`bool? const implicit` (glGetBooleanv et al). The structural world
binds T to the whole array, producing bool[4]?#, which correctly no
longer matches — breaking dasOpenGL in extended_checks (the module is
GLFW-gated, so local suites never compile it).

Restore the rail where it belongs: safe_addr(x : auto(T)[]&) : T -&?#
returns a temporary pointer to the FIRST ELEMENT, mirroring C array
decay. Multi-dim peels one level (int[2][3] -> int[3]?#), exactly like
C's T[2][3] -> T(*)[3]. Overload resolution prefers the [] form for
fixed arrays and keeps the base form for everything else; call sites
(opengl_state.das and downstream repos) need no edits.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 81 out of 81 changed files in this pull request and generated no new comments.

The Uncategorized-section grep in doc.yml rejects any public function
not assigned to a group_by_regex in das2rst.das — a separate gate from
the das2rst run itself, which passes locally without it.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 82 out of 82 changed files in this pull request and generated 4 comments.

let was_in_txn = db |> in_transaction()
db |> exec(txn_begin_sql(was_in_txn, mode))
var success = false
var success : bool
Comment thread modules/dasSQLITE/daslib/sqlite_boost.das
Comment thread daslib/ast_boost.das
Comment on lines 2709 to 2711
uint32_t AstSerializer::getVersion () {
static constexpr uint32_t currentVersion = 89;
static constexpr uint32_t currentVersion = 91; // 91: TypeDecl dim/dimExpr fields deleted (FIXED_ARRAY_REWORK.md, stage 6)
return currentVersion;
@borisbat borisbat merged commit 2f83654 into master Jun 11, 2026
34 of 37 checks passed
borisbat added a commit to profelis/daScript-plugin that referenced this pull request Jun 11, 2026
daslang 0.6.3 (GaijinEntertainment/daScript#3095) replaces TypeDecl's
flattened dim vector with structural Type::tFixedArray chains (element
in firstType, one size per node in fixedDim). New server/0.6.3 scripts
walk the chain in parse_typedecl; the client model stays flat (leaf
baseType + dim list + tdk1 = one-level peel), so completion.ts is
untouched. Registered in VERSION_SCRIPT_DIRS; 0.6.1/0.6.2 binaries
keep the 0.6.1 scripts.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
profelis pushed a commit to profelis/daScript-plugin that referenced this pull request Jun 11, 2026
daslang 0.6.3 (GaijinEntertainment/daScript#3095) replaces TypeDecl's
flattened dim vector with structural Type::tFixedArray chains (element
in firstType, one size per node in fixedDim). New server/0.6.3 scripts
walk the chain in parse_typedecl; the client model stays flat (leaf
baseType + dim list + tdk1 = one-level peel), so completion.ts is
untouched. Registered in VERSION_SCRIPT_DIRS; 0.6.1/0.6.2 binaries
keep the 0.6.1 scripts.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
pull Bot pushed a commit to forksnd/daScript that referenced this pull request Jun 11, 2026
…UDE.md/skills

Plan lives in COVERAGE_GAP.md (stages 0-4; evidence base = the PR GaijinEntertainment#3095
babysitting session, where every CI failure was a local-vs-CI oracle
mismatch).

Stage 0 (this PR):
- CLAUDE.md: syntax/factual corrections are now fix-in-place ALWAYS
  (verified against ds2_parser.ypp + probe-compile); propose-first
  narrowed to restructuring/removals/new skills.
- CLAUDE.md: new "Fixed arrays (structural since 0.6.3)" section —
  chain model, one-peel rule, generic binding (whole-bind vs []-peel,
  one-level -[]), safe_addr element decay, TypeInfo-stays-flattened,
  the both-worlds isArray() note for externals.
- skills/das_macros.md: macro-author view — fixedDim sentinels,
  qualifiers on the chain head, make_fixed_array_type, typemacro
  payloads in typeMacroExpr (dimExpr is gone), leaf-walk recipe.
- skills/linq.das examples: typeDecl.dim (deleted field) -> argTypes.
- Gen2 currency review: every syntax claim in CLAUDE.md's gen2 section
  + das_formatting/regex/strings/daslib_modules skills verified against
  the grammar; suspicious ones probe-compiled (all held up). Four
  shipped-but-undocumented features added: function/method arrow bodies
  (def f(...) : T => expr), call-site block arrow shorthand
  ($(a, b) => a < b), piped default-argument padding.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

AOT: iterating/passing/copying a native C-array field of a handled type emits non-compiling C++

2 participants