Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 15 additions & 2 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,9 @@ Multiple skill files may apply to a single task. For example, creating a new das

When you discover something new about daslang syntax, semantics, or conventions — whether through compiler errors, user corrections, or experimentation — **update this file** with the new knowledge. If it relates to a specific skill area, update the relevant `skills/*.md` file instead.

**Doc improvements at stopping points.** When a task wraps and you spot a typo or factual error in CLAUDE.md or `skills/*.md` — fix it in-place and flag the edit in the end-of-turn summary. Anything more — clarifications, additions, restructuring, removing existing guidance, **or proposing a new skill file when you see a recurring pattern that no existing skill covers** — propose first. Default toward propose-first; doc edits direct future Claude behavior and silent diffs are not OK.
**Syntax and factual corrections are fix-in-place, always.** If a compiler error, probe, or user correction shows that a claim in CLAUDE.md or `skills/*.md` is wrong, incomplete, or stale, fix it in the same session and flag the edit in the end-of-turn summary — never defer it to a proposal. Verify the corrected claim before writing it (grammar truth is `src/parser/ds2_parser.ypp`; behavior truth is a probe-compile with the current binary).

**Doc improvements at stopping points.** Propose-first applies only to what's left: restructuring, removing existing guidance, **or proposing a new skill file when you see a recurring pattern that no existing skill covers**. Doc edits direct future Claude behavior, so structural diffs still get review — but factual drift must be self-healing, not queued behind it.

## daslang Language — Gen2 Syntax (REQUIRED)

Expand All @@ -106,8 +108,9 @@ All code MUST use gen2 syntax (add `options gen2` at the top of every file). Key
- **Table literals:** `{ "k" => v, "k2" => v2 }` — NOT `{{ "k" => v; "k2" => v2 }}`
- **Bare blocks:** `{ var x = 1; ... }` at statement level creates a lexical scope (NOT a table literal). Supports `finally`: `{ ... } finally { ... }`
- **Named arguments:** `foo([name = value])` with square brackets
- **Block arguments:** block/lambda after `func()` pipes as last arg. No `$` for parameterless blocks: `defer() { ... }`. With params: `build_string() $(var writer) { ... }`. Lambdas: `emplace() @(x : int) { ... }`
- **Block arguments:** block/lambda after `func()` pipes as last arg. No `$` for parameterless blocks: `defer() { ... }`. With params: `build_string() $(var writer) { ... }`. Lambdas: `emplace() @(x : int) { ... }`. **Arrow shorthand for single-expression blocks:** `arr |> sort() $(a, b) => a < b`. Defaulted parameters sitting between the explicit args and a trailing block are padded automatically — don't spell them out
- **Lambda:** `@(args) { body }` or `@@(args) { body }` (no-capture). **Inline arrow form:** `@(x) => expr` (capture lambda) and `@@(x) => expr` (no-capture function pointer) — preferred for short transforms passed as arguments: `sometimes(pat, @@(x) => fast(x, 2.0lf))`
- **Function/method arrow body:** `def add(a, b : int) : int => a + b` — single-expression body, return type optional (`def add(a, b : int) => a + b` infers). Works on class methods too: `def get() : int => count + 2`
- **Generator:** `$() { yield value; }` or `$ { yield value; }`
- **Tuple `=>`:** `a => b` creates `tuple<auto;auto>`
- **`typeinfo`:** `typeinfo trait_name(type<T>)` — trait name outside parens
Expand All @@ -123,6 +126,16 @@ All code MUST use gen2 syntax (add `options gen2` at the top of every file). Key
- **Function pointer with explicit type:** `@@<(var self : T) : RetT> funcName` — specifies the exact parameter/return types of a function pointer literal
- **OR types in params** (`T1 | T2 | …`) — a parameter may list alternative accepted types: `int | float | double`, or heterogeneous forms like `array<int> | table<auto> | auto(NT)`. This is a **generic "OR" type, NOT a runtime tagged variant** — the function is monomorphized per the concrete argument type that matches one alternative, so each instantiation sees that concrete type with no per-call dispatch or unpacking cost. Don't "hoist the union cast out of the loop" — there is no union value; a cast like `float(n)` inside the body is just a trivial concrete cast in each instantiation. Use it to widen an overload set in one signature (e.g. `def fast(n : int | float | double)` accepting bare ints, floats, and doubles at the same call site)

### Fixed arrays (structural since 0.6.3)

`int[10]` is a **structural type** (`Type::tFixedArray`), not a qualifier vector on the element: one node per dimension, element in `firstType`, size in `fixedDim`, outermost first (`int[3][4]` = FA(3, FA(4, int))); ref/const/temporary live on the chain head. Operations act on the **outermost level only** (the one-peel rule).

