ANSWER: if the partition is a neighbourhood, does linking survive?
The question is premature, and measuring says why. El's partition is a
FILESYSTEM PATH, not a neighbourhood, and there is no namespacing at all.
MEASURED
import is textual inlining (resolve_imports), guarded against double
inclusion by a __elc_imp__:<path> state key
when a .elh header exists the header is inlined instead and the .el is marked
seen, so symbols resolve at C link time -- so linking IS real, delegated to C
two modules defining `helper` emit two C functions into one translation unit
So linking barely survives the PATH partition. Whether it survives a
neighbourhood partition cannot be asked yet.
A DIAGNOSTIC REGRESSION I CAUSED, found by asking this question. cc does catch
the collision, but reports:
error: redefinition of '__el_body_helper'
error: redefinition of '__env_helper'
error: redefinition of '__thunk_helper'
error: redefinition of 'helper'
The user's own function is FOURTH. The first three are generated symbols
introduced by the unconditional-wrapper pass earlier today -- before it, there
was one clear message. Repaired by catching the collision at El level instead:
duplicate definition: 'helper' is defined 2 times — El has no namespacing,
so imported modules share one global scope
LIMIT, stated rather than hidden: textual inlining destroys file provenance. By
the time codegen runs there is one source string, so the message can say WHICH
name collides but not which files. Naming a.el and b.el needs provenance
threaded through resolve_imports.
104/104 native, 4/4 definitions_query.sh, the compiler itself reports clean,
fixpoint ok.
This commit is contained in:
@@ -2797,6 +2797,12 @@ fn params_to_env_args(params: [Any]) -> String {
|
||||
fn cg_fn(stmt: Map<String, Any>) -> Void {
|
||||
let fn_name: String = stmt["name"]
|
||||
state_set("__cg_current_fn", fn_name)
|
||||
// Record every top-level definition. El has NO namespacing -- imports are
|
||||
// textual inlining, so two modules defining the same name emit two C
|
||||
// functions into one translation unit. cc catches it, but names the
|
||||
// generated helpers first and the user's fn fourth. Recording it lets the
|
||||
// collision be reported at El level, in El terms.
|
||||
record_call(fn_name, "defines")
|
||||
// Emit which constructs this fn carries, so the prohibition check can be a
|
||||
// query over relations instead of a walk inside the emitter.
|
||||
let cdl = stmt["decorators"]
|
||||
|
||||
Executable
+25
@@ -0,0 +1,25 @@
|
||||
#!/usr/bin/env bash
|
||||
# Control for duplicate-definition detection.
|
||||
#
|
||||
# El has no namespacing: import is textual inlining, so two modules defining the
|
||||
# same name emit two C functions into one translation unit.
|
||||
set -uo pipefail
|
||||
ELC="${1:?usage: definitions_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)); }; }
|
||||
|
||||
printf 'fn helper() -> Int { return 1 }\n' > "$W/a.el"
|
||||
printf 'fn helper() -> Int { return 2 }\n' > "$W/b.el"
|
||||
printf 'import "a.el"\nimport "b.el"\nfn main() { println(int_to_str(helper())) }\n' > "$W/m.el"
|
||||
EL_RELATIONS_OUT="$W/r.txt" "$ELC" "$W/m.el" >/dev/null 2>&1
|
||||
out=$("$LANG_DIR/tools/check/definitions.sh" "$W/r.txt" 2>&1); rc=$?
|
||||
chk "a collision across modules is caught at El level" "1" "$rc"
|
||||
chk "the colliding name is reported" "1" "$(echo "$out" | grep -c "'helper' is defined 2 times")"
|
||||
chk "and the reason is given" "1" "$(echo "$out" | grep -c 'no namespacing')"
|
||||
|
||||
printf 'fn only_once() -> Int { return 1 }\nfn main() { println(int_to_str(only_once())) }\n' > "$W/c.el"
|
||||
EL_RELATIONS_OUT="$W/r2.txt" "$ELC" "$W/c.el" >/dev/null 2>&1
|
||||
"$LANG_DIR/tools/check/definitions.sh" "$W/r2.txt" >/dev/null 2>&1
|
||||
chk "a clean program exits 0" "0" "$?"
|
||||
echo; echo " 4 assertions, $((4-F)) passed, $F failed"; exit $F
|
||||
Executable
+24
@@ -0,0 +1,24 @@
|
||||
#!/usr/bin/env bash
|
||||
# definitions.sh — catch duplicate top-level definitions at El level.
|
||||
#
|
||||
# El has no namespacing. `import` is textual inlining, so two modules defining
|
||||
# the same name emit two C functions into one translation unit. cc does catch
|
||||
# it, but reports the generated helpers (__el_body_f, __env_f, __thunk_f) before
|
||||
# the user's own function, so the first three errors name symbols the user never
|
||||
# wrote.
|
||||
#
|
||||
# LIMIT, stated rather than hidden: textual inlining destroys file provenance.
|
||||
# By the time codegen runs there is one source string and no record of which
|
||||
# file a definition came from, so this can say WHICH name collides but not which
|
||||
# files. Naming the files needs provenance threaded through resolve_imports.
|
||||
set -uo pipefail
|
||||
REL="${1:?usage: definitions.sh <relations-file>}"
|
||||
[ -f "$REL" ] || exit 0
|
||||
V=0
|
||||
while read -r name count; do
|
||||
[ "$count" -gt 1 ] || continue
|
||||
printf "duplicate definition: '%s' is defined %s times — El has no namespacing, so imported modules share one global scope\n" "$name" "$count"
|
||||
V=$((V+1))
|
||||
done < <(grep ' calls defines$' "$REL" | awk '{print $1}' | sort | uniq -c | awk '{print $2, $1}')
|
||||
[ "$V" -eq 0 ] && echo "definitions: clean"
|
||||
exit "$V"
|
||||
Reference in New Issue
Block a user