let a construct declare its own meaning instead of the emitter knowing it

codegen called fn_has_decorator for exactly three names — manager, accessor,
route. Twelve others parsed, attached as {name,args}, and compiled to nothing,
including four that look like protection: @authenticate (6 uses), @authorize
(3), @rate_limit (3), @validate (2). The cause was not that the branches were
untidy. A construct had nothing to BE, so its meaning had nowhere to live
except the emitter, and every construct was therefore a compiler edit.

A name -> injection table would have moved the enumeration twenty lines up
without removing it. So the construct now carries its own meaning:

    @decorator("injects_at_entry", "engram_boundary_beat")
    fn audited() {}

    @audited
    fn risky_op() -> Int { ... }   // gets the beat, attributed to "audited"

scan_declared_decorators is a token-level pre-pass beside scan_routes, forced
by streaming codegen having no whole-program AST. manager and accessor are
seeded as the compiled-in core — the fixedSelf shape from substrate.go: a
complete fallback exists, declaration is enrichment.

This is the injection half of the seam only. The prohibition half (@manager's
#error on dharma_emit) stays hardcoded, because "which calls may appear inside
this boundary" is a query over program structure and there is nothing yet to
ask.

Verified three ways: emitted C for existing @manager/@accessor code is
byte-identical to the hardcoded path; a construct with a name the compiler has
never heard of injects correctly; the compiler self-hosts byte-identically.
90/90 native compiler tests pass.
This commit is contained in:
bigmerge
2026-08-17 07:50:56 -05:00
parent dcaa77d77b
commit 5718943f2e
2 changed files with 168 additions and 5 deletions
+33
View File
@@ -772,3 +772,36 @@ test "decorator-authenticate-compiles-to-nothing" {
let bare: String = compile_capture("fn f() -> Int { return 1 }")
assert str_eq(out, bare), "KNOWN DEFECT: @authenticate emits identical C to no decorator at all"
}
// Declared constructs
//
// A construct declares its own meaning and codegen reads it. Adding a
// construct is a declaration in the program; it does not touch the compiler.
test "declared-construct-injects-without-compiler-knowledge" {
let src: String = "@decorator(\"injects_at_entry\", \"engram_boundary_beat\")\nfn audited() {}\n@audited\nfn risky() -> Int { return 7 }"
let out: String = compile_capture(src)
assert str_contains(out, "engram_boundary_beat(EL_STR(\"risky\")"), "a program-declared construct injects"
assert str_contains(out, "EL_STR(\"audited\")"), "the beat is attributed to the declared construct"
}
test "declared-construct-name-unknown-to-codegen" {
// The name is arbitrary. Nothing in the compiler mentions it.
let src: String = "@decorator(\"injects_at_entry\", \"engram_boundary_beat\")\nfn zzq_unlikely_name() {}\n@zzq_unlikely_name\nfn f() -> Int { return 1 }"
let out: String = compile_capture(src)
assert str_contains(out, "EL_STR(\"zzq_unlikely_name\")"), "an arbitrary construct name works"
}
test "undeclared-construct-still-injects-nothing" {
let src: String = "@nobody_declared_this\nfn f() -> Int { return 1 }"
let out: String = compile_capture(src)
assert !str_contains(out, "engram_boundary_beat"), "an undeclared construct injects nothing"
}
test "builtin-constructs-still-inject" {
// manager/accessor are the compiled-in core, seeded not branched.
let src: String = "@manager\nfn m() -> Int { return 1 }\n@accessor\nfn a() -> Int { return 2 }"
let out: String = compile_capture(src)
assert str_contains(out, "EL_STR(\"manager\")"), "seeded manager still injects"
assert str_contains(out, "EL_STR(\"accessor\")"), "seeded accessor still injects"
}