Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
6a6c16a
require [group] - a require whose name expands to every module regist…
borisbat Sep 8, 2026
489d479
require_module_now - a require issued after the prerequisite walk: fr…
borisbat Sep 8, 2026
8d138f0
the JIT takes a DLL cache hit without the emitter: llvm_jit_plan hold…
borisbat Sep 8, 2026
ba6184e
the comment harvest of the arc: the facts the added comments carried …
borisbat Sep 8, 2026
e305719
the groups get their consumers: daslib/module_group's call_module_gro…
borisbat Sep 8, 2026
df210f6
whether a program uses a function or a global, and the slot each hold…
borisbat Sep 8, 2026
641877d
the tune framework joins a group: dasLLVM registers llvm/daslib/llvm_…
borisbat Sep 8, 2026
ccb4114
the rebase onto the name-lookup master: the JIT codegen version steps…
borisbat Sep 8, 2026
82dda8d
the comment harvest of the group consumers, the per-program symbol ta…
borisbat Sep 8, 2026
2488867
the descriptor census skips register_module_group rows - a group name…
borisbat Sep 8, 2026
6d520eb
the grounding's findings: the dasLLVM descriptor joins its groups on …
borisbat Sep 8, 2026
e953b71
the review round's findings: a group member row carries a guard - reg…
borisbat Sep 9, 2026
338e02f
the second round's findings: a late require nested in a compile - a m…
borisbat Sep 9, 2026
8b68990
the rebase onto master's declared-name emitter: the JIT codegen versi…
borisbat Sep 9, 2026
543cbd4
the compile sweep leaves a standalone module's trees alone: a .daspkg…
borisbat Sep 9, 2026
6d4a817
the checklist edits the arc's code did not need go back to master's: …
borisbat Sep 9, 2026
1704129
a by-name call into another context refuses a name the context holds …
borisbat Sep 9, 2026
85e9fd2
the JIT's selection is the program's table, not a bit on the shared F…
borisbat Sep 9, 2026
baa1488
the JIT-entry doc's warm-run sentence stands on the mechanism alone; …
borisbat Sep 9, 2026
531eb39
the hello_jit example opts into the JIT the way -jit does - require d…
borisbat Sep 9, 2026
0e8ff05
cross_context loses the call_in_context wrappers: each was invoke_in_…
borisbat Sep 9, 2026
1dc8dce
the jit finalizer is one function again: the emitter-context branch t…
borisbat Sep 9, 2026
85fcd5f
the rebase over master's #3980 and #3981: the codegen version steps t…
borisbat Sep 9, 2026
b665658
the collector reads public after a group's closing bracket past any w…
borisbat Sep 9, 2026
f0787d9
the dasLLVM architecture doc stays at the 300-line cap with both comp…
borisbat Sep 9, 2026
d5e5945
the jit-selection builtins join the ast page's program-and-module-acc…
borisbat Sep 9, 2026
bc42586
the ambiguity arm asserts the refusal by the recover path and reads t…
borisbat Sep 9, 2026
6aa944b
a group member's module name drops the .das a relative require spelli…
borisbat Sep 9, 2026
d591029
a program a reader restores from a stream takes the reader's file acc…
borisbat Sep 9, 2026
31557aa
the late-require test's child commands no longer open with a quote - …
borisbat Sep 9, 2026
4400d49
dastest's isolated-mode line for a child that printed no result says …
borisbat Sep 9, 2026
51a3db6
the AOT-object globinit's extern visitor carries the program like the…
borisbat Sep 9, 2026
c09352d
call_module_group binds its once-evaluated arguments under names uniq…
borisbat Sep 9, 2026
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
2 changes: 1 addition & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -321,7 +321,7 @@ Most layout is obvious from `ls`. The non-obvious ones:
- `utils/detect-dupe/` (in-repo dupe finder) and `utils/find-dupe/` (Claude judge; needs `daspkg install --root utils/find-dupe` + `ANTHROPIC_API_KEY`) - both also MCP tools
- `utils/mcp/`, `utils/daslang-live/`, `utils/daspkg/` - in-tree tools (most also have skills)
- `tutorials/language/` (language tour) vs `tutorials/<area>/` (per-area) - never put tutorials in `modules/<X>/tutorial/`
- **Array/dim/vector indexing lives across 5 tiers** - bounds checks, neg-index detection and width-aware bounds usually need parallel edits in all: AOT C++ (`include/daScript/simulate/aot.h`), interpreter non-fused (`include/daScript/simulate/runtime_array.h` + `simulate_nodes.h`), interpreter fused (`src/simulate/simulate_fusion_at_array.cpp` + `simulate_fusion_at.cpp`), JIT (`modules/dasLLVM/daslib/llvm_jit.das`), AST const-fold (`src/ast/ast_simulate.cpp`). Debug builds bypass the fused permutations - a fused-path-only fix passes Release CI and trips Debug-ARM. Bump `LLVM_JIT_CODEGEN_VERSION` in `modules/dasLLVM/daslib/llvm_jit_run.das` after JIT emitter/ABI changes to invalidate cached `.dll`s (the cache hash also folds per-function AOT hashes, so AST-level changes self-invalidate)
- **Array/dim/vector indexing lives across 5 tiers** - bounds checks, neg-index detection and width-aware bounds usually need parallel edits in all: AOT C++ (`include/daScript/simulate/aot.h`), interpreter non-fused (`include/daScript/simulate/runtime_array.h` + `simulate_nodes.h`), interpreter fused (`src/simulate/simulate_fusion_at_array.cpp` + `simulate_fusion_at.cpp`), JIT (`modules/dasLLVM/daslib/llvm_jit.das`), AST const-fold (`src/ast/ast_simulate.cpp`). Debug builds bypass the fused permutations - a fused-path-only fix passes Release CI and trips Debug-ARM. Bump `LLVM_JIT_CODEGEN_VERSION` in `modules/dasLLVM/daslib/llvm_jit_plan.das` after JIT emitter/ABI changes to invalidate cached `.dll`s (the cache hash also folds per-function AOT hashes, so AST-level changes self-invalidate)

