feat(static): file-local scoping for STATIC FUNCTION / PROCEDURE
Harbour STATIC FUNCTION / PROCEDURE is scoped to its source file —
multiple .prg files can each declare a `STATIC FUNCTION fn_HGet()`
without colliding. Five previously dropped them into the global VM
symbol table by their plain name, so multi-file builds (e.g. labdb's
22 .prg files where seven each defined their own STATIC fn_HGet)
either failed with redeclaration or silently linked every caller to
whichever definition won. fivenode_go's `sed` rename workaround can
now go away.
Mechanism
* ast.FuncDecl gains IsStatic. parser.go sets it whenever the
top-level STATIC keyword precedes FUNCTION / PROCEDURE.
* gengo records every same-file STATIC FUNCTION name in
g.staticFuncs. The symbol-table entry and the Go function name
for those declarations are mangled to
__STATIC__<fileKey>__<NAME>
so two files declaring `helper()` register two distinct symbols.
* emitPushSymbol rewrites call sites that match a name in
g.staticFuncs to the same mangled form, so same-file references
still resolve while cross-file references would look for a
symbol that doesn't exist.
* cmd/five/main.go's buildMultiPRG excludes STATIC FUNCTIONs from
the cross-file analyzer table — a foreign file calling another
file's STATIC now triggers a clean "undeclared variable" warning
instead of a runtime "function not found" deep inside vm.Run.
Verified
/tmp/a.prg + /tmp/b.prg each define `STATIC FUNCTION helper()`
returning their own string. Building both into one binary shows
each file calling its own helper:
a says: alpha (from a.prg)
(call B): beta (from b.prg)
Misuse (file X calling file Y's STATIC) now warns at compile
time. Full regression: go test ./compiler/... ./hbrt/...
./hbrtl/..., Compat 56/56, std.ch 17/17, FRB 7/7, FiveSql2 43/43.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -259,7 +259,15 @@ func buildMultiPRG(prgFiles []string, output string) {
|
||||
for _, d := range f.Decls {
|
||||
switch decl := d.(type) {
|
||||
case *ast.FuncDecl:
|
||||
crossFileFuncs[strings.ToUpper(decl.Name)] = true
|
||||
// STATIC FUNCTION is file-local: don't expose to
|
||||
// other files via the cross-file table so the
|
||||
// analyzer warns on misuse and the runtime fails
|
||||
// with a clean "undeclared" rather than a
|
||||
// mismatched lookup that silently resolves to
|
||||
// some other file's STATIC.
|
||||
if !decl.IsStatic {
|
||||
crossFileFuncs[strings.ToUpper(decl.Name)] = true
|
||||
}
|
||||
case *ast.ClassDecl:
|
||||
crossFileFuncs[strings.ToUpper(decl.Name)] = true
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user