fix(gengo): M-> and MEMVAR-> route to memvar table, not workarea
Harbour reserves the aliases `M` and `MEMVAR` for the memvar
namespace — `M->cVar` reads a PUBLIC/PRIVATE memvar, not a DBF
field in a workarea named M. Five's emitAliasExpr and emitAssign
treated all aliases identically, emitting:
t.PushAliasField("M", "cVar") // read
_wa := t.WA.(*hbrdd.WorkAreaManager); _wa.SetAliasField("M", ...) // write
which triggered a spurious hbrdd import on programs using memvars
and attempted a workarea lookup that couldn't find a "M" area at
runtime.
Detect the reserved aliases (case-insensitive) at the three
AliasExpr call sites — the read path (emitAliasExpr) and both
assign paths (emitAssign for statements, emitAssignExpr for
expression context) — and route to t.PushMemvar / t.PopMemvar
instead. The existing Thread helpers hash into the MemvarTable
populated by PUBLIC/PRIVATE declarations.
Unblocks harbour-core/tests/macro.prg build (runtime still needs
the TVALUE test helper, unrelated). FiveSql2 43/43, Harbour compat
56/56, Go test ALL PASS.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -1486,6 +1486,14 @@ func (g *Generator) emitAssign(a *ast.AssignExpr, locals localMap) {
|
||||
if aliasExpr, ok := a.Left.(*ast.AliasExpr); ok {
|
||||
if aliasIdent, ok2 := aliasExpr.Alias.(*ast.IdentExpr); ok2 {
|
||||
if fieldIdent, ok3 := aliasExpr.Field.(*ast.IdentExpr); ok3 {
|
||||
upper := strings.ToUpper(aliasIdent.Name)
|
||||
// `M->name := v` / `MEMVAR->name := v` are memvar writes,
|
||||
// not workarea field writes.
|
||||
if upper == "M" || upper == "MEMVAR" {
|
||||
g.emitExpr(a.Right)
|
||||
g.writeln(fmt.Sprintf(`t.PopMemvar(%q)`, fieldIdent.Name))
|
||||
return
|
||||
}
|
||||
g.emitExpr(a.Right)
|
||||
g.writeln(fmt.Sprintf(`{ _wa := t.WA.(*hbrdd.WorkAreaManager); _wa.SetAliasField(%q, %q, t.Pop2()) }`, aliasIdent.Name, fieldIdent.Name))
|
||||
return
|
||||
@@ -2716,6 +2724,16 @@ func (g *Generator) emitAssignExpr(e *ast.AssignExpr) {
|
||||
if aliasExpr, ok := e.Left.(*ast.AliasExpr); ok {
|
||||
if aliasIdent, ok2 := aliasExpr.Alias.(*ast.IdentExpr); ok2 {
|
||||
if fieldIdent, ok3 := aliasExpr.Field.(*ast.IdentExpr); ok3 {
|
||||
upper := strings.ToUpper(aliasIdent.Name)
|
||||
if upper == "M" || upper == "MEMVAR" {
|
||||
// Memvar write — leaves a copy on the stack for the
|
||||
// enclosing expression to pick up (matches Dup+SetAlias
|
||||
// path).
|
||||
g.emitExpr(e.Right)
|
||||
g.writeln("t.Dup()")
|
||||
g.writeln(fmt.Sprintf(`t.PopMemvar(%q)`, fieldIdent.Name))
|
||||
return
|
||||
}
|
||||
g.emitExpr(e.Right)
|
||||
g.writeln("t.Dup()")
|
||||
g.writeln(fmt.Sprintf(`{ _wa := t.WA.(*hbrdd.WorkAreaManager); _wa.SetAliasField(%q, %q, t.Pop2()) }`, aliasIdent.Name, fieldIdent.Name))
|
||||
@@ -3082,6 +3100,13 @@ func (g *Generator) emitAliasExpr(e *ast.AliasExpr) {
|
||||
|
||||
// Case 1: alias->field (static alias, simple field name)
|
||||
if ident, ok := e.Alias.(*ast.IdentExpr); ok && isFieldIdent {
|
||||
upper := strings.ToUpper(ident.Name)
|
||||
// `M->name` / `MEMVAR->name` access the memvar namespace, not
|
||||
// a database workarea. Harbour reserves both aliases for this.
|
||||
if upper == "M" || upper == "MEMVAR" {
|
||||
g.writeln(fmt.Sprintf(`t.PushMemvar(%q)`, fieldIdent.Name))
|
||||
return
|
||||
}
|
||||
g.writeln(fmt.Sprintf(`t.PushAliasField(%q, %q)`, ident.Name, fieldIdent.Name))
|
||||
return
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user