## MCP Server (AI Tool Integration)

Expand Down
30 changes: 22 additions & 8 deletions daslib/ARCHITECTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -232,15 +232,29 @@ Three companions carry a concern each; a section number is unique across all fou
share one macro context; `tune_status` and `log_tune_status` are shells of the same kind.
- **The shells decide with `typeinfo module_exists(llvm_tune)`, a pair with the trait's C++ half
in `src/ast/ast_infer_type.cpp` (`module_exists`): visible from the compiling module, which is
what the `require ?llvm llvm/daslib/llvm_tune public` line above them decided.** Nothing fails
what the `require [tune_framework] public` line above them decided.** Nothing fails
when the two sides drift - `tests/language/optional_require.das` pins the trait's visibility
half on fixtures, and `tests/daslib/test_tune_shells.das` is tier-agnostic by design - so the pair
is recorded here: a trait that answered off the process registry instead would take the
framework arm inside a tool's nested compile, where the framework is not mounted.
- **The `llvm/daslib/*` files resolve in every build that carries `modules/dasLLVM/`; only the
witness decides what `?llvm` and `module_exists(llvm_tune)` answer.** The framework's das files
compile without their C++ module, so a direct `require llvm/daslib/llvm_tune` in a build without
dasLLVM compiles with the shells inert and no `<name>_variants()` registry - a program that
reads one is framework-only and says so with that direct require. `daslib/just_in_time` keeps
its direct require for the opposite reason: a static host that never registered the witness
still runs the JIT through the LLVM library, and the guard would switch it off.
- **The `llvm/daslib/*` files resolve in every build that carries `modules/dasLLVM/`; only a
build configured with dasLLVM joins the `tune_framework` group with `llvm_tune` - the `llvm`
witness module's constructor for a static host, the descriptor's row guarded on `llvm` for a
DLL build - so the group, and with it `module_exists(llvm_tune)`, follows the configure and
never the das files on disk.** `llvm_code` is not a member: `llvm_tune` requires
it `public`, so every requirer of the group sees the `[llvm_code]` annotation through it, as
every requirer of `llvm_tune` always did. The framework's das files compile without their C++ module, so a direct
`require llvm/daslib/llvm_tune` in a build without dasLLVM compiles with the shells inert and no
`<name>_variants()` registry - a program that reads one is framework-only and says so with that
direct require. `daslib/just_in_time` keeps its direct require for the opposite reason: a static
host that never registered the witness still runs the JIT through the LLVM library, and the
group would switch it off.

## 38. cross_context

