#!/usr/bin/env bash
# prohibitions.sh — enforce boundary prohibitions as a QUERY over relations the
# compiler emitted, rather than as a rule the compiler contains.
#
# A prohibition is a containment relation over the call graph: "these calls may
# appear only inside a fn carrying construct C". The compiler's job is to say
# what it saw — who calls what, who carries what, who prohibits what. Deciding
# whether that is legal is a query, and a query does not belong in an emitter.
#
# Detection still happens at BUILD time. What moved is where the rule and the
# checker live, which is what "a #error has no runtime" was hiding.
#
#   usage: prohibitions.sh <relations-file>
#   exit 0 = clean; exit N = N violations
set -uo pipefail
REL="${1:?usage: prohibitions.sh <relations-file>}"
[ -f "$REL" ] || { echo "no relations file: $REL" >&2; exit 0; }

V=0
# construct -> prohibited names
while read -r construct _ rest; do
    [ "${rest#prohibits:}" = "$rest" ] && continue
    names="${rest#prohibits:}"
    IFS=',' read -ra NAMES <<< "$names"
    for n in "${NAMES[@]}"; do
        # every fn that calls a prohibited name
        while read -r caller _ callee; do
            [ "$callee" = "$n" ] || continue
            # ...must carry the owning construct
            if ! grep -qx "$caller calls @$construct" "$REL"; then
                printf 'boundary violation: %s may only be called from an @%s fn, but %s is not one\n' \
                       "$n" "$construct" "$caller"
                V=$((V+1))
            fi
        done < <(sort -u "$REL")
    done
done < <(sort -u "$REL")

[ "$V" -eq 0 ] && echo "prohibitions: clean"
exit "$V"