- **Generic binding:** `auto(TT)` binds the WHOLE array (`TT = int[3][4]`); `auto(TT)[]` peels one level (`TT = int[4]`, parameter constness inherited); `TT - []` in a return/alias removes ONE level — pre-0.6.3 docs saying "removes all dims" describe the deleted flattened world
- **`safe_addr(arr)` on a fixed array returns a pointer to the FIRST ELEMENT** (C-style decay; multi-dim peels one level: `int[2][3]` → `int[3]?#`) — this is what makes `glGetBooleanv(what, safe_addr(flags4))`-style C interop work
- **Typedefs compose:** `typedef M4 = float[4]` then `M4[10]` is `float[10][4]` — array-ness survives aliasing and generic substitution (the pre-0.6.3 flattening bugs are gone)
- **Runtime `TypeInfo` is still flattened** (`dim[]`/`dimSize`) — only the AST is structural. C++ `TypeDecl::isArray()` means "is a fixed array" in both pre- and post-rework daslang (useful for external modules spanning versions)
- Macro authors: das-side `TypeDecl` fields are `fixedDim`/`fixedDimExpr` (NOT `dim`/`dimExpr` — deleted); typemacro payloads live in `typeMacroExpr`; build chains with `make_fixed_array_type(total, element)` from `daslib/ast_boost` — details in `skills/das_macros.md`

### Important defaults

- No implicit type promotion: `int + float` is a compile error — both sides must match
Expand Down
72 changes: 72 additions & 0 deletions COVERAGE_GAP.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
# Closing the local-vs-CI coverage gap

Follow-up to the fixed-array rework (PR #3095). Every CI failure during that
PR's babysitting was an **oracle mismatch** — a gate CI enforces that no local
step mirrored — not a wrong change. This plan closes those gaps at four levels.
Comment thread
borisbat marked this conversation as resolved.
Process: per-stage plan → implement → review, same as FIXED_ARRAY_REWORK.md.

## Evidence base (from the #3095 session, 2026-06-11)

| Incident | Gap |
|---|---|
| doc.yml failed twice: das2rst positional handmade-doc validation, then the `Uncategorized` grep | Only 2 of doc.yml's 6 gates were mirrored locally; CI stops at the FIRST das2rst panic, hiding the rest |
| doctest `CHECK` on TypeDecl bit-fields: MSVC tolerates, clang/gcc reject — 15 lanes red | No clang-family compile of tests-cpp before push; a syntax-only pass would catch it in seconds |
| `safe_addr(bool[4])` decay regression hid in `modules/dasOpenGL/opengl/*.das` | GLFW-gated das code is compiled by NOTHING local — only extended_checks' sequence smoke |
| extended_checks installs external dasImgui from ITS master → `no member named 'dim'` | ABI breaks vs external repos invisible until CI; resolved pre-merge only via the both-worlds `isArray()` spelling |
| (Not hit, same family) Debug lanes bypass fused interpreter permutations | Release-only local testing misses Debug-divergent paths |

## Stage 0 — language-doc currency (CLAUDE.md + skills)

- Sharpen the "Updating Instructions" rule: syntax/factual corrections are
**fix-in-place, always** (flag in end-of-turn summary); propose-first stays
for restructuring, removals, new skill files.
- Fixed-array sweep: purge stale `dim`/`dimExpr` guidance (das_macros.md,
macro examples, typemacro payload references → `typeMacroExpr`); document
the structural model where macro authors look (element in `firstType`, one
size per node in `fixedDim`, quals on the chain head, one-peel rule,
`make_fixed_array_type`); document the new user-facing semantics
(`auto(TT)` whole-bind, `auto(TT)[]` peel + const inheritance, one-level
`-[]`, `safe_addr` element-pointer decay).
- Gen2 currency review: extract every syntax claim from CLAUDE.md's gen2
section + syntax-adjacent skills; verify each against `ds2_parser.ypp` AND a
probe-compile with the current binary; fix stale claims in place. Probe
corpus kept (re-runnable after future grammar changes).

## Stage 1 — process docs (shared, in-repo)

- `skills/preflight.md`: CI-lane ↔ local-mirror table for every lane in
build.yml / extended_checks.yml / doc.yml — exact local command, or an
honest "not mirrorable, see skills/wsl_ci_repro.md".
- `skills/make_pr.md`: step-4 trigger gains "removed fields / new enum
values" (das2rst validates positionally); gate list gains the Uncategorized
grep, the latex sphinx build, and a "type-system / daslib-generics change →
sequence smoke + externals sweep" trigger.
- New `skills/abi_break_sweep.md`: externals checklist — grep patterns,
both-worlds spellings (prefer a predicate that exists with the same meaning
in both worlds over feature macros — the `isArray()` precedent), junction
rebuild order, stale-`.shared_module` startup trap, daspkg-index as scope.

## Stage 2 — tools

- `utils/preflight/` (das, clargs): one command, tiered. `--fast` = format +
lint + clang syntax-only pass on changed C++. `--full` adds interp/AOT/JIT
suites, tests-cpp, all six doc gates, sequence smoke, CI-only-das compile
sweep. Parallelize on big machines.
- clang syntax-only checker for changed `.cpp`/`.h` (`clang-cl /Zs` with
project includes) — the 80/20 for the MSVC-vs-clang diagnostic gap.
- Docs-gate runner mirroring doc.yml steps 1–6 exactly.
- CI-only das surface list (dasOpenGL, sequence, release tooling …) compiled
via `compile_check` with proper mounts.

## Stage 3 — local build matrix

- clang-cl CMake preset in a gitignored alt build dir, wired into
`preflight --full` (compile-focused; full builds affordable but rarely needed).
- mingw preset: deferred unless a mingw-specific failure class appears.
- Debug-config build target for the fused-path divergence family.

## Stage 4 — CI

- Nightly cron on daspkg-index building every index package against daslang
master — external ABI breakage surfaces as a nightly signal instead of
inside an unrelated PR's extended_checks.
Loading
Loading