- **A function reached by name in the context `macro_context_of` answers is `[export]`ed, a pair
with `Program::markMacroSymbolUse` (`src/ast/ast_export.cpp`), whose roots are `exports`, `init`,
`shutdown` and `macroInit` - every other function is dropped from the macro context.** Nothing
fails when one side moves alone: the tests name an `[export]`ed function, so a narrower or wider
C++ root set passes them. The module itself is not the caller's dependency (`src/ast/ARCHITECTURE.md`
sec.3): its macro context is the caller's only handle on it.
8 changes: 4 additions & 4 deletions daslib/ARCHITECTURE_EMIT.md
Original file line number Diff line number Diff line change
Expand Up @@ -87,12 +87,12 @@ Companion to `ARCHITECTURE.md` in this folder; section numbers are unique across
runs it, collecting on its first tick.
- **There is no `!stopFlags` guard between `[init]` calls** - a panic propagates out of the
ctor instead of soft-stopping the sequence.
- **Global init order is fixed by `var->index` assignment**: `StandaloneContextGen`'s
- **Global init order is fixed by the program's variable index table**: `StandaloneContextGen`'s
`preVisitGlobalLet` emits required modules' globals via ordered `for_each_module`
before the adapter walks the entry module's own - correct only because
`ast_allocate_stack.cpp` assigns `var->index` through the same dependency-first
module order with the entry module last, and `runInitScript` executes ascending
index. `[init]` function order is not re-derived at all: the emitter reads the
`ast_allocate_stack.cpp` fills `Program::variableIndices` (read back as
`variable_index(program, var)`) through the same dependency-first module order with the
entry module last, and `runInitScript` executes ascending index. `[init]` function order is not re-derived at all: the emitter reads the
simulated context's list through rtti `for_each_init_function`, so the C++ late-init
sort stays the single source of truth.
- **The TU holds every used function of every module.** A standalone context is one
Expand Down
11 changes: 8 additions & 3 deletions daslib/ARCHITECTURE_LINQ.md
Original file line number Diff line number Diff line change
Expand Up @@ -178,9 +178,14 @@ Companion to `ARCHITECTURE.md` in this folder; section numbers are unique across
`linq_fold_xml`. `linq_fold_sql` is the exception - it recognizes a `[sql_table]` source
and hands the whole chain to the `_sql` macro, so it has no adapter and no emit. A source
file requires `linq_fold_common` and never a sibling source.
- **`try_splice_patterns` tries recognizers in a fixed order** - sql, decs, xml (behind the
pugixml `static_if`), json, table - and the array arm runs last with no recognizer of its
own: it is what claims a chain nobody else claimed.
- **`try_splice_patterns` tries recognizers in a fixed order** - sql, decs, the registered
`linq_fold_sources` in registration order, json, table - and the array arm runs last with
no recognizer of its own: it is what claims a chain nobody else claimed. A source outside
daslib joins the `linq_fold_source` group from its descriptor and from its C++ module's
constructor, and registers a
`LinqFoldSourceRecognizer` from its `register_linq_fold_source` entry, which
`register_all_linq_fold_rows` calls on every member; pugixml's `linq_fold_xml` is the one
such source.
- **The adapter contract is four abstract methods; everything else on the base has a
default.** `bind_name`, `element_type`, `wrap_source_loop` and `wrap_invoke` are abstract,
so every source answers them. The per-operation hooks (`emit_loop_or_count`,
Expand Down
12 changes: 12 additions & 0 deletions daslib/ARCHITECTURE_LINT.md
Original file line number Diff line number Diff line change
Expand Up @@ -234,3 +234,15 @@ Companion to `ARCHITECTURE.md` in this folder; section numbers are unique across
- **C++ mirror pairs**: the STYLE024/025 unsafe map <-> infer's `unsafe_*` rules;
`style036_inert_contract` <-> infer's contract clearing. Nothing fails when one side
moves alone.

### 4.1 STYLE029/030 over a require group {#style-lint-require-group}

- **The source line at a declaration's (file, line, column) key, read for the `[...]`
spelling after the keyword and its guard, is what tells a group apart from a require written
by hand.** A group's members share the group line's key, but a group with one member has one
declaration, exactly like a plain require, so the key alone cannot say; both rules judge
members and then keep, suppress and report per key.
- **A declaration's verdict is settled only after every member sharing its key has been
judged**, so `st030_report_requires` records verdicts in one walk and reports them in a
later one. Reporting inside the deciding walk would fire on a member judged ahead of the
used member that keeps the line.
18 changes: 12 additions & 6 deletions daslib/REVIEW.md
Original file line number Diff line number Diff line change
Expand Up @@ -40,13 +40,13 @@ a stack, never in a bare scalar.** A saved value kept in a scalar survives into
construct's exit path and unbalances it.

**A diff that adds or changes a daslib fact - code or a `//!` contract - whose truth is
decided by a C++-side definition, with no test, lint, or compile error failing when the two
sides no longer match, records the pair, naming both sides, in whichever `ARCHITECTURE*.md` in
this folder holds the daslib file's section - adding that section to `ARCHITECTURE.md` when no
doc holds one.**
decided by a definition outside this folder - a C++ definition, or another module's descriptor
- with no test, lint, or compile error failing when the two sides no longer match, records the
pair, naming both sides, in whichever `ARCHITECTURE*.md` in this folder holds the daslib file's
section - adding that section to `ARCHITECTURE.md` when no doc holds one.**

**When a diff changes one side of a recorded daslib/C++ pair so the two no longer match, it
changes the other side in the same diff.**
**When a diff changes one side of a recorded pair so the two no longer match, it changes the
other side in the same diff.**

**A diff that renames, deletes, or changes the behavior of a daslib name that an
`ARCHITECTURE*.md` in this folder spells updates that entry in the same change.** The doc is
Expand Down Expand Up @@ -246,6 +246,12 @@ signed sum wraps, the bounds test passes, and the walk leaves the array silently
and emit only the taken arm.** Nothing folds at macro-application time, so a generated
`if ($v(flag))` keeps its dead arm and type-checks it.

**A diff that adds a function to `cross_context.das` wrapping `invoke_in_context` - taking the
`unsafe` and the `addr` off the caller - is a defect; the caller writes the call itself.** The
wrapper's signature checks nothing the call does not: the target is found by name and argument
count, and a pointer crosses into a context that holds its lock for the whole run, so the
`unsafe` belongs at the site that hands the pointer over.

**A diff that adds or changes a buffer-I/O overload returns before taking `addr(buf[0])` on
an empty buffer.** The address is out of bounds and the call sits inside `unsafe`, so an
overload without the guard passes every non-empty test.
Expand Down
5 changes: 3 additions & 2 deletions daslib/REVIEW_LINQ.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,8 +71,9 @@ a projected adapter is a defect - skip the row instead.** The row's emit assumes
element, so running it on the un-projected adapter orders or dedups raw rows - a wrong result,
not a missed splice.

**A diff that reorders the `build_*_rows()` calls in `register_all_linq_fold_rows` without
naming the chains whose matching arm changes is a defect.** Rows land in `splice_patterns` in
**A diff that reorders the registration calls in `register_all_linq_fold_rows` - the
`build_*_rows()` calls, the group call that registers the sources - without naming the chains
whose matching arm changes is a defect.** Rows land in `splice_patterns` in
call order, the walker takes the first match, and that registrar is the only place the order
exists.

Expand Down
30 changes: 16 additions & 14 deletions daslib/aot_cpp.das
Original file line number Diff line number Diff line change
Expand Up @@ -626,6 +626,7 @@ class UseTypeMarker : AstVisitor {
useHandles : table<TypeAnnotation?>;
useModules : table<Module?>;
inUsedCode : bool = false;
program : ProgramPtr;

def override preVisitTypeDecl(typeDecl : TypeDeclPtr) {
}
Expand All @@ -635,7 +636,7 @@ class UseTypeMarker : AstVisitor {
}

def override preVisitFunction(fun : FunctionPtr) {
inUsedCode = fun.flags.used;
inUsedCode = is_used(program, fun);
}
def override visitFunction(var fun : FunctionPtr) : FunctionPtr {
inUsedCode = false;
Expand Down Expand Up @@ -666,7 +667,7 @@ class UseTypeMarker : AstVisitor {
mark(variable._type);
}
def override preVisitGlobalLetVariable(variable : Variable?; lastArg : bool) {
inUsedCode = variable.flags.used;
inUsedCode = is_used(program, variable);
mark(variable._type);
}
def override visitGlobalLetVariable(var variable : VariablePtr; lastArg : bool) : VariablePtr {
Expand Down Expand Up @@ -1571,7 +1572,7 @@ class public CppAot : AstVisitor {
}
def override preVisitGlobalLetVariable(variable : Variable?; lastArg : bool) {
write(*ss, tabs());
if (!variable.flags.used) {
if (!is_used(program, variable)) {
write(*ss, "/* ");
}
if (variable.flags.global_shared) {
Expand All @@ -1587,7 +1588,7 @@ class public CppAot : AstVisitor {
}
def override visitGlobalLetVariable(variable : VariablePtr; last : bool) {
write(*ss, ";");
if (!variable.flags.used) {
if (!is_used(program, variable)) {
write(*ss, " */");
}
write(*ss, "/*{variable.name}*/\n");
Expand Down Expand Up @@ -4211,7 +4212,7 @@ def private aotEmitStruct(ps : Structure?;
def public dumpDependencies(program : ProgramPtr; var aotVisitor : CppAot?; prune_to_used : bool = false) {
//! Writes forward declarations and dependent type/function definitions for AOT output.
buildStructEnumCollisions(program);
var utm = new UseTypeMarker();
var utm = new UseTypeMarker(program := program);
make_visitor(*utm) $(adapter) {
visit(program, adapter);
if (prune_to_used) { // a standalone context emits the foreign functions too
Expand Down Expand Up @@ -4327,13 +4328,13 @@ class ArgsConverter : AstVisitor {



def collectUsedFunctions(modules : array<Module?>; totalFunctions : int; this_module : Module?; all_modules : bool; is_all : bool = false) : array<Function?> {
def collectUsedFunctions(program : ProgramPtr; modules : array<Module?>; totalFunctions : int; this_module : Module?; all_modules : bool; is_all : bool = false) : array<Function?> {
var fnn : array<Function?>;
fnn.reserve(totalFunctions);
for (pm in modules) {
pm |> for_each_module_function($(pfun) {
if ((!all_modules && pfun._module != this_module)
|| pfun.index < 0 || !pfun.flags.used) return ;
|| function_index(program, pfun) < 0 || !is_used(program, pfun)) return ;
if (!is_all) {
if (pfun.flags.builtIn || pfun.flags.noAot) return ;
}
Expand All @@ -4352,7 +4353,7 @@ def public foreignUsedFunctions(program : ProgramPtr; before_no_aot_marking : bo
program.get_ptr() |> for_each_module_no_order($(pm) {
if (pm == thisModule) return
pm |> for_each_module_function($(pfun) {
if (pfun.index < 0 || !pfun.flags.used || pfun.flags.builtIn || pfun.moreFlags.isTemplate
if (function_index(program, pfun) < 0 || !is_used(program, pfun) || pfun.flags.builtIn || pfun.moreFlags.isTemplate
|| (!before_no_aot_marking && pfun.flags.noAot)) return
foreign |> push(pfun)
})
Expand All @@ -4366,7 +4367,7 @@ def public collectProgramUsedFunctions(program : ProgramPtr; all_modules : bool;
program.get_ptr() |> for_each_module_no_order($(mod) {
modules.push(mod);
})
return collectUsedFunctions(modules, program.totalFunctions, program.getThisModule, all_modules, is_all)
return collectUsedFunctions(program, modules, program.totalFunctions, program.getThisModule, all_modules, is_all)
}

def registerAotCpp(var logs : StringBuilderWriter?; program : ProgramPtr; var context : Context; cross_platform; headers, all_modules : bool = false) {
Expand Down Expand Up @@ -4435,14 +4436,15 @@ def public setAotHashes(program : ProgramPtr; ctx : Context) {
var fni = 0;
program.get_ptr() |> for_each_module_no_order($(mod) {
mod |> for_each_module_function($(pfun) {
if (pfun.index < 0 || !pfun.flags.used) return ;
pfun.hash = get_function_hash_by_id(pfun, pfun.index, unsafe(reinterpret<void?>(ctx)));
let pindex = function_index(program, pfun)
if (pindex < 0 || !is_used(program, pfun)) return ;
pfun.hash = get_function_hash_by_id(pfun, pindex, unsafe(reinterpret<void?>(ctx)));
fni++;
})
});
program.get_ptr() |> for_each_module_no_order($(mod) {
mod |> for_each_module_function($(pfun) {
if (pfun.index < 0 || !pfun.flags.used) return ;
if (function_index(program, pfun) < 0 || !is_used(program, pfun)) return ;
pfun.aotHash = get_function_aot_hash(pfun);
});
});
Expand Down Expand Up @@ -4481,12 +4483,12 @@ def private collectUsedModules(program : ProgramPtr) : table<Module?> {
var used : table<Module?>
program.get_ptr() |> for_each_module_no_order($(pm) {
pm |> for_each_module_function($(pfun) {
if (pfun.index >= 0 && pfun.flags.used && pfun._module != null) {
if (function_index(program, pfun) >= 0 && is_used(program, pfun) && pfun._module != null) {
used |> insert(pfun._module)
}
})
})
var utm = new UseTypeMarker()
var utm = new UseTypeMarker(program := program)
make_visitor(*utm) $(adapter) {
visit(program, adapter)
var foreign <- foreignUsedFunctions(program, false)
Expand Down
Loading
Loading