c741cfe928
I said prohibition could not move because "a #error has no runtime". That
conflated two separable things: WHEN a violation is detected (build time --
correct, and unchanged) and WHERE the rule and the checker live (the compiler
-- assumed).
A prohibition is a containment relation over the call graph. So codegen now
records what it saw:
sneaky calls raw_sql
allowed calls raw_sql
allowed calls @repository
repository calls prohibits:raw_sql
and tools/check/prohibitions.sh decides, at build time, outside the compiler.
PREDICTIONS AND RESULTS
P1 codegen can emit the call graph it already walks TRUE
P2 the check becomes a query outside the compiler TRUE
P3 all prohibition decisions leave codegen TRUE zero #errors now
P4 violations still caught at build time TRUE exit=1
P5 codegen drops below the 4661 baseline FALSE 4962, +301
P5 is the finding. The TRAVERSAL is irreducible -- you must walk the AST to
find calls, and those ~120 lines do not move no matter who decides. What is not
irreducible is the rule (which names) or the decision (#error). Those left. I
predicted the whole 223 lines would go because I had not separated walking from
adjudicating.
Still compiled, and measured rather than assumed: the capability-tier system
(cap_check_call, is_self_formation_call, is_dharma_call, is_llm_call,
cap_record_violation, emit_cap_violations) is 76 lines of the same shape --
prohibits_WITHIN rather than prohibits_outside, so the checker needs the
opposite polarity to absorb it.
98/98 native, 4/4 prohibition_query.sh, 7/7 seam_binding.sh, fixpoint ok.
39 lines
1.4 KiB
Bash
Executable File
39 lines
1.4 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Control for prohibition-as-query: the compiler records, the checker decides.
|
|
set -uo pipefail
|
|
ELC="${1:?usage: prohibition_query.sh <elc>}"
|
|
LANG_DIR="${2:-$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)}"
|
|
W=$(mktemp -d); trap 'rm -rf "$W"' EXIT; F=0
|
|
chk(){ [ "$2" = "$3" ] && printf ' ok %s\n' "$1" || { printf ' FAIL %s\n expected %s got %s\n' "$1" "$2" "$3"; F=$((F+1)); }; }
|
|
|
|
cat > "$W/p.el" <<'EOF'
|
|
@decorator("prohibits_outside", "raw_sql")
|
|
fn repository() {}
|
|
|
|
fn sneaky() -> Int { raw_sql("DROP") return 1 }
|
|
|
|
@repository
|
|
fn allowed() -> Int { raw_sql("SELECT") return 2 }
|
|
|
|
fn main() { println("ok") }
|
|
EOF
|
|
EL_RELATIONS_OUT="$W/rel.txt" "$ELC" "$W/p.el" >/dev/null 2>&1
|
|
"$LANG_DIR/tools/check/prohibitions.sh" "$W/rel.txt" > "$W/out.txt" 2>&1; rc=$?
|
|
chk "a violation outside the boundary is caught" "1" "$rc"
|
|
chk "the offending fn is named" "1" "$(grep -c 'sneaky is not one' "$W/out.txt")"
|
|
chk "a call inside the boundary is NOT flagged" "0" "$(grep -c 'allowed is not one' "$W/out.txt")"
|
|
|
|
cat > "$W/q.el" <<'EOF'
|
|
@decorator("prohibits_outside", "raw_sql")
|
|
fn repository() {}
|
|
|
|
@repository
|
|
fn only_allowed() -> Int { raw_sql("SELECT") return 1 }
|
|
|
|
fn main() { println("ok") }
|
|
EOF
|
|
EL_RELATIONS_OUT="$W/rel2.txt" "$ELC" "$W/q.el" >/dev/null 2>&1
|
|
"$LANG_DIR/tools/check/prohibitions.sh" "$W/rel2.txt" >/dev/null 2>&1
|
|
chk "a clean program exits 0" "0" "$?"
|
|
echo; echo " 4 assertions, $((4-F)) passed, $F failed"; exit $F
|