Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
871d349
-log-compile-time prints the process startup timeline (builtin module…
borisbat Sep 8, 2026
ac56703
a C++ module the scan replayed from a manifest loads at the first req…
borisbat Sep 8, 2026
612fb56
a require guard loads a waiting module - `require ?X x` and builtin_m…
borisbat Sep 8, 2026
055bbf5
the diff's added C++ comments settle into the documents: the loader-b…
borisbat Sep 8, 2026
0147b5d
a deferred module whose own dlopen fails brings every deferred module…
borisbat Sep 8, 2026
0e7d395
tests/module_cache shares one _common module for the spawn helpers th…
borisbat Sep 8, 2026
ca26aa8
the deferred-module rows and the loader belong to the scan: require_d…
borisbat Sep 8, 2026
e46a9e0
test_deferred_modules pins what the audit found unpinned: builtin_mod…
borisbat Sep 8, 2026
8e8552a
a parse no prerequisite walk precedes - the compile of a string - mee…
borisbat Sep 8, 2026
5a19cc2
ast_boost's printer names a block's expression and variable lists by …
borisbat Sep 8, 2026
f9cdc93
test_deferred_modules returns on a static host - the AOT test binary'…
borisbat Sep 8, 2026
5cf70ba
the LSP subtools spawn plain - neither validate nor nav enumerates th…
borisbat Sep 8, 2026
ef12ffa
the mcp/lsp exe ban is a utils/REVIEW.das check - the two folder chec…
borisbat Sep 8, 2026
fc733d5
the codegen-version bump has a duty where its out-of-folder triggers …
borisbat Sep 8, 2026
f9c0fe3
plans/review_md_splits.md ledgers the four checklist splits the audit…
borisbat Sep 8, 2026
abbc109
the manifest reader's two helpers - split_tabs and parse_on_error - s…
borisbat Sep 8, 2026
48a06ae
the deferred-module rows sit behind one recursive mutex across defer,…
borisbat Sep 8, 2026
e06a43f
Module::Initialize on a half-warm tree - one descriptor compiled cold…
borisbat Sep 8, 2026
7445b33
load_deferred_dynamic_module answers false for a null name, as the de…
borisbat Sep 8, 2026
7aa624a
the deserializer fetches a builtin module the way the parser's requir…
borisbat Sep 8, 2026
8a2a4b9
the half-warm arm's copies bring the libraries beside the artifacts a…
borisbat Sep 8, 2026
de5d19c
the half-warm arm reads its premise off the half-warm start too - whe…
borisbat Sep 8, 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
4 changes: 2 additions & 2 deletions daslib/ast_boost.das
Original file line number Diff line number Diff line change
Expand Up @@ -1302,7 +1302,7 @@ def private debug_expression_impl(var writer : StringBuilderWriter; expr : Expre
unsafe {
p8 = (reinterpret<int8?>(expr)) + int(offset)
}
if (tstr == "$::dasvector`ptr`Expression") {
if (tstr |> ends_with("::dasvector`ptr`Expression")) {
let pv = unsafe(reinterpret<dasvector`ptr`Expression?>(p8))
if (!empty(*pv)) {
let ts = repeat(" ", tabs + 2)
Expand All @@ -1316,7 +1316,7 @@ def private debug_expression_impl(var writer : StringBuilderWriter; expr : Expre
}
writer |> write("]")
}
} elif (tstr == "$::dasvector`ptr`Variable") {
} elif (tstr |> ends_with("::dasvector`ptr`Variable")) {
let pv = unsafe(reinterpret<dasvector`ptr`Variable?>(p8))
if (!empty(*pv)) {
let ts = repeat(" ", tabs + 2)
Expand Down
4 changes: 2 additions & 2 deletions doc/REVIEW.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ reader sends that host and whether the host sets cookies.**

**A diff that adds, to any text that reaches a built page - an authored `.rst` under `source`,
a stub under `source/stdlib/handmade`, or page text a `reflections` generator writes - anything
a reader is told to fetch, run, or type - an address, a file, a command, a flag, an API a
`.das_package` manifest calls - states, in the PR body, that each exists and works at merge, and
a reader is told to fetch, run, or type - an address, a file, a command, a flag, an API, a name
a `.das_package` manifest resolves - states, in the PR body, that each exists and works at merge, and
where that was checked** - the build proves the page renders, never that what it points a
reader at is there.
Original file line number Diff line number Diff line change
@@ -1 +1 @@
Returns ``true`` if a module with the given name is registered, ``false`` otherwise.
Returns ``true`` if a module with the given name is registered, or waits in a ``.das_module.manifest`` row for the first ``require`` that names it, ``false`` otherwise. Unlike ``typeinfo builtin_module_exists(mod)``, which takes a bare module name and loads a waiting module, it loads nothing.
11 changes: 11 additions & 0 deletions include/daScript/ast/REVIEW.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# AST Headers Code Review Checklist

**Read `REVIEW_COMMON.md` (repo root) first - its contract binds this checklist.** Architecture doc:
`src/ast/ARCHITECTURE.md` (repo root).

- **A diff that changes which module a bind registers into, or the name it registers under -
`vectorHomeModule`, `typeFactory<vector<TT>>::make`, `registerVectorFunctions`, the name a
`ManagedVectorAnnotation` or `ManagedStructureAnnotation` takes (`ast_handle.h`) - bumps
`LLVM_JIT_CODEGEN_VERSION` in `modules/dasLLVM/daslib/llvm_jit_run.das` (repo root), in the
same change.** The JIT's DLL cache key folds the codegen version and each function's AST hash,
never the module an extern lives in, so a cached DLL binds the old name and crashes on the hit.
7 changes: 7 additions & 0 deletions include/daScript/ast/ast.h
Original file line number Diff line number Diff line change
Expand Up @@ -1106,6 +1106,12 @@ namespace das

DAS_API bool isValidBuiltinName ( const string & name, bool canPunkt = false );

// src/ast/ARCHITECTURE.md sec.2
typedef bool (*DeferredModuleLoader) ( const string & name );
DAS_API void setDeferredModuleLoader ( DeferredModuleLoader loader );
DAS_API DeferredModuleLoader getDeferredModuleLoader ();
DAS_API bool guardModuleAvailable ( const string & name );

class DAS_API Module {
public:
Module ( const string & n = "" );
Expand Down Expand Up @@ -1154,6 +1160,7 @@ namespace das
static Module * require ( const string & name );
static Module * requireEx ( const string & name, bool allowPromoted, const string & requireName = string(), const string & expectedFileName = string() );
static void Initialize();
static bool InitializeDependencies ( string & notInitialized );
static void CollectFileInfo(das::vector<FileInfoPtr> &accesses);
static void Shutdown( bool dumpHandleLeaks = true );
// Runtime-only shutdown — for standalone exes built with `daslang -exe`,
Expand Down
10 changes: 9 additions & 1 deletion include/daScript/ast/ast_handle.h
Original file line number Diff line number Diff line change
Expand Up @@ -748,6 +748,14 @@ namespace das
}
};

// src/ast/ARCHITECTURE.md sec.2
__forceinline Module * vectorHomeModule ( const TypeDeclPtr & elem, const ModuleLibrary & library ) {
auto t = elem;
while ( t && t->isPointer() && t->firstType ) t = t->firstType;
if ( t && t->isHandle() && t->annotation && t->annotation->module ) return t->annotation->module;
return library.front();
}

template <typename TT>
struct typeFactory<vector<TT>> {
using VT = vector<TT>;
Expand All @@ -759,7 +767,7 @@ namespace das
ann->cppName = "das::vector<" + describeCppType(declT, CpptSubstitureRef::no,
CpptSkipRef::no, CpptSkipConst::no,
CpptRedundantConst::yes, ChooseSmartPtr::yes) + ">";
auto mod = library.front();
auto mod = vectorHomeModule(declT, library);
mod->addAnnotation(ann);
registerVectorFunctions<vector<TT>>::init(mod,library,
declT->canCopy(),
Expand Down
2 changes: 1 addition & 1 deletion include/daScript/ast/ast_serializer.h
Original file line number Diff line number Diff line change
Expand Up @@ -258,7 +258,7 @@ namespace das {
AstSerializer & serializeModule ( Module & module, bool already_exists );

static constexpr uint32_t getVersion () {
return 204; // 204: the record header stamps the source by content hash, not mtime; the policy stream carries every CodeOfPolicies field
return 205; // 205: a vector of a handled element streams under the element's module (204: the record header stamps the source by content hash; the policy stream carries every CodeOfPolicies field)
}

void serializeProgram ( ProgramPtr program, ModuleGroup & libGroup ) noexcept;
Expand Down
6 changes: 6 additions & 0 deletions include/daScript/ast/dyn_modules.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,4 +40,10 @@ DAS_API void begin_dynamic_module_recording();
DAS_API void end_dynamic_module_recording(vector<DynModuleManifestRow> & rows, bool & optOut);
DAS_API void replay_native_path(const char * mod_name, const char * src, const char * dst);
DAS_API void replay_dynamic_module(const char * path, const char * cpp_class, int on_error);
DAS_API void defer_dynamic_module(const char * path, const char * cpp_class, int on_error, const char * das_name);
DAS_API bool load_deferred_dynamic_module(const char * das_name);
DAS_API size_t load_all_deferred_dynamic_modules(); // the count it attempted
DAS_API bool is_dynamic_module_deferred(const char * das_name);
DAS_API void clear_deferred_dynamic_modules(); // the rows are the scan's: cleared at scan start and at shutdown
DAS_CC_API void ignore_dynamic_module_manifests(bool ignore);
}
11 changes: 7 additions & 4 deletions modules/dasLLVM/REVIEW.md
Original file line number Diff line number Diff line change
Expand Up @@ -36,10 +36,13 @@
Option resolution before the first timer, and log lines, are not work.

- **A change that can alter the machine code the JIT's DLL or split-obj cache serves back for
identical inputs bumps `LLVM_JIT_CODEGEN_VERSION`** (`daslib/llvm_jit_run.das`; what counts
as emitting: `ARCHITECTURE.md` sec.1.2). The constant folds only into those two keys;
selecting among existing generators' `[llvm_code]` arguments - the `[tune]` stamping - is not
such a change, because stamped arguments fold into the cache keys per function.
identical inputs - IR generation, target-machine setup, a `[llvm_code]` generator body, or the
call ABI the generated code binds: function signatures, the name scheme, the prologue, the
externs the install phase binds - bumps `LLVM_JIT_CODEGEN_VERSION`**
(`daslib/llvm_jit_run.das`); selecting among existing generators' `[llvm_code]` arguments, the
`[tune]` stamping, is not such a change. The caches are addressed by the AST hashes and this
constant, so such a change without the bump serves the old machine code back
(`ARCHITECTURE.md` sec.1.2).

- **A diff that adds an environment or config input to a JIT cache key folds it inside
`jit_env_salt` (`daslib/llvm_jit_run.das`), never directly into either JIT key - the DLL
Expand Down
2 changes: 1 addition & 1 deletion modules/dasLLVM/daslib/llvm_jit_run.das
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ var LINK_WHOLE_LIB = false // when true, standalone exe links against the whole
// invalidates cached DLLs (e.g. edits to llvm_jit.das, llvm_macro.das, llvm_jit_common.das,
// runtime helper ABI, default target triple). Cache filenames fold this in, so a bump
// makes every previously written DLL miss the cache on the next run and get GC'd.
let LLVM_JIT_CODEGEN_VERSION : uint64 = 0x76ul // 0x76: a statement after a terminator in the same block list lands in its own dead block instead of after the ret (0x75: the global-offset lookup is memory(none) and emitted at its use site - LLVM dedups and hoists it, an untaken branch never pays it; a solid-context global resolves once per function at entry (0x74: a runtime-only exe emits no register_native_path rows, and a whole-lib exe emits them once (0x73: computed goto lowers to one switch with the trap as its default, not an icmp chain (0x72: policies.fast_math defaults to the host's float flags, so a fast-math host now JITs fast-math (0x71: a CPU class row's cpu is the arch's bare baseline, so a DAS_JIT_BASELINE build enables the row's set and nothing a level implies (0x70: the wasm feature string drops +relaxed-simd and the idot family keeps only the exact extmul + extadd_pairwise lowering on wasm SIMD128 (0x6f: the first wasm idot lowering; 0x6e: the aarch64 SDOT / SMMLA tables gate on DotProd / i8mm, not the arch alone, and the force env reaches the generic exe machine (0x6d: the inline polynomial rail carries NaN: tanh selects the operand back over its ordered clamp, and the sincos quadrant / tan octant convert through llvm.fptosi.sat instead of poisoning on NaN and out-of-range (0x6c: aarch64 vector tan/exp2/log2/log/pow join the inline polynomial rail bit-exactly with the interpreter, sinh/cosh/tanh ride the exp one; 0x6b: aarch64 vector sin/cos ride the inline polynomial; 0x6a: srem/urem for 32-bit %; 0x69: every string argument of an extern is substituted, not just the ones which asked)
let LLVM_JIT_CODEGEN_VERSION : uint64 = 0x77ul // 0x77: a vector of a handled element type registers into the element's module, so the externs a DLL binds by mangled name moved out of `$` (0x76: a statement after a terminator in the same block list lands in its own dead block instead of after the ret (0x75: the global-offset lookup is memory(none) and emitted at its use site - LLVM dedups and hoists it, an untaken branch never pays it; a solid-context global resolves once per function at entry (0x74: a runtime-only exe emits no register_native_path rows, and a whole-lib exe emits them once (0x73: computed goto lowers to one switch with the trap as its default, not an icmp chain (0x72: policies.fast_math defaults to the host's float flags, so a fast-math host now JITs fast-math (0x71: a CPU class row's cpu is the arch's bare baseline, so a DAS_JIT_BASELINE build enables the row's set and nothing a level implies (0x70: the wasm feature string drops +relaxed-simd and the idot family keeps only the exact extmul + extadd_pairwise lowering on wasm SIMD128 (0x6f: the first wasm idot lowering; 0x6e: the aarch64 SDOT / SMMLA tables gate on DotProd / i8mm, not the arch alone, and the force env reaches the generic exe machine (0x6d: the inline polynomial rail carries NaN: tanh selects the operand back over its ordered clamp, and the sincos quadrant / tan octant convert through llvm.fptosi.sat instead of poisoning on NaN and out-of-range (0x6c: aarch64 vector tan/exp2/log2/log/pow join the inline polynomial rail bit-exactly with the interpreter, sinh/cosh/tanh ride the exp one; 0x6b: aarch64 vector sin/cos ride the inline polynomial; 0x6a: srem/urem for 32-bit %; 0x69: every string argument of an extern is substituted, not just the ones which asked))

// Read by tests-cpp/small/test_jit_emitter_pin.cpp: FNV-1a64 of the emitter sources
// (normalized to LF; file list in the test)
Expand Down
28 changes: 28 additions & 0 deletions plans/review_md_splits.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
# Checklist splits owed - a docs-only PR at the end of the startup chain

Four rules the per-PR audits found working but hard to apply: each fuses two checks, or takes
the wrong subject. Splitting is a restructuring, so it rides its own docs-only PR (those merge
gate-free) rather than a code PR. Meaning-preserving; the dragon reads the result.

- `utils/REVIEW.md` - the three CI-row rules take "an arm" as subject while the definition
above them says one arm may hold both assertions a CI row can run and assertions none can,
so a mixed arm fits neither rule. Make the assertion set the subject: "the load-bearing
assertions a CI row can run ship with a row that executes them on every pull request; the
ones no CI row can run ship with a compile-check row and a recorded run". Then split each
60-word rule into a trigger sentence and a duty sentence, and drop "one arm may hold both",
which follows.
- `doc/REVIEW.md` - the fetch/run/type rule's subject and verb sit 45 words apart in two nested
dash lists. Split into the duty ("A diff that adds anything a reader is told to fetch, run,
or type to text that reaches a built page states, in the PR body, that each exists and works
at merge, and where that was checked"), then the scope sentence naming the three page kinds
and the `.. include::` arm (a diff touching only an included `.md` never surfaces this
checklist in the folder walk, so that arm needs a routing line in the opening), then the WHY.
- `modules/dasLLVM/REVIEW.md` - the module-cache flag rule fuses two checks (a child asserting
a compile-time macro line spawns with `-no-module-cache`; a test whose subject is the cache
pins its own file with `-module-cache <temp>`) behind a five-clause qualifier and a five-line
mechanism tail. One rule per check; the replay mechanism moves to `ARCHITECTURE.md`. Same
shape, weaker: the intrinsic emitter reference-cell rule fuses three obligations.
- `doc/source/stdlib/handmade/REVIEW.md` - the type-file format rule (one type-description
line, one line per member, declaration order) carries a placement duty in a subordinate
clause (a property's description is its own `function-` file). Two rules; drop "handmade
file in this folder" from the head, which the folder walk already scopes.
16 changes: 14 additions & 2 deletions skills/dynamic_modules.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,8 +100,20 @@ of the machine, a variant picked by hardware - opts out by calling `no_manifest(
`initialize`; it then runs on every start. Everything a descriptor registers is replayed, so
the opt-out is only for a descriptor whose answer changes between starts.

`DAS_TRACE_MODULE_LOAD=1` prints one line per descriptor saying whether it was replayed or
compiled, and why.
A replayed C++ module loads at the first `require` naming it, not in the scan: the require
walk loads the row and runs the `initDependencies` fixed point; a module needing another
deferred one pulls the whole deferred set in. A program pays for the C++ modules it requires.
Two consequences:

- `require ?mod x` and `typeinfo builtin_module_exists(mod)` still ask whether the build has
`mod`: a guard loads a waiting module, so a cold start and a warm one answer alike.
- A tool that enumerates the process's modules (the MCP server) runs with
`-ignore-manifest`: no manifest read or written, every descriptor compiles, every C++ module
loads on start. `has_module(name)` (`daslib/rtti`) answers loaded-or-deferred, so a sweep
gate asking what the tree has keeps its answer.

`DAS_TRACE_MODULE_LOAD=1` prints one line per descriptor (replayed or compiled, why, the
deferred count) and one per deferred module as a require loads it.

## Adding a `.das` file to an existing module needs the same edit

Expand Down
2 changes: 1 addition & 1 deletion skills/internal/environment_variables.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ and unknown codes are harmless. The `-no-lint` command-line flag skips the lint
|---|---|---|
| `DAS_GC_STAGE_REPORT` | flag | Report gc_node deltas per compilation stage - the first thing to reach for on a `GC APP LEAK` at exit. |
| `DAS_GC_BREAK_ON_ID` | number | Break when the gc_node with this id is allocated. Pair it with the id from a leak report. |
| `DAS_TRACE_MODULE_LOAD` | flag | Log every module as it loads, with its resolved path - the fastest way to see which of two same-named modules actually won - and one line per `.das_module` descriptor saying whether the scan replayed its manifest or compiled it, and why (`src/ast/ARCHITECTURE.md` sec.2). |
| `DAS_TRACE_MODULE_LOAD` | flag | Log every module as it loads, with its resolved path - the fastest way to see which of two same-named modules actually won - and one line per `.das_module` descriptor: replayed (with its time, shared-module load share and deferred count) or compiled, and why; a deferred module's load prints `require <name>: loading the deferred <class>` (`src/ast/ARCHITECTURE.md` sec.2). |

## Ambient variables daslang reads but does not own

Expand Down
2 changes: 1 addition & 1 deletion skills/mcp_tools.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ The daslang MCP server (`utils/mcp/main.das`) exposes compiler diagnostics, prog

**`shutdown` tool.** Shuts down the MCP server process. Claude Code auto-restarts it, picking up code changes to `.das` tool files. Tool registration changes (adding/removing tools) still require a manual MCP restart.

**Configuration.** Configure `.mcp.json` with `"command"` pointing at the daslang binary (`bin/daslang` on Windows MSVC, `build/daslang` on Linux/macOS, `bin/daslang` for the installed SDK), `"args": ["utils/mcp/main.das"]`. See `utils/mcp/README.md` for details and Claude Code permissions.
**Configuration.** Configure `.mcp.json` with `"command"` pointing at the daslang binary (`bin/daslang` on Windows MSVC, `build/daslang` on Linux/macOS, `bin/daslang` for the installed SDK), `"args": ["-ignore-manifest", "utils/mcp/main.das"]` (the flag loads every C++ module on start; the server enumerates them). See `utils/mcp/README.md` for details and Claude Code permissions.

**Fresh checkouts / worktrees.** `.mcp.json`, `sgconfig.yml`, `bin/`, and the tree-sitter grammar lib are all gitignored, so a new `git worktree add` (or clone) has no daslang MCP at all. Bootstrap it with `daslang utils/mcp/setup.das -- --root <worktree>` - it configures `build/` on the cmake generator of the tree running the setup (platform default when that tree has no `build/CMakeCache.txt`), builds a worktree-local binary (+ grammar), copies the platform `sgconfig.yml`, and merges a `daslang` entry into `.mcp.json` (adds no new secrets; existing servers, including any secret env blocks, are preserved as-is). `--no-build` skips the build. Restart the session to pick it up.

Expand Down
Loading
Loading