Audit follow-up after Wave 1's pcode `+=` fix surfaced a parallel
class of silent miscompiles in the *gengo* (native-Go) emit path.
Three real bugs hiding behind happy-path test coverage:
* `arr[i] += x` was ASSIGN-only — the IndexExpr branch returned
after emitting `arr[i] := x`, dropping the original element.
Now: PushArray + Push index, ArrayPush to read, fold with RHS,
re-do PushArray + index, ArrayPop to store.
* `alias->field += x` (and the M-> / MEMVAR-> namespace variants)
were ASSIGN-only too. Same shape of bug — `x->v += 7` compiled
as `x->v := 7`. Compound branch reads via PushAliasField (or
PushMemvar for M->), folds, stores via SetAliasField (or
PopMemvar).
* PRIVATE / PUBLIC mid-function declarations were treated as
extra LOCAL slots. emitMidVarDecl extended `locals` past the
function's declared count and emitted `PopLocalFast(idx)` for
the init. The slot didn't exist at runtime, so the init either
silently scribbled past the frame (small N) or panicked with
"local variable index out of range" once exercised. New logic:
PRIVATE/PUBLIC declarations bypass the locals table and emit
`PopMemvar(name)` for the init expression. The runtime auto-
creates the memvar.
* Memvar assignment fallback. After the LOCAL/STATIC checks miss
in emitAssign, the bottom path used to be a one-line WARN that
emitted RHS + `Pop()` — silently discarding the value. PRIVATE
pSum stayed at its initial value forever. Now: ASSIGN goes
through PopMemvar; compound forms read via PushMemvar, fold,
write back via PopMemvar.
Test fixture (tests/std_ch/test_compound_lhs.prg) covers all four
shapes. The std.ch runner picks it up so the regression suite now
stands at 15/15.
Other gates green:
go test ./... : PASS
FiveSql2 SQL:1999 : 43/43
Harbour compat : 56/56
std.ch suite : 15/15
FRB suite : 5/5
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
37 lines
806 B
Plaintext
37 lines
806 B
Plaintext
PROCEDURE Main()
|
|
LOCAL aArr := { 10, 20, 30 }, oObj, n
|
|
|
|
/* 1. array index += */
|
|
aArr[2] += 5
|
|
? "1. aArr[2] += 5 =>", aArr[2], "(expect 25)"
|
|
|
|
/* 2. alias->field += (use a real workarea) */
|
|
FErase("c.dbf")
|
|
dbCreate("c.dbf", { {"V", "N", 6, 0} })
|
|
USE c.dbf NEW EXCLUSIVE ALIAS x
|
|
dbAppend(); FieldPut(1, 100)
|
|
dbCommit()
|
|
|
|
x->v += 7
|
|
? "2. x->v += 7 =>", x->v, "(expect 107)"
|
|
|
|
x->(dbCloseArea())
|
|
FErase("c.dbf")
|
|
|
|
/* 3. memvar / private += */
|
|
PRIVATE pSum := 50
|
|
pSum += 25
|
|
? "3. PRIVATE pSum += 25 =>", pSum, "(expect 75)"
|
|
|
|
/* 4. STATIC += (already worked per gengo source, sanity) */
|
|
StaticTest()
|
|
|
|
? "DONE"
|
|
RETURN
|
|
|
|
STATIC FUNCTION StaticTest()
|
|
STATIC s_n := 100
|
|
s_n += 5
|
|
? "4. STATIC s_n += 5 =>", s_n, "(expect 105)"
|
|
RETURN NIL
|