let a construct declare what may not cross it
The other half of a boundary: not what runs when something crosses, but what
may not cross at all. It was two string literals in vbd_is_restricted_name and
one #error in cg_fn — one prohibition, uneditable without a compiler release.
@decorator("prohibits_outside", "raw_sql")
fn repository() {}
fn sneaky() -> Int { raw_sql("DROP") }
// #error "boundary violation: raw_sql may only be called from an
// @repository fn, but 'sneaky' is not one"
The recursive matcher is parameterised through a state key rather than by
threading an argument through every branch of the walk — the mechanism codegen
already uses for __match_counter and __if_expr_counter. Each prohibition is
checked in its own turn, so the owning construct is known by construction and
the diagnostic names it instead of hardcoding one rule's wording.
PREDICTIONS AND RESULTS
1 the 3 duplicated uniqueness rules are textually identical TRUE
2 a declared prohibition reproduces @manager's #error TRUE
3 existing output byte-identical TRUE
4 a program can declare its own prohibition TRUE
5 fixpoint holds TRUE
I misread result 2 on first pass: a @manager fn calling dharma_emit still
emitted one #error, which looked like a failure. It is the CAPABILITY-tier rule
at codegen.el:2578, a separate prohibition system, and it fires identically on
the pre-change compiler.
MEASURED DEFECTS STILL OPEN
- two independent prohibition systems (VBD constructs, capability tiers);
only the first is declarable
- 3 uniqueness rules written 6 times, once per codegen path, kept in sync by
hand and identical today
102/102 native compiler tests pass, compiler self-hosts byte-identically.
This commit is contained in:
@@ -875,3 +875,31 @@ test "constructs-compose-guard-entry-exit" {
|
||||
assert b < x, "entry injection before exit injection"
|
||||
assert x >= 0, "three independent constructs compose on one fn"
|
||||
}
|
||||
|
||||
// ── Declared constructs: wraps and prohibitions ──────────────────────────────
|
||||
|
||||
test "declared-wrap-emits-closure-and-convention" {
|
||||
let src: String = "@decorator(\"wraps_body\", \"with_timeout\")\nfn timed() {}\n@timed\nfn slow(k: Int) -> Int { return 9 }"
|
||||
let out: String = compile_capture(src)
|
||||
assert str_contains(out, "struct __env_slow"), "captured environment is emitted"
|
||||
assert str_contains(out, "__thunk_slow(void* __v)"), "a thunk taking void* is emitted"
|
||||
assert str_contains(out, "extern el_val_t with_timeout(el_val_t, el_val_t, el_val_t(*)(void*), void*);"), "codegen emits the calling convention — El's single type cannot describe a callable"
|
||||
}
|
||||
|
||||
test "declared-prohibition-fires-outside-the-boundary" {
|
||||
let src: String = "@decorator(\"prohibits_outside\", \"raw_sql\")\nfn repository() {}\nfn sneaky() -> Int { raw_sql(\"DROP\") return 1 }"
|
||||
let out: String = compile_capture(src)
|
||||
assert str_contains(out, "raw_sql may only be called from an @repository fn"), "a program-declared prohibition is enforced"
|
||||
}
|
||||
|
||||
test "declared-prohibition-permits-inside-the-boundary" {
|
||||
let src: String = "@decorator(\"prohibits_outside\", \"raw_sql\")\nfn repository() {}\n@repository\nfn allowed() -> Int { raw_sql(\"SELECT\") return 1 }"
|
||||
let out: String = compile_capture(src)
|
||||
assert !str_contains(out, "raw_sql may only be called"), "the owning construct permits the call"
|
||||
}
|
||||
|
||||
test "seeded-vbd-prohibition-still-enforced" {
|
||||
let src: String = "fn leaky() -> Int { dharma_emit(\"x\", \"y\") return 1 }"
|
||||
let out: String = compile_capture(src)
|
||||
assert str_contains(out, "may only be called from an @manager fn"), "the compiled-in core prohibition survives being declared rather than branched"
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user